├── tests ├── bins │ ├── 6502_bench.bin │ ├── 6502_bench.asm │ ├── 6502_loop_test.bin │ ├── 6502_decimal_test.bin │ └── 6502_decimal_test.a65 └── tests.rs ├── rustfmt.toml ├── .gitattributes ├── .gitignore ├── README.tpl ├── emulator_6502.iml ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── src ├── test_utilities.rs ├── lib.rs ├── address_modes.rs └── opcodes │ └── illegal.rs ├── matrix_scraper.py ├── benches └── benches.rs └── README.md /tests/bins/6502_bench.bin: -------------------------------------------------------------------------------- 1 | i�������� -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 150 2 | newline_style = "Unix" 3 | -------------------------------------------------------------------------------- /tests/bins/6502_bench.asm: -------------------------------------------------------------------------------- 1 | loop: 2 | ADC #$01 3 | BNE loop 4 | INX 5 | BNE loop 6 | INY 7 | BNE loop 8 | BRK -------------------------------------------------------------------------------- /tests/bins/6502_loop_test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GarettCooper/emulator_6502/HEAD/tests/bins/6502_loop_test.bin -------------------------------------------------------------------------------- /tests/bins/6502_decimal_test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GarettCooper/emulator_6502/HEAD/tests/bins/6502_decimal_test.bin -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | *.rs text 4 | *.py text 5 | *.md text 6 | *.asm text 7 | *.toml text 8 | *.yaml text 9 | 10 | *.bin binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | .idea/**/workspace.xml 6 | .idea/**/tasks.xml 7 | .idea/**/usage.statistics.xml 8 | .idea/**/dictionaries 9 | .idea/**/shelf 10 | .idea/modules.xml 11 | .idea/misc.xml 12 | .idea/vcs.xml 13 | -------------------------------------------------------------------------------- /README.tpl: -------------------------------------------------------------------------------- 1 | {{badges}} 2 | [![Crate](https://img.shields.io/crates/v/emulator_6502.svg)](https://crates.io/crates/emulator_6502) 3 | [![Documentation](https://docs.rs/emulator_6502/badge.svg)](https://docs.rs/emulator_6502) 4 | 5 | # {{crate}} 6 | 7 | {{readme}} 8 | 9 | Current version: {{version}} 10 | 11 | License: {{license}} -------------------------------------------------------------------------------- /emulator_6502.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | before_script: 3 | - rustup component add clippy 4 | - rustup component add rustfmt 5 | script: 6 | - cargo fmt --all -- --check 7 | - cargo clippy --all-targets --all-features -- -D warnings 8 | - cargo test --verbose 9 | - cargo test --verbose --all-features 10 | matrix: 11 | include: 12 | - rust: stable 13 | name: "Windows, Stable" 14 | os: windows 15 | 16 | - rust: beta 17 | name: "Windows, Beta" 18 | os: windows 19 | 20 | - rust: nightly 21 | name: "Windows, Nightly" 22 | os: windows 23 | 24 | - rust: stable 25 | name: "Linux, Stable" 26 | os: linux 27 | 28 | - rust: beta 29 | name: "Linux, Beta" 30 | os: linux 31 | 32 | - rust: nightly 33 | name: "Linux, Nightly" 34 | os: linux 35 | 36 | allow_failures: 37 | - rust: nightly 38 | fast_finish: true 39 | 40 | cache: 41 | cargo: true -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "emulator_6502" 3 | version = "1.1.0" 4 | authors = ["Garett Cooper"] 5 | edition = "2018" 6 | description = "Rust implementation of an MOS 6502 emulator, intended to be a talking point during the interview process for my Winter 2020 co-op placement." 7 | homepage = "https://github.com/GarettCooper/emulator_6502" 8 | repository = "https://github.com/GarettCooper/emulator_6502" 9 | readme = "README.md" 10 | keywords = ["emulator", "6502", "nes"] 11 | categories = ["emulators"] 12 | license = "MIT" 13 | exclude= [".travis.yml", "matrix_scraper.py"] 14 | 15 | [dependencies] 16 | log = "0.4.*" 17 | 18 | [features] 19 | default = [] 20 | illegal_opcodes = [] 21 | binary_coded_decimal = [] 22 | implementation_transparency = [] 23 | 24 | [badges] 25 | travis-ci = { repository = "GarettCooper/emulator_6502" } 26 | 27 | [dev-dependencies] 28 | criterion = "0.3.1" 29 | 30 | [[bench]] 31 | name = "benches" 32 | harness = false -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Garett Cooper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/test_utilities.rs: -------------------------------------------------------------------------------- 1 | //! Module of test utility types 2 | 3 | use super::*; 4 | 5 | pub(crate) struct StubInterface6502 { 6 | pub(crate) read: fn(u16, u8) -> u8, 7 | pub(crate) read_count: u8, 8 | pub(crate) write: fn(u16, u8, u8), 9 | pub(crate) write_count: u8, 10 | } 11 | 12 | impl StubInterface6502 { 13 | pub(crate) fn new(read_fn: fn(u16, u8) -> u8, write_fn: fn(u16, u8, u8)) -> Self { 14 | StubInterface6502 { 15 | read: read_fn, 16 | write: write_fn, 17 | read_count: 0, 18 | write_count: 0, 19 | } 20 | } 21 | } 22 | 23 | impl Interface6502 for StubInterface6502 { 24 | fn read(&mut self, address: u16) -> u8 { 25 | self.read_count += 1; 26 | (self.read)(address, self.read_count) 27 | } 28 | 29 | fn write(&mut self, address: u16, data: u8) { 30 | self.write_count += 1; 31 | (self.write)(address, data, self.read_count) 32 | } 33 | } 34 | 35 | impl Default for StubInterface6502 { 36 | fn default() -> Self { 37 | StubInterface6502::new( 38 | |_address, _read_count| panic!("Read Function was not initialized"), 39 | |_address, _data, _write_count| panic!("Write Function was not initialized"), 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /matrix_scraper.py: -------------------------------------------------------------------------------- 1 | try: 2 | from BeautifulSoup import BeautifulSoup 3 | except ImportError: 4 | from bs4 import BeautifulSoup 5 | 6 | import requests 7 | import re 8 | 9 | html = BeautifulSoup(requests.get("http://www.oxyron.de/html/opcodes02.html").text) 10 | template = "Opcode{{ name:\"{0}\", function: {0}, address_mode: {1}, cycles: {2} }},\t\t//{3}" 11 | 12 | address_map = {"imm" : "immediate", 13 | "zp" : "zero_page", 14 | "zpx" : "zero_page_x", 15 | "zpy" : "zero_page_y", 16 | "izx" : "indirect_x", 17 | "izy" : "indirect_y", 18 | "abs" : "absolute", 19 | "abx" : "absolute_x", 20 | "aby": "absolute_y", 21 | "ind": "indirect", 22 | "rel": "relative", 23 | "" : "implied" 24 | } 25 | 26 | i = 0 27 | for td in filter(lambda tag : not ("font size=\"+1\"" in str(tag)), html.find_all("td")[18:289]): 28 | opcode = re.search("(?<=>)([A-Z])+", str(td)) 29 | 30 | address_mode = re.search("(?<=>)([a-z])+", str(td)) 31 | if address_mode is None: address_mode = "" 32 | else: address_mode = address_mode.group().lower() 33 | 34 | cycles = re.search("(\d)(?=[*<])", str(td)) 35 | if cycles is None: cycles = 0 36 | else: cycles = cycles.group() 37 | print(template.format(opcode.group().lower(), address_map[address_mode], cycles, hex(i))) 38 | i += 1 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | use emulator_6502::*; 2 | use std::env::var; 3 | use std::fs::*; 4 | use std::io::*; 5 | use std::path::PathBuf; 6 | 7 | struct BasicRam { 8 | ram: Box<[u8; u16::max_value() as usize + 1]>, 9 | complete: bool, 10 | } 11 | 12 | impl BasicRam { 13 | fn load_program(&mut self, start: usize, data: &mut Vec) { 14 | self.ram[start..start + data.len()].clone_from_slice(data); 15 | } 16 | } 17 | 18 | impl Interface6502 for BasicRam { 19 | fn read(&mut self, address: u16) -> u8 { 20 | match address { 21 | 0xfffe..=0xffff => { 22 | self.complete = true; //If brk has been called the test is complete 23 | 0xff //Keep program in an infinite break loop until test terminates 24 | } 25 | _ => self.ram[address as usize], 26 | } 27 | } 28 | 29 | fn write(&mut self, address: u16, data: u8) { 30 | self.ram[address as usize] = data 31 | } 32 | } 33 | 34 | /// Function for loading test programs 35 | fn load_test(ram: &mut BasicRam, file_name: &str, location: usize) -> Result<()> { 36 | let root_dir = &var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR"); 37 | let mut source_path = PathBuf::from(root_dir); 38 | source_path.push("tests/bins"); 39 | source_path.push(file_name); 40 | 41 | let mut file = File::open(source_path)?; 42 | let mut buffer = Vec::new(); 43 | file.read_to_end(&mut buffer)?; 44 | 45 | ram.load_program(location, &mut buffer); 46 | 47 | Ok(()) 48 | } 49 | 50 | #[test] 51 | fn loop_test() -> Result<()> { 52 | std::env::set_var("RUST_LOG", "error"); 53 | let mut ram = BasicRam { 54 | ram: Box::new([0; u16::max_value() as usize + 1]), 55 | complete: false, 56 | }; 57 | load_test(&mut ram, "6502_loop_test.bin", 0x400)?; 58 | 59 | let mut cpu = MOS6502::new_start(0x400); 60 | let mut cycle_timeout = 0; 61 | while !ram.complete { 62 | cpu.cycle(&mut ram); 63 | cycle_timeout += 1; 64 | assert!(cycle_timeout < 5000) //Timeout 65 | } 66 | 67 | assert_eq!(ram.ram[0], 100); 68 | 69 | Ok(()) 70 | } 71 | 72 | #[test] 73 | #[cfg(feature = "binary_coded_decimal")] 74 | fn bcd_test() -> Result<()> { 75 | std::env::set_var("RUST_LOG", "trace"); 76 | let mut ram = BasicRam { 77 | ram: Box::new([0; u16::max_value() as usize + 1]), 78 | complete: false, 79 | }; 80 | load_test(&mut ram, "6502_decimal_test.bin", 0x200)?; 81 | 82 | let mut cpu = MOS6502::new_start(0x200); 83 | let mut cycle_timeout = 0; 84 | while !ram.complete { 85 | cpu.cycle(&mut ram); 86 | cycle_timeout += 1; 87 | assert!(cycle_timeout < 46089520) //Timeout 88 | } 89 | assert_eq!(ram.ram[0x0b], 0); 90 | Ok(()) 91 | } 92 | -------------------------------------------------------------------------------- /benches/benches.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use emulator_6502::*; 3 | use std::env::var; 4 | use std::fs::*; 5 | use std::io::*; 6 | use std::path::PathBuf; 7 | 8 | struct BasicRam { 9 | ram: Box<[u8; u16::max_value() as usize + 1]>, 10 | complete: bool, 11 | } 12 | 13 | impl BasicRam { 14 | fn load_program(&mut self, start: usize, data: &mut Vec) { 15 | self.ram[start..start + data.len()].clone_from_slice(data); 16 | } 17 | } 18 | 19 | impl Interface6502 for BasicRam { 20 | fn read(&mut self, address: u16) -> u8 { 21 | match address { 22 | 0xfffe..=0xffff => { 23 | self.complete = true; //If brk has been called the test is complete 24 | 0xff //Keep program in an infinite break loop until test terminates 25 | } 26 | _ => self.ram[address as usize], 27 | } 28 | } 29 | 30 | fn write(&mut self, address: u16, data: u8) { 31 | self.ram[address as usize] = data 32 | } 33 | } 34 | 35 | /// Function for loading test programs 36 | fn load_test(ram: &mut BasicRam, file_name: &str, location: usize) -> Result<()> { 37 | let root_dir = &var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR"); 38 | let mut source_path = PathBuf::from(root_dir); 39 | source_path.push("tests/bins"); 40 | source_path.push(file_name); 41 | 42 | let mut file = File::open(source_path)?; 43 | let mut buffer = Vec::new(); 44 | file.read_to_end(&mut buffer)?; 45 | 46 | ram.load_program(location, &mut buffer); 47 | 48 | Ok(()) 49 | } 50 | 51 | fn bench_test() { 52 | let mut ram = BasicRam { 53 | ram: Box::new([0; u16::max_value() as usize + 1]), 54 | complete: false, 55 | }; 56 | load_test(&mut ram, "6502_bench.bin", 0x400).unwrap(); 57 | 58 | let mut cpu = MOS6502::new_start(0x400); 59 | while !ram.complete { 60 | cpu.cycle(&mut ram); 61 | } 62 | } 63 | 64 | #[cfg(feature = "binary_coded_decimal")] 65 | fn bcd_bench() -> Result<()> { 66 | std::env::set_var("RUST_LOG", "trace"); 67 | let mut ram = BasicRam { 68 | ram: Box::new([0; u16::max_value() as usize + 1]), 69 | complete: false, 70 | }; 71 | load_test(&mut ram, "6502_decimal_test.bin", 0x200)?; 72 | 73 | let mut cpu = MOS6502::new_start(0x200); 74 | let mut cycle_timeout = 0; 75 | while !ram.complete { 76 | cpu.cycle(&mut ram); 77 | cycle_timeout += 1; 78 | assert!(cycle_timeout < 46089520) //Timeout 79 | } 80 | assert_eq!(ram.ram[0x0b], 0); 81 | Ok(()) 82 | } 83 | 84 | fn criterion_benchmark(c: &mut Criterion) { 85 | c.bench_function("Loop Bench", |b| b.iter(bench_test)); 86 | if cfg!(feature = "binary_coded_decimal") { 87 | let mut group = c.benchmark_group("Binary Coded Decimal"); 88 | group.sample_size(10); 89 | group.bench_function("BCD Bench", |b| b.iter(bcd_bench)); 90 | } 91 | } 92 | 93 | criterion_group!(benches, criterion_benchmark); 94 | criterion_main!(benches); 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/GarettCooper/emulator_6502.svg?branch=master)](https://travis-ci.org/GarettCooper/emulator_6502) 2 | [![Crate](https://img.shields.io/crates/v/emulator_6502.svg)](https://crates.io/crates/emulator_6502) 3 | [![Documentation](https://docs.rs/emulator_6502/badge.svg)](https://docs.rs/emulator_6502) 4 | 5 | # emulator_6502 6 | 7 | Hello friends, prospective employers, and people who Googled "6502 emulator rust", you've found 8 | a small personal project I've been working on since early September of 2019 to use as a talking 9 | point during the interview process for my Winter 2020 co-op placement. The goal of the project 10 | is to demonstrate my ability to pick up a new programming language while developing a complex 11 | system. 12 | 13 | This is a general purpose Rust implementation of an [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502) 14 | emulator, capable of executing code in isolation or as part of one of the many systems the 6502 15 | was used in, including the Commodore 64, Apple II, and Nintendo Entertainment System. To do so, 16 | the library provides the Interface6502 trait which allows the client to implement its own 17 | functions for reading and writing to memory addresses. For a real implementation example, check 18 | out my [gc_nes_emulator](https://github.com/GarettCooper/gc_nes_emulator). 19 | 20 | #### Defining an interface 21 | 22 | ```rust 23 | 24 | struct BasicRam{ 25 | ram: Box<[u8; u16::max_value() as usize + 1]> //The maximum address range of the 6502 26 | } 27 | 28 | impl BasicRam { 29 | fn load_program(&mut self, start: usize, data: &mut Vec){ 30 | self.ram[start..].clone_from_slice(data); 31 | } 32 | } 33 | 34 | impl Interface6502 for BasicRam{ 35 | fn read(&mut self, address: u16) -> u8{ 36 | self.ram[address as usize] 37 | } 38 | 39 | fn write(&mut self, address: u16, data: u8){ 40 | self.ram[address as usize] = data 41 | } 42 | } 43 | 44 | ``` 45 | 46 | In this example, the interface to be used with the emulator simply maps addresses to ram locations. 47 | The client is responsible for loading the 6502 binary program it wishes to run into an appropriate 48 | part of the address range. A more complex interface could map specific addresses to other emulated 49 | device components. 50 | 51 | For example, a NES implementation using this 6502 emulator would map reads and writes to addresses 52 | 0x2000-0x2007 to communication with the NES' picture processing unit, while a Commodore 64 53 | implementation would map addresses 0xd000-0xd3ff for drawing to the screen. 54 | 55 | #### Running a program 56 | 57 | ```rust 58 | 59 | fn main() -> Result<()>{ 60 | let mut ram = BasicRam{ ram: Box::new([0; u16::max_value() as usize + 1]) }; 61 | 62 | //Load a program into memory... 63 | let mut file = File::open("C:/some_6502_program.bin")?; 64 | let mut buffer = Vec::new(); 65 | file.read_to_end(&mut buffer)?; 66 | 67 | //Copy it into the BasicRam 68 | ram.load_program(0x0400, &mut buffer); 69 | 70 | let mut cpu = MOS6502::new(); //Create a new emulator instance 71 | cpu.set_program_counter(0x0400); //Set the program counter to the first byte of the program in memory 72 | cpu.cycle(&mut ram); // The emulator can execute cycles individually, for systems that require precise timing... 73 | cpu.execute_instruction(&mut ram); // or instruction by instruction for a coarser approach 74 | 75 | Ok(()) 76 | } 77 | 78 | ``` 79 | Each cycle/instruction the processor borrows mutable ownership of the interface in order to read and write to it. 80 | 81 | NOTE: When an instruction is executed, the entire computation is carried out simultaneously before the processor simply waits for the 82 | remaining number of cycles, meaning that timing of reads and writes is only accurate on an instruction-by-instruction basis, not cycle-by-cycle 83 | 84 | #### Supported Features: 85 | * Full implementation of documented instruction set 86 | * Emulation of bugs that existed in the original 6502 hardware 87 | * Binary Coded Decimal when the "binary_coded_decimal" compilation feature is enabled 88 | * Illegal undocumented opcodes when the "illegal_opcodes" compilation feature is enabled 89 | 90 | If illegal opcodes are called without the "illegal_opcodes" compilation feature enabled, the emulator will log a warning 91 | and run for the appropriate number of cycles without changing state. 92 | 93 | Current version: 1.1.0 94 | 95 | License: MIT 96 | -------------------------------------------------------------------------------- /tests/bins/6502_decimal_test.a65: -------------------------------------------------------------------------------- 1 | ; Verify decimal mode behavior 2 | ; Written by Bruce Clark. This code is public domain. 3 | ; see http://www.6502.org/tutorials/decimal_mode.html 4 | ; 5 | ; Returns: 6 | ; ERROR = 0 if the test passed 7 | ; ERROR = 1 if the test failed 8 | ; modify the code at the DONE label for desired program end 9 | ; 10 | ; This routine requires 17 bytes of RAM -- 1 byte each for: 11 | ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF 12 | ; and 2 bytes for N2H 13 | ; 14 | ; Variables: 15 | ; N1 and N2 are the two numbers to be added or subtracted 16 | ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 17 | ; DA and DNVZC are the actual accumulator and flag results in decimal mode 18 | ; HA and HNVZC are the accumulator and flag results when N1 and N2 are 19 | ; added or subtracted using binary arithmetic 20 | ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and 21 | ; flag results, calculated using binary arithmetic 22 | ; 23 | ; This program takes approximately 1 minute at 1 MHz (a few seconds more on 24 | ; a 65C02 than a 6502 or 65816) 25 | ; 26 | 27 | ; Configuration: 28 | cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 29 | vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only 30 | chk_a = 1 ; check accumulator 31 | chk_n = 0 ; check sign (negative) flag 32 | chk_v = 0 ; check overflow flag 33 | chk_z = 0 ; check zero flag 34 | chk_c = 1 ; check carry flag 35 | 36 | end_of_test macro 37 | db $db ;execute 65C02 stop instruction 38 | endm 39 | 40 | bss 41 | org 0 42 | ; operands - register Y = carry in 43 | N1 ds 1 44 | N2 ds 1 45 | ; binary result 46 | HA ds 1 47 | HNVZC ds 1 48 | ;04 49 | ; decimal result 50 | DA ds 1 51 | DNVZC ds 1 52 | ; predicted results 53 | AR ds 1 54 | NF ds 1 55 | ;08 56 | VF ds 1 57 | ZF ds 1 58 | CF ds 1 59 | ERROR ds 1 60 | ;0C 61 | ; workspace 62 | N1L ds 1 63 | N1H ds 1 64 | N2L ds 1 65 | N2H ds 2 66 | 67 | code 68 | org $200 69 | TEST ldy #1 ; initialize Y (used to loop through carry flag values) 70 | sty ERROR ; store 1 in ERROR until the test passes 71 | lda #0 ; initialize N1 and N2 72 | sta N1 73 | sta N2 74 | LOOP1 lda N2 ; N2L = N2 & $0F 75 | and #$0F ; [1] see text 76 | if vld_bcd = 1 77 | cmp #$0a 78 | bcs NEXT2 79 | endif 80 | sta N2L 81 | lda N2 ; N2H = N2 & $F0 82 | and #$F0 ; [2] see text 83 | if vld_bcd = 1 84 | cmp #$a0 85 | bcs NEXT2 86 | endif 87 | sta N2H 88 | ora #$0F ; N2H+1 = (N2 & $F0) + $0F 89 | sta N2H+1 90 | LOOP2 lda N1 ; N1L = N1 & $0F 91 | and #$0F ; [3] see text 92 | if vld_bcd = 1 93 | cmp #$0a 94 | bcs NEXT1 95 | endif 96 | sta N1L 97 | lda N1 ; N1H = N1 & $F0 98 | and #$F0 ; [4] see text 99 | if vld_bcd = 1 100 | cmp #$a0 101 | bcs NEXT1 102 | endif 103 | sta N1H 104 | jsr ADD 105 | jsr A6502 106 | jsr COMPARE 107 | bne DONE 108 | jsr SUB 109 | jsr S6502 110 | jsr COMPARE 111 | bne DONE 112 | NEXT1 inc N1 ; [5] see text 113 | bne LOOP2 ; loop through all 256 values of N1 114 | NEXT2 inc N2 ; [6] see text 115 | bne LOOP1 ; loop through all 256 values of N2 116 | dey 117 | bpl LOOP1 ; loop through both values of the carry flag 118 | lda #0 ; test passed, so store 0 in ERROR 119 | sta ERROR 120 | DONE 121 | end_of_test 122 | 123 | ; Calculate the actual decimal mode accumulator and flags, the accumulator 124 | ; and flag results when N1 is added to N2 using binary arithmetic, the 125 | ; predicted accumulator result, the predicted carry flag, and the predicted 126 | ; V flag 127 | ; 128 | ADD sed ; decimal mode 129 | cpy #1 ; set carry if Y = 1, clear carry if Y = 0 130 | lda N1 131 | adc N2 132 | sta DA ; actual accumulator result in decimal mode 133 | php 134 | pla 135 | sta DNVZC ; actual flags result in decimal mode 136 | cld ; binary mode 137 | cpy #1 ; set carry if Y = 1, clear carry if Y = 0 138 | lda N1 139 | adc N2 140 | sta HA ; accumulator result of N1+N2 using binary arithmetic 141 | 142 | php 143 | pla 144 | sta HNVZC ; flags result of N1+N2 using binary arithmetic 145 | cpy #1 146 | lda N1L 147 | adc N2L 148 | cmp #$0A 149 | ldx #0 150 | bcc A1 151 | inx 152 | adc #5 ; add 6 (carry is set) 153 | and #$0F 154 | sec 155 | A1 ora N1H 156 | ; 157 | ; if N1L + N2L < $0A, then add N2 & $F0 158 | ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) 159 | ; 160 | adc N2H,x 161 | php 162 | bcs A2 163 | cmp #$A0 164 | bcc A3 165 | A2 adc #$5F ; add $60 (carry is set) 166 | sec 167 | A3 sta AR ; predicted accumulator result 168 | php 169 | pla 170 | sta CF ; predicted carry result 171 | pla 172 | ; 173 | ; note that all 8 bits of the P register are stored in VF 174 | ; 175 | sta VF ; predicted V flags 176 | rts 177 | 178 | ; Calculate the actual decimal mode accumulator and flags, and the 179 | ; accumulator and flag results when N2 is subtracted from N1 using binary 180 | ; arithmetic 181 | ; 182 | SUB sed ; decimal mode 183 | cpy #1 ; set carry if Y = 1, clear carry if Y = 0 184 | lda N1 185 | sbc N2 186 | sta DA ; actual accumulator result in decimal mode 187 | php 188 | pla 189 | sta DNVZC ; actual flags result in decimal mode 190 | cld ; binary mode 191 | cpy #1 ; set carry if Y = 1, clear carry if Y = 0 192 | lda N1 193 | sbc N2 194 | sta HA ; accumulator result of N1-N2 using binary arithmetic 195 | 196 | php 197 | pla 198 | sta HNVZC ; flags result of N1-N2 using binary arithmetic 199 | rts 200 | 201 | if cputype != 1 202 | ; Calculate the predicted SBC accumulator result for the 6502 and 65816 203 | ; 204 | SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 205 | lda N1L 206 | sbc N2L 207 | ldx #0 208 | bcs S11 209 | inx 210 | sbc #5 ; subtract 6 (carry is clear) 211 | and #$0F 212 | clc 213 | S11 ora N1H 214 | ; 215 | ; if N1L - N2L >= 0, then subtract N2 & $F0 216 | ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) 217 | ; 218 | sbc N2H,x 219 | bcs S12 220 | sbc #$5F ; subtract $60 (carry is clear) 221 | S12 sta AR 222 | rts 223 | endif 224 | 225 | if cputype = 1 226 | ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 227 | ; 228 | SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 229 | lda N1L 230 | sbc N2L 231 | ldx #0 232 | bcs S21 233 | inx 234 | and #$0F 235 | clc 236 | S21 ora N1H 237 | ; 238 | ; if N1L - N2L >= 0, then subtract N2 & $F0 239 | ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) 240 | ; 241 | sbc N2H,x 242 | bcs S22 243 | sbc #$5F ; subtract $60 (carry is clear) 244 | S22 cpx #0 245 | beq S23 246 | sbc #6 247 | S23 sta AR ; predicted accumulator result 248 | rts 249 | endif 250 | 251 | ; Compare accumulator actual results to predicted results 252 | ; 253 | ; Return: 254 | ; Z flag = 1 (BEQ branch) if same 255 | ; Z flag = 0 (BNE branch) if different 256 | ; 257 | COMPARE 258 | if chk_a = 1 259 | lda DA 260 | cmp AR 261 | bne C1 262 | endif 263 | if chk_n = 1 264 | lda DNVZC ; [7] see text 265 | eor NF 266 | and #$80 ; mask off N flag 267 | bne C1 268 | endif 269 | if chk_v = 1 270 | lda DNVZC ; [8] see text 271 | eor VF 272 | and #$40 ; mask off V flag 273 | bne C1 ; [9] see text 274 | endif 275 | if chk_z = 1 276 | lda DNVZC 277 | eor ZF ; mask off Z flag 278 | and #2 279 | bne C1 ; [10] see text 280 | endif 281 | if chk_c = 1 282 | lda DNVZC 283 | eor CF 284 | and #1 ; mask off C flag 285 | endif 286 | C1 rts 287 | 288 | ; These routines store the predicted values for ADC and SBC for the 6502, 289 | ; 65C02, and 65816 in AR, CF, NF, VF, and ZF 290 | 291 | if cputype = 0 292 | 293 | A6502 lda VF ; 6502 294 | ; 295 | ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains 296 | ; the N flag for NF 297 | ; 298 | sta NF 299 | lda HNVZC 300 | sta ZF 301 | rts 302 | 303 | S6502 jsr SUB1 304 | lda HNVZC 305 | sta NF 306 | sta VF 307 | sta ZF 308 | sta CF 309 | rts 310 | 311 | endif 312 | if cputype = 1 313 | 314 | A6502 lda AR ; 65C02 315 | php 316 | pla 317 | sta NF 318 | sta ZF 319 | rts 320 | 321 | S6502 jsr SUB2 322 | lda AR 323 | php 324 | pla 325 | sta NF 326 | sta ZF 327 | lda HNVZC 328 | sta VF 329 | sta CF 330 | rts 331 | 332 | endif 333 | if cputype = 2 334 | 335 | A6502 lda AR ; 65C816 336 | php 337 | pla 338 | sta NF 339 | sta ZF 340 | rts 341 | 342 | S6502 jsr SUB1 343 | lda AR 344 | php 345 | pla 346 | sta NF 347 | sta ZF 348 | lda HNVZC 349 | sta VF 350 | sta CF 351 | rts 352 | 353 | endif 354 | 355 | end TEST 356 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Hello friends, prospective employers, and people who Googled "6502 emulator rust", you've found 2 | //! a small personal project I've been working on since early September of 2019 to use as a talking 3 | //! point during the interview process for my Winter 2020 co-op placement. The goal of the project 4 | //! is to demonstrate my ability to pick up a new programming language while developing a complex 5 | //! system. 6 | //! 7 | //! This is a general purpose Rust implementation of an [MOS 6502](https://en.wikipedia.org/wiki/MOS_Technology_6502) 8 | //! emulator, capable of executing code in isolation or as part of one of the many systems the 6502 9 | //! was used in, including the Commodore 64, Apple II, and Nintendo Entertainment System. To do so, 10 | //! the library provides the Interface6502 trait which allows the client to implement its own 11 | //! functions for reading and writing to memory addresses. For a real implementation example, check 12 | //! out my [gc_nes_emulator](https://github.com/GarettCooper/gc_nes_emulator). 13 | //! 14 | //! ### Defining an interface 15 | //! 16 | //! ```rust,ignore 17 | //! 18 | //! struct BasicRam{ 19 | //! ram: Box<[u8; u16::max_value() as usize + 1]> //The maximum address range of the 6502 20 | //! } 21 | //! 22 | //! impl BasicRam { 23 | //! fn load_program(&mut self, start: usize, data: &mut Vec){ 24 | //! self.ram[start..].clone_from_slice(data); 25 | //! } 26 | //! } 27 | //! 28 | //! impl Interface6502 for BasicRam{ 29 | //! fn read(&mut self, address: u16) -> u8{ 30 | //! self.ram[address as usize] 31 | //! } 32 | //! 33 | //! fn write(&mut self, address: u16, data: u8){ 34 | //! self.ram[address as usize] = data 35 | //! } 36 | //! } 37 | //! 38 | //! ``` 39 | //! 40 | //! In this example, the interface to be used with the emulator simply maps addresses to ram locations. 41 | //! The client is responsible for loading the 6502 binary program it wishes to run into an appropriate 42 | //! part of the address range. A more complex interface could map specific addresses to other emulated 43 | //! device components. 44 | //! 45 | //! For example, a NES implementation using this 6502 emulator would map reads and writes to addresses 46 | //! 0x2000-0x2007 to communication with the NES' picture processing unit, while a Commodore 64 47 | //! implementation would map addresses 0xd000-0xd3ff for drawing to the screen. 48 | //! 49 | //! ### Running a program 50 | //! 51 | //! ```rust,ignore 52 | //! 53 | //! fn main() -> Result<()>{ 54 | //! let mut ram = BasicRam{ ram: Box::new([0; u16::max_value() as usize + 1]) }; 55 | //! 56 | //! //Load a program into memory... 57 | //! let mut file = File::open("C:/some_6502_program.bin")?; 58 | //! let mut buffer = Vec::new(); 59 | //! file.read_to_end(&mut buffer)?; 60 | //! 61 | //! //Copy it into the BasicRam 62 | //! ram.load_program(0x0400, &mut buffer); 63 | //! 64 | //! let mut cpu = MOS6502::new(); //Create a new emulator instance 65 | //! cpu.set_program_counter(0x0400); //Set the program counter to the first byte of the program in memory 66 | //! cpu.cycle(&mut ram); // The emulator can execute cycles individually, for systems that require precise timing... 67 | //! cpu.execute_instruction(&mut ram); // or instruction by instruction for a coarser approach 68 | //! 69 | //! Ok(()) 70 | //! } 71 | //! 72 | //! ``` 73 | //! Each cycle/instruction the processor borrows mutable ownership of the interface in order to read and write to it. 74 | //! 75 | //! NOTE: When an instruction is executed, the entire computation is carried out simultaneously before the processor simply waits for the 76 | //! remaining number of cycles, meaning that timing of reads and writes is only accurate on an instruction-by-instruction basis, not cycle-by-cycle 77 | //! 78 | //! ### Supported Features: 79 | //! * Full implementation of documented instruction set 80 | //! * Emulation of bugs that existed in the original 6502 hardware 81 | //! * Binary Coded Decimal when the "binary_coded_decimal" compilation feature is enabled 82 | //! * Illegal undocumented opcodes when the "illegal_opcodes" compilation feature is enabled 83 | //! 84 | //! If illegal opcodes are called without the "illegal_opcodes" compilation feature enabled, the emulator will log a warning 85 | //! and run for the appropriate number of cycles without changing state. 86 | 87 | #![allow(clippy::needless_return)] // My preferred style 88 | 89 | mod address_modes; 90 | mod opcodes; 91 | #[cfg(test)] 92 | mod test_utilities; 93 | 94 | #[macro_use] 95 | extern crate log; 96 | 97 | use address_modes::*; 98 | 99 | //Declare some type alias for clarity's sake 100 | /// The type of all Address Mode functions 101 | type AddressModeFunction = fn(&mut MOS6502, &mut dyn Interface6502) -> AddressModeValue; 102 | /// The type of all Opcode functions 103 | type OpcodeFunction = fn(&mut MOS6502, &mut dyn Interface6502, AddressModeValue); 104 | 105 | ///The value that will be added to the stack pointer 106 | const STACK_PAGE: u16 = 0x0100; 107 | ///The address that the program counter will be read from when a non-maskable interrupt request is made 108 | const NMI_ADDRESS_LOCATION: u16 = 0xfffa; 109 | ///The address that the program counter will be read from when reset is called 110 | const RESET_ADDRESS_LOCATION: u16 = 0xfffc; 111 | ///The address that the program counter will be read from when an interrupt request is made or BRK is called 112 | const IRQ_ADDRESS_LOCATION: u16 = 0xfffe; 113 | 114 | /// Struct representation of the MOS 6502 processor 115 | /// 116 | /// ### Usage Example 117 | /// ```rust,ignore 118 | /// fn main() -> Result<()>{ 119 | /// let mut ram = BasicRam{ ram: Box::new([0; u16::max_value() as usize + 1]) }; 120 | /// 121 | /// //Load a program into memory... 122 | /// let mut file = File::open("C:/some_6502_program.bin")?; 123 | /// let mut buffer = Vec::new(); 124 | /// file.read_to_end(&mut buffer)?; 125 | /// 126 | /// //Copy it into the BasicRam 127 | /// ram.load_program(0x0400, &mut buffer); 128 | /// 129 | /// let mut cpu = MOS6502::new(); //Create a new emulator instance 130 | /// cpu.set_program_counter(0x0400); //Set the program counter to the first byte of the program in memory 131 | /// cpu.cycle(&mut ram); // The emulator can execute cycles individually, for systems that require precise timing... 132 | /// cpu.execute_instruction(&mut ram); // or instruction by instruction for a coarser approach 133 | /// 134 | /// Ok(()) 135 | /// } 136 | /// ``` 137 | #[derive(Debug, PartialEq, Clone)] 138 | pub struct MOS6502 { 139 | // Registers 140 | /// The accumulator register of the 6502, where the results of arithmetic opcodes are placed 141 | accumulator: u8, 142 | /// The x register of the 6502 143 | x_register: u8, 144 | /// The y register of the 6502 145 | y_register: u8, 146 | /// Pointer to the instruction that will be executed next 147 | program_counter: u16, 148 | /// Pointer to the top of the stack 149 | stack_pointer: u8, 150 | /// Register holding the 6502's status flags 151 | status_register: u8, 152 | // Other 153 | /// The number of cycles before the next opcode is run 154 | remaining_cycles: u8, 155 | /// The total number of cycles that have passed during program execution 156 | total_cycles: u64, 157 | // Tracking Booleans 158 | /// Boolean tracking whether or not a non-maskable interrupt request has been made 159 | pending_nmi: bool, 160 | /// Boolean tracking whether or not an interrupt request has been made 161 | pending_irq: bool, 162 | } 163 | 164 | impl MOS6502 { 165 | /// Creates a new MOS6502 emulation with the program counter at 0x0400 166 | pub fn new() -> Self { 167 | MOS6502 { 168 | accumulator: 0x00, 169 | x_register: 0x00, 170 | y_register: 0x00, 171 | program_counter: 0x0400, 172 | stack_pointer: 0xFD, 173 | status_register: 0x24, 174 | remaining_cycles: 0, 175 | total_cycles: 0, 176 | pending_nmi: false, 177 | pending_irq: false, 178 | } 179 | } 180 | 181 | /// Creates a new MOS6502 emulation with the program counter at the provided start address 182 | pub fn new_start(start: u16) -> Self { 183 | return MOS6502 { 184 | program_counter: start, 185 | ..MOS6502::new() 186 | }; 187 | } 188 | 189 | /// Creates a new MOS6502 emulation with the program counter at the address read from the reset vector (0xfffa-0xfffb). 190 | /// 191 | /// This is the standard method used for determining where the program starts on most systems 192 | pub fn new_reset_position(interface: &mut (dyn Interface6502)) -> Self { 193 | return MOS6502 { 194 | program_counter: read_16(interface, RESET_ADDRESS_LOCATION), 195 | ..MOS6502::new() 196 | }; 197 | } 198 | 199 | /// Force the program counter to a specific address 200 | pub fn set_program_counter(&mut self, program_counter: u16) { 201 | self.program_counter = program_counter 202 | } 203 | 204 | /// Returns the value of the program counter register 205 | #[cfg(feature = "implementation_transparency")] 206 | pub fn get_program_counter(&self) -> u16 { 207 | self.program_counter 208 | } 209 | 210 | /// Returns the value of the accumulator register 211 | #[cfg(feature = "implementation_transparency")] 212 | pub fn get_accumulator(&self) -> u8 { 213 | self.accumulator 214 | } 215 | 216 | /// Sets the value of the accumulator register 217 | #[cfg(feature = "implementation_transparency")] 218 | pub fn set_accumulator(&mut self, value: u8) { 219 | self.accumulator = value 220 | } 221 | 222 | /// Returns the value of the X register 223 | #[cfg(feature = "implementation_transparency")] 224 | pub fn get_x_register(&self) -> u8 { 225 | self.x_register 226 | } 227 | 228 | /// Returns the value of the X register 229 | #[cfg(feature = "implementation_transparency")] 230 | pub fn set_x_register(&mut self, value: u8) { 231 | self.x_register = value 232 | } 233 | 234 | /// Returns the value of the Y register 235 | #[cfg(feature = "implementation_transparency")] 236 | pub fn get_y_register(&self) -> u8 { 237 | self.y_register 238 | } 239 | 240 | /// Returns the value of the Y register 241 | #[cfg(feature = "implementation_transparency")] 242 | pub fn set_y_register(&mut self, value: u8) { 243 | self.y_register = value 244 | } 245 | 246 | /// Returns the value of the stack pointer register 247 | #[cfg(feature = "implementation_transparency")] 248 | pub fn get_stack_pointer(&self) -> u8 { 249 | self.stack_pointer 250 | } 251 | 252 | /// Sets the value of the stack pointer register 253 | #[cfg(feature = "implementation_transparency")] 254 | pub fn set_stack_pointer(&mut self, value: u8) { 255 | self.stack_pointer = value 256 | } 257 | 258 | /// Returns the value of the status register 259 | #[cfg(feature = "implementation_transparency")] 260 | pub fn get_status_register(&self) -> u8 { 261 | self.status_register 262 | } 263 | 264 | /// Sets the value of the status register 265 | #[cfg(feature = "implementation_transparency")] 266 | pub fn set_status_register(&mut self, value: u8) { 267 | self.status_register = value 268 | } 269 | 270 | /// Returns the number of remaining cycles for currently running instruction 271 | #[cfg(feature = "implementation_transparency")] 272 | pub fn get_remaining_cycles(&self) -> u8 { 273 | self.remaining_cycles 274 | } 275 | 276 | /// Runs a processor cycle, mutably borrows the reading and writing interface for the duration 277 | pub fn cycle(&mut self, interface: &mut (dyn Interface6502)) { 278 | if self.remaining_cycles == 0 { 279 | if self.pending_nmi || (self.pending_irq && !self.get_flag(StatusFlag::InterruptDisable)) { 280 | //An interrupt will let the executing instruction complete 281 | self.push_stack_16(interface, self.program_counter); 282 | self.set_flag(StatusFlag::BreakIrq, true); 283 | self.push_stack(interface, self.status_register); 284 | self.set_flag(StatusFlag::InterruptDisable, true); 285 | 286 | if self.pending_nmi { 287 | self.program_counter = read_16(interface, NMI_ADDRESS_LOCATION); 288 | self.remaining_cycles = 8; 289 | } else { 290 | self.program_counter = read_16(interface, IRQ_ADDRESS_LOCATION); 291 | self.remaining_cycles = 7; 292 | } 293 | 294 | self.pending_nmi = false; 295 | self.pending_irq = false; 296 | } else { 297 | //Proceed normally 298 | let instruction = opcodes::OPCODE_TABLE[interface.read(self.program_counter) as usize]; 299 | let log_program_counter = self.program_counter; 300 | self.program_counter += 1; 301 | let address_mode_value = instruction.find_address(self, interface); 302 | 303 | trace!( 304 | "0x{:04X} {} {:?} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{}", 305 | log_program_counter, 306 | instruction.get_name(), 307 | address_mode_value, 308 | self.accumulator, 309 | self.x_register, 310 | self.y_register, 311 | self.status_register, 312 | self.stack_pointer, 313 | self.total_cycles + 7, 314 | ); 315 | 316 | instruction.execute_instruction(self, interface, address_mode_value); 317 | self.remaining_cycles += instruction.get_cycles(); 318 | } 319 | } 320 | self.remaining_cycles -= 1; 321 | self.total_cycles += 1; 322 | } 323 | 324 | /// Runs as many processor cycles as it takes to complete the instruction at the program counter 325 | pub fn execute_instruction(&mut self, interface: &mut (dyn Interface6502)) { 326 | self.cycle(interface); //No do-while loops in Rust 327 | while self.remaining_cycles != 0 { 328 | self.cycle(interface) 329 | } 330 | } 331 | 332 | /// Pushes a byte onto the stack 333 | fn push_stack(&mut self, interface: &mut dyn Interface6502, data: u8) { 334 | interface.write(STACK_PAGE + u16::from(self.stack_pointer), data); 335 | self.stack_pointer = self.stack_pointer.wrapping_sub(1); 336 | } 337 | 338 | /// Pushes two bytes onto the stack 339 | fn push_stack_16(&mut self, interface: &mut dyn Interface6502, data: u16) { 340 | self.push_stack(interface, (data >> 8) as u8); 341 | self.push_stack(interface, data as u8); 342 | } 343 | 344 | /// Pops a byte from the stack 345 | fn pop_stack(&mut self, interface: &mut dyn Interface6502) -> u8 { 346 | self.stack_pointer = self.stack_pointer.wrapping_add(1); 347 | interface.read(STACK_PAGE + u16::from(self.stack_pointer)) 348 | } 349 | 350 | /// Pops two bytes from the stack 351 | fn pop_stack_16(&mut self, interface: &mut dyn Interface6502) -> u16 { 352 | let lo = u16::from(self.pop_stack(interface)); 353 | let hi = u16::from(self.pop_stack(interface)); 354 | return (hi << 8) | lo; 355 | } 356 | 357 | /// Sets a status flag to the given boolean value 358 | fn set_flag(&mut self, flag: StatusFlag, value: bool) { 359 | //Clear flag 360 | self.status_register &= !(flag as u8); 361 | //TODO: Work out a branch free method of doing this, possibly converting flag values to bit index 362 | if value { 363 | self.status_register |= flag as u8 364 | } 365 | } 366 | 367 | /// Returns the value of a flag in the status register as a boolean 368 | fn get_flag(&self, flag: StatusFlag) -> bool { 369 | return (self.status_register & flag as u8) > 0; 370 | } 371 | 372 | /// Request that an interrupt occurs after the current instruction completes 373 | pub fn interrupt_request(&mut self) { 374 | self.pending_irq = true; 375 | } 376 | 377 | /// Request that an interrupt occurs after the current instruction completes, even if the interrupt disabled flag is set 378 | pub fn non_maskable_interrupt_request(&mut self) { 379 | self.pending_nmi = true; 380 | } 381 | 382 | /// Resets the 6502 to a known state 383 | pub fn reset(&mut self, interface: &mut dyn Interface6502) { 384 | self.program_counter = read_16(interface, RESET_ADDRESS_LOCATION); 385 | 386 | self.accumulator = 0x00; 387 | self.x_register = 0x00; 388 | self.y_register = 0x00; 389 | 390 | self.stack_pointer = 0xFD; 391 | self.status_register = 0x34; 392 | self.remaining_cycles = 8; 393 | } 394 | } 395 | 396 | /// Wrapper function for reading 16 bits at a time 397 | fn read_16(bus: &mut dyn Interface6502, address: u16) -> u16 { 398 | let lo = u16::from(bus.read(address)); 399 | let hi = u16::from(bus.read(address + 1)); 400 | return (hi << 8) | lo; 401 | } 402 | 403 | /// Trait that other devices can use for interfacing with the 6502. 404 | /// 405 | /// ### Declaration Example 406 | /// ```rust,ignore 407 | /// struct BasicRam{ 408 | /// ram: Box<[u8; u16::max_value() as usize + 1]> //The maximum address range of the 6502 409 | /// } 410 | /// 411 | /// impl BasicRam { 412 | /// fn load_program(&mut self, start: usize, data: &mut Vec){ 413 | /// self.ram[start..].clone_from_slice(data); 414 | /// } 415 | /// } 416 | /// 417 | /// impl Interface6502 for BasicRam{ 418 | /// fn read(&mut self, address: u16) -> u8{ 419 | /// self.ram[address as usize] 420 | /// } 421 | /// 422 | /// fn write(&mut self, address: u16, data: u8){ 423 | /// self.ram[address as usize] = data 424 | /// } 425 | /// } 426 | /// ``` 427 | pub trait Interface6502 { 428 | /// Reads a byte from the interface at the given address 429 | fn read(&mut self, address: u16) -> u8; 430 | /// Writes a byte to the interface at the given address 431 | fn write(&mut self, address: u16, data: u8); 432 | } 433 | 434 | #[derive(Debug, Copy, Clone)] 435 | /// Enum used to represent the different flags in the 6502's status register 436 | enum StatusFlag { 437 | Carry = 0b0000_0001, 438 | Zero = 0b0000_0010, 439 | InterruptDisable = 0b0000_0100, 440 | Decimal = 0b0000_1000, 441 | Break = 0b0011_0000, 442 | BreakIrq = 0b0010_0000, 443 | Overflow = 0b0100_0000, 444 | Negative = 0b1000_0000, 445 | } 446 | 447 | impl Default for MOS6502 { 448 | fn default() -> Self { 449 | MOS6502::new() 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /src/address_modes.rs: -------------------------------------------------------------------------------- 1 | //! ### ADDRESS MODES 2 | //! This module contains all of the functions for the 6502's addressing modes. 3 | //! 4 | //! An address mode function is called before an opcode function, returning a memory address and the 5 | //! number of extra cycles that may be required under specific circumstances 6 | //! (Typically crossing page boundaries). 7 | 8 | use super::{Interface6502, MOS6502}; 9 | use std::fmt; 10 | 11 | /// Absolute: Address mode returning a 16-bit absolute address 12 | pub(crate) fn absolute(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 13 | let address: u16 = super::read_16(bus, cpu.program_counter); 14 | cpu.program_counter += 2; 15 | return AddressModeValue::AbsoluteAddress(address); 16 | } 17 | 18 | /// Absolute X: Address mode returning a 16-bit absolute address offset by the x register 19 | pub(crate) fn absolute_x(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 20 | let address: u16 = super::read_16(bus, cpu.program_counter); 21 | let offset_address: u16 = address + u16::from(cpu.x_register); 22 | 23 | cpu.remaining_cycles += if (offset_address) & 0xff00 != address & 0xff00 { 24 | //Offset crossed a page boundary, any opcode using this address mode will take an extra cycle 25 | 1 26 | } else { 27 | 0 28 | }; 29 | 30 | cpu.program_counter += 2; 31 | return AddressModeValue::AbsoluteAddress(offset_address); 32 | } 33 | 34 | /// Absolute X: Address mode returning a 16-bit absolute address offset by the x register. This extra 35 | /// mode accounts for the special case where the instruction takes the same number of cycles regardless 36 | /// of crossing a page boundary. 37 | pub(crate) fn absolute_x_const(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 38 | let address: u16 = super::read_16(bus, cpu.program_counter); 39 | let offset_address: u16 = address + u16::from(cpu.x_register); 40 | 41 | cpu.program_counter += 2; 42 | return AddressModeValue::AbsoluteAddress(offset_address); 43 | } 44 | 45 | /// Absolute Y: Address mode returning a 16-bit absolute address offset by the y register 46 | pub(crate) fn absolute_y(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 47 | let address: u16 = super::read_16(bus, cpu.program_counter); 48 | let offset_address: u16 = address.wrapping_add(u16::from(cpu.y_register)); 49 | 50 | cpu.remaining_cycles += if (offset_address) & 0xff00 != address & 0xff00 { 51 | //Offset crossed a page boundary, any opcode using this address mode will take an extra cycle 52 | 1 53 | } else { 54 | 0 55 | }; 56 | 57 | cpu.program_counter += 2; 58 | return AddressModeValue::AbsoluteAddress(offset_address); 59 | } 60 | 61 | /// Absolute Y: Address mode returning a 16-bit absolute address offset by the y register. This extra 62 | /// mode accounts for the special case where the instruction takes the same number of cycles regardless 63 | /// of crossing a page boundary. 64 | pub(crate) fn absolute_y_const(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 65 | let address: u16 = super::read_16(bus, cpu.program_counter); 66 | let offset_address: u16 = address.wrapping_add(u16::from(cpu.y_register)); 67 | 68 | cpu.program_counter += 2; 69 | return AddressModeValue::AbsoluteAddress(offset_address); 70 | } 71 | 72 | /// Immediate: Address mode using next byte as value 73 | pub(crate) fn immediate(cpu: &mut MOS6502, _bus: &mut dyn Interface6502) -> AddressModeValue { 74 | //Return the current location of the program counter 75 | let address = cpu.program_counter; 76 | cpu.program_counter += 1; 77 | return AddressModeValue::AbsoluteAddress(address); 78 | } 79 | 80 | /// Implied: Address mode for opcodes that do not require a value or address 81 | pub(crate) fn implied(_cpu: &mut MOS6502, _bus: &mut dyn Interface6502) -> AddressModeValue { 82 | return AddressModeValue::Implied; 83 | } 84 | 85 | /// Indirect: Address mode that reads from the given address to get the actual address 86 | pub(crate) fn indirect(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 87 | let indirect_address = super::read_16(bus, cpu.program_counter); 88 | 89 | // Simulate bug at page edge 90 | let page = indirect_address & 0xff00; 91 | let address = (u16::from(bus.read(page | ((indirect_address + 1) & 0xff))) << 8) | u16::from(bus.read(indirect_address)); 92 | 93 | cpu.program_counter += 2; 94 | return AddressModeValue::AbsoluteAddress(address); 95 | } 96 | 97 | /// Indirect X: Address mode that reads from the 8-bit given address offset by x to get the actual address 98 | pub(crate) fn indirect_x(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 99 | let indirect_address = bus.read(cpu.program_counter); 100 | 101 | // Simulate bug at page edge 102 | let address = ((bus.read(indirect_address.wrapping_add(cpu.x_register).wrapping_add(1) as u16) as u16) << 8) 103 | | bus.read(indirect_address.wrapping_add(cpu.x_register) as u16) as u16; 104 | 105 | cpu.program_counter += 1; 106 | return AddressModeValue::AbsoluteAddress(address); 107 | } 108 | 109 | /// Indirect Y: Address mode that reads from the 8-bit given address to get the actual address and then offsets it by y 110 | pub(crate) fn indirect_y(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 111 | let indirect_address = bus.read(cpu.program_counter); 112 | // Simulate bug at page edge 113 | let address = ((bus.read(indirect_address.wrapping_add(1) as u16) as u16) << 8) | bus.read(indirect_address as u16) as u16; 114 | let offset_address = address.wrapping_add(u16::from(cpu.y_register)); 115 | 116 | cpu.remaining_cycles += if (offset_address) & 0xff00 != address & 0xff00 { 117 | //Offset crossed a page boundary, any opcode using this address mode will take an extra cycle 118 | 1 119 | } else { 120 | 0 121 | }; 122 | 123 | cpu.program_counter += 1; 124 | return AddressModeValue::AbsoluteAddress(offset_address); 125 | } 126 | 127 | /// Indirect Y: Address mode that reads from the 8-bit given address to get the actual address and then offsets it by y. 128 | /// This extra mode accounts for the special case where the instruction takes the same number of cycles regardless 129 | /// of crossing a page boundary. 130 | pub(crate) fn indirect_y_const(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 131 | let indirect_address = bus.read(cpu.program_counter); 132 | // Simulate bug at page edge 133 | let address = ((bus.read(indirect_address.wrapping_add(1) as u16) as u16) << 8) | bus.read(indirect_address as u16) as u16; 134 | let offset_address = address.wrapping_add(u16::from(cpu.y_register)); 135 | 136 | cpu.program_counter += 1; 137 | return AddressModeValue::AbsoluteAddress(offset_address); 138 | } 139 | 140 | /// Relative: Address mode used by branch instructions that reads an 8-bit signed relative address to add to the program counter 141 | pub(crate) fn relative(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 142 | let relative_address = bus.read(cpu.program_counter); 143 | cpu.program_counter += 1; 144 | return AddressModeValue::RelativeAddress(relative_address); 145 | } 146 | 147 | /// Zero-page: Address mode that uses an 8-bit address to access memory on the 0 page (0x00__) 148 | pub(crate) fn zero_page(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 149 | let address = u16::from(bus.read(cpu.program_counter)); 150 | cpu.program_counter += 1; 151 | return AddressModeValue::AbsoluteAddress(address); 152 | } 153 | 154 | /// Zero-page X: Address mode that uses an 8-bit address to access memory on the 0 page (0x00__), offset by x 155 | // TODO: Implement offset bug 156 | pub(crate) fn zero_page_x(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 157 | let address = bus.read(cpu.program_counter).wrapping_add(cpu.x_register); 158 | cpu.program_counter += 1; 159 | return AddressModeValue::AbsoluteAddress(u16::from(address)); 160 | } 161 | 162 | /// Zero-page Y: Address mode that uses an 8-bit address to access memory on the 0 page (0x00__), offset by y 163 | // TODO: Implement offset bug 164 | pub(crate) fn zero_page_y(cpu: &mut MOS6502, bus: &mut dyn Interface6502) -> AddressModeValue { 165 | let address = bus.read(cpu.program_counter).wrapping_add(cpu.y_register); 166 | cpu.program_counter += 1; 167 | return AddressModeValue::AbsoluteAddress(u16::from(address)); 168 | } 169 | 170 | /// Enum for the return type of Address modes 171 | #[derive(PartialEq, Clone, Copy)] 172 | pub(crate) enum AddressModeValue { 173 | Implied, 174 | RelativeAddress(u8), 175 | AbsoluteAddress(u16), 176 | } 177 | 178 | impl fmt::Debug for AddressModeValue { 179 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 180 | match self { 181 | AddressModeValue::Implied => write!(f, "Implied"), 182 | AddressModeValue::RelativeAddress(address) => write!(f, "Relative Address: {:02X}", address), 183 | AddressModeValue::AbsoluteAddress(address) => write!(f, "Absolute Address: {:04X}", address), 184 | } 185 | } 186 | } 187 | 188 | //TESTS--------------------------------------------------------------------------------------------- 189 | 190 | #[cfg(test)] 191 | mod test { 192 | #![allow(unused_variables, unused_mut)] //Allow some warnings for test code 193 | use super::*; 194 | use crate::test_utilities::StubInterface6502; 195 | 196 | #[test] 197 | fn test_absolute() { 198 | let mut cpu = MOS6502::new_start(0x0000); 199 | let mut bus = StubInterface6502::new( 200 | |address, read_count| 0xff, 201 | |address, data, write_count| panic!("Write function was called"), 202 | ); 203 | 204 | let expected_program_counter = cpu.program_counter + 2; 205 | let address_mode_value = absolute(&mut cpu, &mut bus); 206 | 207 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xffff)); 208 | assert_eq!(cpu.remaining_cycles, 0); 209 | assert_eq!(expected_program_counter, cpu.program_counter) 210 | } 211 | 212 | #[test] 213 | fn test_absolute_x() { 214 | let mut cpu = MOS6502::new_start(0x0000); 215 | let mut bus = StubInterface6502::new( 216 | |address, read_count| 0x00, 217 | |address, data, write_count| panic!("Write function was called"), 218 | ); 219 | cpu.x_register = 2; 220 | let expected_program_counter = cpu.program_counter + 2; 221 | let address_mode_value = absolute_x(&mut cpu, &mut bus); 222 | 223 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x0002)); 224 | assert_eq!(cpu.remaining_cycles, 0); 225 | assert_eq!(expected_program_counter, cpu.program_counter) 226 | } 227 | 228 | #[test] 229 | fn test_absolute_x_extra_cycle() { 230 | let mut cpu = MOS6502::new_start(0x0000); 231 | let mut bus = StubInterface6502::new( 232 | |address, read_count| 0x10, 233 | |address, data, write_count| panic!("Write function was called"), 234 | ); 235 | cpu.x_register = 255; 236 | let expected_program_counter = cpu.program_counter + 2; 237 | let address_mode_value = absolute_x(&mut cpu, &mut bus); 238 | 239 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x110f)); 240 | assert_eq!(cpu.remaining_cycles, 1); 241 | assert_eq!(expected_program_counter, cpu.program_counter) 242 | } 243 | 244 | #[test] 245 | fn test_absolute_y() { 246 | let mut cpu = MOS6502::new_start(0x0000); 247 | let mut bus = StubInterface6502::new( 248 | |address, read_count| 0x00, 249 | |address, data, write_count| panic!("Write function was called"), 250 | ); 251 | cpu.y_register = 2; 252 | let expected_program_counter = cpu.program_counter + 2; 253 | let address_mode_value = absolute_y(&mut cpu, &mut bus); 254 | 255 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x0002)); 256 | assert_eq!(cpu.remaining_cycles, 0); 257 | assert_eq!(expected_program_counter, cpu.program_counter) 258 | } 259 | 260 | #[test] 261 | fn test_absolute_y_extra_cycle() { 262 | let mut cpu = MOS6502::new_start(0x0000); 263 | let mut bus = StubInterface6502::new( 264 | |address, read_count| 0x10, 265 | |address, data, write_count| panic!("Write function was called"), 266 | ); 267 | cpu.y_register = 255; 268 | let expected_program_counter = cpu.program_counter + 2; 269 | let address_mode_value = absolute_y(&mut cpu, &mut bus); 270 | 271 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x110f)); 272 | assert_eq!(cpu.remaining_cycles, 1); 273 | assert_eq!(expected_program_counter, cpu.program_counter) 274 | } 275 | 276 | #[test] 277 | fn test_immediate() { 278 | let mut cpu = MOS6502::new_start(0x0000); 279 | let mut bus = StubInterface6502::new( 280 | |address, read_count| panic!("Read function was called"), 281 | |address, data, write_count| panic!("Write function was called"), 282 | ); 283 | 284 | let prior_program_counter = cpu.program_counter; 285 | let address_mode_value = immediate(&mut cpu, &mut bus); 286 | 287 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(prior_program_counter)); 288 | assert_eq!(cpu.remaining_cycles, 0); 289 | assert_eq!(prior_program_counter + 1, cpu.program_counter) 290 | } 291 | 292 | #[test] 293 | fn test_implied() { 294 | let mut cpu = MOS6502::new_start(0x0000); 295 | let mut bus = StubInterface6502::new( 296 | |address, read_count| panic!("Read function was called"), 297 | |address, data, write_count| panic!("Write function was called"), 298 | ); 299 | 300 | let expected_program_counter = cpu.program_counter; 301 | let address_mode_value = implied(&mut cpu, &mut bus); 302 | 303 | assert_eq!(address_mode_value, AddressModeValue::Implied); 304 | assert_eq!(cpu.remaining_cycles, 0); 305 | assert_eq!(expected_program_counter, cpu.program_counter) 306 | } 307 | 308 | #[test] 309 | fn test_indirect() { 310 | let mut cpu = MOS6502::new_start(0x0000); 311 | let mut bus = StubInterface6502::new( 312 | |address, read_count| match address { 313 | 0x0000 => 0x11, 314 | 0x0001 => 0x10, 315 | 0x1011 => 0x01, 316 | 0x1012 => 0xff, 317 | _ => 0x00, 318 | }, 319 | |address, data, write_count| panic!("Write function was called"), 320 | ); 321 | 322 | let expected_program_counter = cpu.program_counter + 2; 323 | let address_mode_value = indirect(&mut cpu, &mut bus); 324 | 325 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xff01)); 326 | assert_eq!(cpu.remaining_cycles, 0); 327 | assert_eq!(expected_program_counter, cpu.program_counter) 328 | } 329 | 330 | #[test] 331 | fn test_indirect_page_edge() { 332 | let mut cpu = MOS6502::new_start(0x0000); 333 | let mut bus = StubInterface6502::new( 334 | |address, read_count| match address { 335 | 0x0000 => 0xff, 336 | 0x0001 => 0x10, 337 | 0x10ff => 0x01, 338 | 0x1000 => 0xa7, 339 | _ => 0x00, 340 | }, 341 | |address, data, write_count| panic!("Write function was called"), 342 | ); 343 | 344 | let expected_program_counter = cpu.program_counter + 2; 345 | let address_mode_value = indirect(&mut cpu, &mut bus); 346 | 347 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xa701)); 348 | assert_eq!(cpu.remaining_cycles, 0); 349 | assert_eq!(expected_program_counter, cpu.program_counter) 350 | } 351 | 352 | #[test] 353 | fn test_indirect_x() { 354 | let mut cpu = MOS6502::new_start(0x0000); 355 | let mut bus = StubInterface6502::new( 356 | |address, read_count| match address { 357 | 0x0000 => 0x25, 358 | 0x0035 => 0x01, 359 | 0x0036 => 0xa7, 360 | _ => 0x00, 361 | }, 362 | |address, data, write_count| panic!("Write function was called"), 363 | ); 364 | 365 | cpu.x_register = 0x10; 366 | let expected_program_counter = cpu.program_counter + 1; 367 | let address_mode_value = indirect_x(&mut cpu, &mut bus); 368 | 369 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xa701)); 370 | assert_eq!(cpu.remaining_cycles, 0); 371 | assert_eq!(expected_program_counter, cpu.program_counter) 372 | } 373 | 374 | #[test] 375 | fn test_indirect_x_page_edge() { 376 | let mut cpu = MOS6502::new_start(0x0000); 377 | let mut bus = StubInterface6502::new( 378 | |address, read_count| match address { 379 | 0x0000 => 0xfe, 380 | 0x00ff => 0x01, 381 | _ => 0x00, 382 | }, 383 | |address, data, write_count| panic!("Write function was called"), 384 | ); 385 | 386 | cpu.x_register = 0x1; 387 | let expected_program_counter = cpu.program_counter + 1; 388 | let address_mode_value = indirect_x(&mut cpu, &mut bus); 389 | 390 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xfe01)); 391 | assert_eq!(cpu.remaining_cycles, 0); 392 | assert_eq!(expected_program_counter, cpu.program_counter) 393 | } 394 | 395 | #[test] 396 | fn test_indirect_y() { 397 | let mut cpu = MOS6502::new_start(0x0000); 398 | let mut bus = StubInterface6502::new( 399 | |address, read_count| match address { 400 | 0x0000 => 0x25, 401 | 0x0025 => 0x01, 402 | 0x0026 => 0xa7, 403 | _ => 0x00, 404 | }, 405 | |address, data, write_count| panic!("Write function was called"), 406 | ); 407 | 408 | cpu.y_register = 0x10; 409 | let expected_program_counter = cpu.program_counter + 1; 410 | let address_mode_value = indirect_y(&mut cpu, &mut bus); 411 | 412 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xa711)); 413 | assert_eq!(cpu.remaining_cycles, 0); 414 | assert_eq!(expected_program_counter, cpu.program_counter) 415 | } 416 | 417 | #[test] 418 | fn test_indirect_y_extra_cycle() { 419 | let mut cpu = MOS6502::new_start(0x0000); 420 | let mut bus = StubInterface6502::new( 421 | |address, read_count| match address { 422 | 0x0000 => 0x25, 423 | 0x0025 => 0xff, 424 | 0x0026 => 0xa7, 425 | _ => 0x00, 426 | }, 427 | |address, data, write_count| panic!("Write function was called"), 428 | ); 429 | 430 | cpu.y_register = 0x10; 431 | let expected_program_counter = cpu.program_counter + 1; 432 | let address_mode_value = indirect_y(&mut cpu, &mut bus); 433 | 434 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xa80f)); 435 | assert_eq!(cpu.remaining_cycles, 1); 436 | assert_eq!(expected_program_counter, cpu.program_counter) 437 | } 438 | 439 | #[test] 440 | fn test_indirect_y_page_edge() { 441 | let mut cpu = MOS6502::new_start(0x0000); 442 | let mut bus = StubInterface6502::new( 443 | |address, read_count| match address { 444 | 0x0000 => 0xff, 445 | 0x00ff => 0x0a, 446 | 0x0026 => 0xa7, 447 | _ => 0x00, 448 | }, 449 | |address, data, write_count| panic!("Write function was called"), 450 | ); 451 | 452 | cpu.y_register = 0x10; 453 | let expected_program_counter = cpu.program_counter + 1; 454 | let address_mode_value = indirect_y(&mut cpu, &mut bus); 455 | 456 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0xff1a)); 457 | assert_eq!(cpu.remaining_cycles, 0); 458 | assert_eq!(expected_program_counter, cpu.program_counter) 459 | } 460 | 461 | #[test] 462 | fn test_relative() { 463 | let mut cpu = MOS6502::new_start(0x0000); 464 | let mut bus = StubInterface6502::new( 465 | |address, read_count| 0x10, 466 | |address, data, write_count| panic!("Write function was called"), 467 | ); 468 | 469 | let expected_program_counter = cpu.program_counter + 1; 470 | let address_mode_value = relative(&mut cpu, &mut bus); 471 | 472 | assert_eq!(address_mode_value, AddressModeValue::RelativeAddress(0x10)); 473 | assert_eq!(cpu.remaining_cycles, 0); 474 | assert_eq!(expected_program_counter, cpu.program_counter) 475 | } 476 | 477 | #[test] 478 | fn test_zero_page() { 479 | let mut cpu = MOS6502::new_start(0x0000); 480 | let mut bus = StubInterface6502::new( 481 | |address, read_count| 0x10, 482 | |address, data, write_count| panic!("Write function was called"), 483 | ); 484 | 485 | let expected_program_counter = cpu.program_counter + 1; 486 | let address_mode_value = zero_page(&mut cpu, &mut bus); 487 | 488 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x0010)); 489 | assert_eq!(cpu.remaining_cycles, 0); 490 | assert_eq!(expected_program_counter, cpu.program_counter) 491 | } 492 | 493 | #[test] 494 | fn test_zero_page_x() { 495 | let mut cpu = MOS6502::new_start(0x0000); 496 | let mut bus = StubInterface6502::new( 497 | |address, read_count| 0x10, 498 | |address, data, write_count| panic!("Write function was called"), 499 | ); 500 | 501 | cpu.x_register = 0x10; 502 | let expected_program_counter = cpu.program_counter + 1; 503 | let address_mode_value = zero_page_x(&mut cpu, &mut bus); 504 | 505 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x0020)); 506 | assert_eq!(cpu.remaining_cycles, 0); 507 | assert_eq!(expected_program_counter, cpu.program_counter) 508 | } 509 | 510 | #[test] 511 | fn test_zero_page_y() { 512 | let mut cpu = MOS6502::new_start(0x0000); 513 | let mut bus = StubInterface6502::new( 514 | |address, read_count| 0x10, 515 | |address, data, write_count| panic!("Write function was called"), 516 | ); 517 | 518 | cpu.y_register = 0x10; 519 | let expected_program_counter = cpu.program_counter + 1; 520 | let address_mode_value = zero_page_y(&mut cpu, &mut bus); 521 | 522 | assert_eq!(address_mode_value, AddressModeValue::AbsoluteAddress(0x0020)); 523 | assert_eq!(cpu.remaining_cycles, 0); 524 | assert_eq!(expected_program_counter, cpu.program_counter) 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /src/opcodes/illegal.rs: -------------------------------------------------------------------------------- 1 | //! ### ILLEGAL OPCODES 2 | //! This module contains all functions for the illegal opcodes that are not defined by official sources 3 | use super::*; 4 | use crate::address_modes::AddressModeValue; 5 | use crate::MOS6502; 6 | 7 | /// SLO: Combines the ASl and ORA opcodes 8 | pub(super) fn slo(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 9 | if cfg!(feature = "illegal_opcodes") { 10 | asl(cpu, bus, address_mode_value); 11 | ora(cpu, bus, address_mode_value); 12 | } else { 13 | warn!("Illegal opcode SLO called, ignoring"); 14 | } 15 | } 16 | 17 | /// RLA: Combines the ROL and AND opcodes 18 | pub(super) fn rla(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 19 | if cfg!(feature = "illegal_opcodes") { 20 | rol(cpu, bus, address_mode_value); 21 | and(cpu, bus, address_mode_value); 22 | } else { 23 | warn!("Illegal opcode RLA called, ignoring"); 24 | } 25 | } 26 | 27 | /// SRE: Combines the LSR and EOR opcodes 28 | pub(super) fn sre(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 29 | if cfg!(feature = "illegal_opcodes") { 30 | lsr(cpu, bus, address_mode_value); 31 | eor(cpu, bus, address_mode_value); 32 | } else { 33 | warn!("Illegal opcode SRE called, ignoring"); 34 | } 35 | } 36 | 37 | /// RRA: Combines the ROR and ADC opcodes 38 | pub(super) fn rra(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 39 | if cfg!(feature = "illegal_opcodes") { 40 | ror(cpu, bus, address_mode_value); 41 | adc(cpu, bus, address_mode_value); 42 | } else { 43 | warn!("Illegal opcode RRA called, ignoring"); 44 | } 45 | } 46 | 47 | /// SAX: Sets the accumulator to the result of a logical and performed with the accumulator and x register 48 | pub(super) fn sax(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 49 | if cfg!(feature = "illegal_opcodes") { 50 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 51 | bus.write(address, cpu.accumulator & cpu.x_register); 52 | } else { 53 | panic!("SAX opcode called with invalid address mode!") 54 | } 55 | } else { 56 | warn!("Illegal opcode SAX called, ignoring"); 57 | } 58 | } 59 | 60 | /// LAX: Combines the LDA and LDX opcodes, loading the addressed value into both registers 61 | pub(super) fn lax(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 62 | if cfg!(feature = "illegal_opcodes") { 63 | lda(cpu, bus, address_mode_value); 64 | ldx(cpu, bus, address_mode_value); 65 | } else { 66 | warn!("Illegal opcode LAX called, ignoring"); 67 | } 68 | } 69 | 70 | /// DCP: Combines the DEC and CMP opcodes, decrementing the addressed value and comparing it to the accumulator 71 | pub(super) fn dcp(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 72 | if cfg!(feature = "illegal_opcodes") { 73 | dec(cpu, bus, address_mode_value); 74 | cmp(cpu, bus, address_mode_value); 75 | } else { 76 | warn!("Illegal opcode DCP called, ignoring"); 77 | } 78 | } 79 | 80 | /// ISC: Combines the INC and SBC opcodes, incrementing the addressed value and then subtracting it from the accumulator 81 | pub(super) fn isc(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 82 | if cfg!(feature = "illegal_opcodes") { 83 | inc(cpu, bus, address_mode_value); 84 | sbc(cpu, bus, address_mode_value); 85 | } else { 86 | warn!("Illegal opcode ISC called, ignoring"); 87 | } 88 | } 89 | /// ANC: Performs a logical AND on the accumulator with the immediate value and sets the carry flag based on bit 7 like ASL 90 | pub(super) fn anc(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 91 | if cfg!(feature = "illegal_opcodes") { 92 | and(cpu, bus, address_mode_value); 93 | cpu.set_flag(StatusFlag::Carry, cpu.accumulator >> 7 == 1); 94 | } else { 95 | warn!("Illegal opcode ANC called, ignoring"); 96 | } 97 | } 98 | 99 | /// ALR: Combines the AND (immediate) and LSR opcodes, shifting the accumulator right after the AND is performed 100 | pub(super) fn alr(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 101 | if cfg!(feature = "illegal_opcodes") { 102 | and(cpu, bus, address_mode_value); 103 | lsr(cpu, bus, AddressModeValue::Implied); 104 | } else { 105 | warn!("Illegal opcode ALR called, ignoring"); 106 | } 107 | } 108 | 109 | /// ARR: Combines the AND (immediate) and ROR opcodes, rotating the accumulator right after the AND is performed 110 | /// 111 | /// NOTE: This can have some unexpected effects on flags 112 | // TODO: Verify this behaviour with a more reputable source 113 | pub(super) fn arr(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 114 | if cfg!(feature = "illegal_opcodes") { 115 | and(cpu, bus, address_mode_value); 116 | //Some flags are set based on ADC 117 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 118 | let value = bus.read(address); 119 | let result = cpu.accumulator.wrapping_add(value); 120 | cpu.set_flag( 121 | StatusFlag::Overflow, 122 | (!(cpu.accumulator ^ value) & (cpu.accumulator ^ result) & StatusFlag::Negative as u8) > 0, 123 | ); 124 | } else { 125 | panic!("ARR opcode called with invalid address mode!") 126 | } 127 | cpu.accumulator &= !1u8; // The state of bit 0 is lost instead of being placed in the carry bit 128 | ror(cpu, bus, AddressModeValue::Implied); 129 | } else { 130 | warn!("Illegal opcode ARR called, ignoring"); 131 | } 132 | } 133 | 134 | /// XAA: Combines the TXA and AND opcodes, copying the x register into the accumulator and then ANDing it with the addressed value 135 | pub(super) fn xaa(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 136 | if cfg!(feature = "illegal_opcodes") { 137 | txa(cpu, bus, AddressModeValue::Implied); 138 | and(cpu, bus, address_mode_value); 139 | } else { 140 | warn!("Illegal opcode XAA called, ignoring"); 141 | } 142 | } 143 | 144 | /// AXS: Sets the x register to the result of the x register AND the accumulator minus the immediate value 145 | pub(super) fn axs(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 146 | if cfg!(feature = "illegal_opcodes") { 147 | let value = cpu.accumulator & cpu.x_register; 148 | cpu.x_register = compare(cpu, bus, value, address_mode_value); 149 | } else { 150 | warn!("Illegal opcode AXS called, ignoring"); 151 | } 152 | } 153 | 154 | /// AHX: Sets the addressed value to the high byte of the address AND A AND X 155 | pub(super) fn ahx(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 156 | if cfg!(feature = "illegal_opcodes") { 157 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 158 | bus.write(address, cpu.accumulator & cpu.x_register & (address >> 8) as u8); 159 | } else { 160 | panic!("AHX opcode called with invalid address mode!") 161 | } 162 | } else { 163 | warn!("Illegal opcode AHX called, ignoring"); 164 | } 165 | } 166 | 167 | /// SHY: Sets the addressed value to the high byte of the address AND Y 168 | pub(super) fn shy(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 169 | if cfg!(feature = "illegal_opcodes") { 170 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 171 | bus.write(address, cpu.y_register & (address >> 8) as u8); 172 | } else { 173 | panic!("SHY opcode called with invalid address mode!") 174 | } 175 | } else { 176 | warn!("Illegal opcode SHY called, ignoring"); 177 | } 178 | } 179 | 180 | /// SHX: Sets the addressed value to the high byte of the address AND X 181 | pub(super) fn shx(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 182 | if cfg!(feature = "illegal_opcodes") { 183 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 184 | bus.write(address, cpu.x_register & (address >> 8) as u8); 185 | } else { 186 | panic!("SHX opcode called with invalid address mode!") 187 | } 188 | } else { 189 | warn!("Illegal opcode SHX called, ignoring"); 190 | } 191 | } 192 | 193 | /// TAS: Sets the stack pointer to the accumulator AND the x register and then mimics AHX 194 | pub(super) fn tas(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 195 | if cfg!(feature = "illegal_opcodes") { 196 | cpu.stack_pointer = cpu.accumulator & cpu.x_register; 197 | ahx(cpu, bus, address_mode_value); 198 | } else { 199 | warn!("Illegal opcode TAS called, ignoring"); 200 | } 201 | } 202 | 203 | /// LAS: Sets the stack pointer, x register, and accumulator to the addressed value AND the stack pointer 204 | pub(super) fn las(cpu: &mut MOS6502, bus: &mut dyn Interface6502, address_mode_value: AddressModeValue) { 205 | if cfg!(feature = "illegal_opcodes") { 206 | if let AddressModeValue::AbsoluteAddress(address) = address_mode_value { 207 | let value = bus.read(address) & cpu.stack_pointer; 208 | cpu.accumulator = value; 209 | cpu.x_register = value; 210 | cpu.stack_pointer = value; 211 | cpu.set_flag(StatusFlag::Negative, value & StatusFlag::Negative as u8 > 0); 212 | cpu.set_flag(StatusFlag::Zero, value == 0); 213 | } else { 214 | panic!("LAS opcode called with invalid address mode!") 215 | } 216 | } else { 217 | warn!("Illegal opcode LAS called, ignoring"); 218 | } 219 | } 220 | 221 | /// KIL: Halts the CPU, calling this function will just call a panic! 222 | pub(super) fn kil(_cpu: &mut MOS6502, _bus: &mut dyn Interface6502, _address_mode_value: AddressModeValue) { 223 | if cfg!(feature = "illegal_opcodes") { 224 | error!("KIL opcode called!, crashing the emulator..."); 225 | panic!("KIL opcode called!"); 226 | } else { 227 | warn!("Illegal opcode KIL called, ignoring"); 228 | } 229 | } 230 | 231 | #[cfg(all(test, feature = "illegal_opcodes"))] 232 | mod test { 233 | #![allow(unused_variables, unused_mut)] // Allow some warnings for test code 234 | 235 | use super::*; 236 | use crate::address_modes::AddressModeValue; 237 | use crate::test_utilities::StubInterface6502; 238 | use crate::MOS6502; 239 | 240 | #[test] 241 | fn test_slo() { 242 | let mut cpu_initial = MOS6502 { 243 | accumulator: 0x01, 244 | ..Default::default() 245 | }; 246 | 247 | let mut stub_bus = StubInterface6502 { 248 | read: |address, read_count| match (address, read_count) { 249 | (0x00ff, 1) => 0x4f, 250 | (0x00ff, 2) => 0x4f << 1, 251 | _ => panic!("Unintended Address Accessed: 0x{:X}, read_count: {}", address, read_count), 252 | }, 253 | write: |address, data, write_count| { 254 | assert_eq!(address, 0x00ff); 255 | assert_eq!(data, 0x4f << 1); 256 | }, 257 | ..Default::default() 258 | }; 259 | let mut cpu_expected = MOS6502 { 260 | accumulator: 0x9f, 261 | ..cpu_initial 262 | }; 263 | cpu_expected.set_flag(StatusFlag::Negative, true); 264 | 265 | slo(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 266 | 267 | assert_eq!(cpu_initial, cpu_expected); 268 | } 269 | 270 | #[test] 271 | fn test_slo_zero_carry_flag() { 272 | let mut cpu_initial = MOS6502 { 273 | accumulator: 0x00, 274 | ..Default::default() 275 | }; 276 | 277 | let mut stub_bus = StubInterface6502 { 278 | read: |address, read_count| match (address, read_count) { 279 | (0x00ff, 1) => 0x80, 280 | (0x00ff, 2) => 0x80 << 1, 281 | _ => panic!("Unintended Address Accessed: 0x{:X}, read_count: {}", address, read_count), 282 | }, 283 | write: |address, data, write_count| { 284 | assert_eq!(address, 0x00ff); 285 | assert_eq!(data, 0x80 << 1); 286 | }, 287 | ..Default::default() 288 | }; 289 | 290 | let mut cpu_expected = MOS6502 { 291 | accumulator: 0x00, 292 | ..cpu_initial 293 | }; 294 | cpu_expected.set_flag(StatusFlag::Carry, true); 295 | cpu_expected.set_flag(StatusFlag::Zero, true); 296 | 297 | slo(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 298 | 299 | assert_eq!(cpu_initial, cpu_expected); 300 | } 301 | 302 | #[test] 303 | fn test_rla() { 304 | let mut cpu_initial = MOS6502 { 305 | accumulator: 0x06, 306 | ..Default::default() 307 | }; 308 | cpu_initial.set_flag(StatusFlag::Carry, true); 309 | 310 | let mut stub_bus = StubInterface6502 { 311 | read: |address, read_count| match (address, read_count) { 312 | (0x00ff, 1) => 0x41, 313 | (0x00ff, 2) => 0x83, 314 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 315 | }, 316 | write: |address, data, write_count| { 317 | assert_eq!(address, 0x00ff); 318 | assert_eq!(data, 0x83); 319 | }, 320 | ..Default::default() 321 | }; 322 | 323 | let mut cpu_expected = MOS6502 { 324 | accumulator: 0x02, 325 | ..cpu_initial 326 | }; 327 | cpu_expected.set_flag(StatusFlag::Carry, false); 328 | 329 | rla(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 330 | 331 | assert_eq!(cpu_initial, cpu_expected); 332 | } 333 | 334 | #[test] 335 | fn test_rla_zero_carry_flag() { 336 | let mut cpu_initial = MOS6502 { 337 | accumulator: 0x08, 338 | ..Default::default() 339 | }; 340 | 341 | let mut stub_bus = StubInterface6502 { 342 | read: |address, read_count| match (address, read_count) { 343 | (0x00ff, 1) => 0xd0, 344 | (0x00ff, 2) => 0xd0 << 1, 345 | _ => panic!("Unintended Address Accessed: 0x{:X}, read_count: {}", address, read_count), 346 | }, 347 | write: |address, data, write_count| { 348 | assert_eq!(address, 0x00ff); 349 | assert_eq!(data, 0xd0 << 1); 350 | }, 351 | ..Default::default() 352 | }; 353 | 354 | let mut cpu_expected = MOS6502 { 355 | accumulator: 0x00, 356 | ..cpu_initial 357 | }; 358 | cpu_expected.set_flag(StatusFlag::Carry, true); 359 | cpu_expected.set_flag(StatusFlag::Zero, true); 360 | 361 | rla(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 362 | 363 | assert_eq!(cpu_initial, cpu_expected); 364 | } 365 | 366 | #[test] 367 | fn test_sre() { 368 | let mut cpu_initial = MOS6502 { 369 | accumulator: 0x80, 370 | ..Default::default() 371 | }; 372 | 373 | let mut stub_bus = StubInterface6502 { 374 | read: |address, read_count| match (address, read_count) { 375 | (0x00ff, 1) => 0x80, 376 | (0x00ff, 2) => 0x80 >> 1, 377 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 378 | }, 379 | write: |address, data, write_count| { 380 | assert_eq!(address, 0x00ff); 381 | assert_eq!(data, 0x80 >> 1); 382 | }, 383 | ..Default::default() 384 | }; 385 | 386 | let mut cpu_expected = MOS6502 { 387 | accumulator: 0xc0, 388 | ..cpu_initial 389 | }; 390 | cpu_expected.set_flag(StatusFlag::Negative, true); 391 | 392 | sre(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 393 | 394 | assert_eq!(cpu_initial, cpu_expected); 395 | } 396 | 397 | #[test] 398 | fn test_sre_zero_carry_flag() { 399 | let mut cpu_initial = MOS6502 { 400 | accumulator: 0x7f, 401 | ..Default::default() 402 | }; 403 | 404 | let mut stub_bus = StubInterface6502 { 405 | read: |address, read_count| match (address, read_count) { 406 | (0x00ff, 1) => 0xff, 407 | (0x00ff, 2) => 0xff >> 1, 408 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 409 | }, 410 | write: |address, data, write_count| { 411 | assert_eq!(address, 0x00ff); 412 | assert_eq!(data, 0xff >> 1); 413 | }, 414 | ..Default::default() 415 | }; 416 | 417 | let mut cpu_expected = MOS6502 { 418 | accumulator: 0x00, 419 | ..cpu_initial 420 | }; 421 | cpu_expected.set_flag(StatusFlag::Zero, true); 422 | cpu_expected.set_flag(StatusFlag::Carry, true); 423 | 424 | sre(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 425 | assert_eq!(cpu_initial, cpu_expected); 426 | } 427 | 428 | #[test] 429 | fn test_rra() { 430 | let mut cpu_initial = MOS6502 { 431 | accumulator: 0x09, 432 | ..Default::default() 433 | }; 434 | cpu_initial.set_flag(StatusFlag::Carry, true); 435 | 436 | let mut stub_bus = StubInterface6502 { 437 | read: |address, read_count| match (address, read_count) { 438 | (0x00ff, 1) => 0x20, 439 | (0x00ff, 2) => 0x90, 440 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 441 | }, 442 | write: |address, data, write_count| { 443 | assert_eq!(address, 0x00ff); 444 | assert_eq!(data, 0x90); 445 | }, 446 | ..Default::default() 447 | }; 448 | 449 | let mut cpu_expected = MOS6502 { 450 | accumulator: 0x99, 451 | ..cpu_initial 452 | }; 453 | cpu_expected.set_flag(StatusFlag::Carry, false); 454 | cpu_expected.set_flag(StatusFlag::Negative, true); 455 | 456 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 457 | 458 | assert_eq!(cpu_initial, cpu_expected); 459 | } 460 | 461 | #[test] 462 | fn test_rra_zero_carry_flags() { 463 | let mut cpu_initial = MOS6502 { 464 | accumulator: 0xff, 465 | ..Default::default() 466 | }; 467 | 468 | let mut stub_bus = StubInterface6502 { 469 | read: |address, read_count| match (address, read_count) { 470 | (0x00ff, 1) => 0x02, 471 | (0x00ff, 2) => 0x01, 472 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 473 | }, 474 | write: |address, data, write_count| { 475 | assert_eq!(address, 0x00ff); 476 | assert_eq!(data, 0x01); 477 | }, 478 | ..Default::default() 479 | }; 480 | 481 | let mut cpu_expected = MOS6502 { 482 | accumulator: 0x00, 483 | ..cpu_initial 484 | }; 485 | cpu_expected.set_flag(StatusFlag::Zero, true); 486 | cpu_expected.set_flag(StatusFlag::Carry, true); 487 | 488 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 489 | 490 | assert_eq!(cpu_initial, cpu_expected); 491 | } 492 | 493 | #[test] 494 | fn test_rra_overflow_negative_flags() { 495 | let mut cpu_initial = MOS6502 { 496 | accumulator: 0x7f, 497 | ..Default::default() 498 | }; 499 | 500 | let mut stub_bus = StubInterface6502 { 501 | read: |address, read_count| match (address, read_count) { 502 | (0x00ff, 1) => 0x02, 503 | (0x00ff, 2) => 0x01, 504 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 505 | }, 506 | write: |address, data, write_count| { 507 | assert_eq!(address, 0x00ff); 508 | assert_eq!(data, 0x01); 509 | }, 510 | ..Default::default() 511 | }; 512 | 513 | let mut cpu_expected = MOS6502 { 514 | accumulator: 0x80, 515 | ..cpu_initial 516 | }; 517 | cpu_expected.set_flag(StatusFlag::Overflow, true); 518 | cpu_expected.set_flag(StatusFlag::Negative, true); 519 | 520 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 521 | 522 | assert_eq!(cpu_initial, cpu_expected); 523 | } 524 | 525 | #[test] 526 | #[cfg(feature = "binary_coded_decimal")] 527 | fn test_rra_decimal_mode() { 528 | let mut cpu_initial = MOS6502 { 529 | accumulator: 0x09, 530 | ..Default::default() 531 | }; 532 | cpu_initial.set_flag(StatusFlag::Decimal, true); 533 | 534 | let mut stub_bus = StubInterface6502 { 535 | read: |address, read_count| match (address, read_count) { 536 | (0x00ff, 1) => 0x12, 537 | (0x00ff, 2) => 0x09, 538 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 539 | }, 540 | write: |address, data, write_count| { 541 | assert_eq!(address, 0x00ff); 542 | assert_eq!(data, 0x09); 543 | }, 544 | ..Default::default() 545 | }; 546 | 547 | let mut cpu_expected = MOS6502 { 548 | accumulator: 0x18, 549 | ..cpu_initial 550 | }; 551 | 552 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 553 | 554 | assert_eq!(cpu_initial, cpu_expected); 555 | } 556 | 557 | #[test] 558 | #[cfg(feature = "binary_coded_decimal")] 559 | fn test_rra_decimal_mode_zero_flag() { 560 | let mut cpu_initial = MOS6502 { 561 | accumulator: 0x98, 562 | ..Default::default() 563 | }; 564 | cpu_initial.set_flag(StatusFlag::Decimal, true); 565 | 566 | let mut stub_bus = StubInterface6502 { 567 | read: |address, read_count| match (address, read_count) { 568 | (0x00ff, 1) => 0x03, 569 | (0x00ff, 2) => 0x01, 570 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 571 | }, 572 | write: |address, data, write_count| { 573 | assert_eq!(address, 0x00ff); 574 | assert_eq!(data, 0x01); 575 | }, 576 | ..Default::default() 577 | }; 578 | 579 | let mut cpu_expected = MOS6502 { 580 | accumulator: 0x00, 581 | ..cpu_initial 582 | }; 583 | cpu_expected.set_flag(StatusFlag::Zero, false); 584 | cpu_expected.set_flag(StatusFlag::Carry, true); 585 | cpu_expected.set_flag(StatusFlag::Negative, true); 586 | 587 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 588 | 589 | assert_eq!(cpu_initial, cpu_expected); 590 | } 591 | 592 | #[test] 593 | #[cfg(feature = "binary_coded_decimal")] 594 | fn test_rra_decimal_mode_overflow_negative_flags() { 595 | let mut cpu_initial = MOS6502 { 596 | accumulator: 0x75, 597 | ..Default::default() 598 | }; 599 | cpu_initial.set_flag(StatusFlag::Decimal, true); 600 | 601 | let mut stub_bus = StubInterface6502 { 602 | read: |address, read_count| match (address, read_count) { 603 | (0x00ff, 1) => 0x0c, 604 | (0x00ff, 2) => 0x06, 605 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 606 | }, 607 | write: |address, data, write_count| { 608 | assert_eq!(address, 0x00ff); 609 | assert_eq!(data, 0x06); 610 | }, 611 | ..Default::default() 612 | }; 613 | 614 | let mut cpu_expected = MOS6502 { 615 | accumulator: 0x81, 616 | ..cpu_initial 617 | }; 618 | cpu_expected.set_flag(StatusFlag::Negative, true); 619 | cpu_expected.set_flag(StatusFlag::Overflow, true); 620 | 621 | rra(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 622 | 623 | assert_eq!(cpu_initial, cpu_expected); 624 | } 625 | 626 | #[test] 627 | fn test_sax() { 628 | let mut cpu_initial = MOS6502 { 629 | accumulator: 0x81, 630 | x_register: 0x41, 631 | ..Default::default() 632 | }; 633 | 634 | let mut stub_bus = StubInterface6502 { 635 | read: |address, read_count| { 636 | panic! {"Read function was called"} 637 | }, 638 | write: |address, data, write_count| { 639 | assert_eq!(address, 0x00ff); 640 | assert_eq!(data, 0x01); 641 | }, 642 | ..Default::default() 643 | }; 644 | 645 | let cpu_expected = MOS6502 { ..cpu_initial }; 646 | 647 | sax(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 648 | 649 | assert_eq!(cpu_initial, cpu_expected); 650 | } 651 | 652 | #[test] 653 | fn test_lax() { 654 | let mut cpu_initial = MOS6502 { 655 | accumulator: 0x00, 656 | x_register: 0x00, 657 | ..Default::default() 658 | }; 659 | 660 | let mut stub_bus = StubInterface6502 { 661 | read: |address, read_count| 0xff, 662 | write: |address, data, write_count| { 663 | panic! {"Write function was called"} 664 | }, 665 | ..Default::default() 666 | }; 667 | 668 | let mut cpu_expected = MOS6502 { 669 | accumulator: 0xff, 670 | x_register: 0xff, 671 | ..cpu_initial 672 | }; 673 | cpu_expected.set_flag(StatusFlag::Negative, true); 674 | 675 | lax(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 676 | assert_eq!(cpu_initial, cpu_expected); 677 | } 678 | 679 | #[test] 680 | fn test_lax_negative() { 681 | let mut cpu_initial = MOS6502 { 682 | accumulator: 0xff, 683 | x_register: 0xab, 684 | ..Default::default() 685 | }; 686 | 687 | let mut stub_bus = StubInterface6502 { 688 | read: |address, read_count| 0x00, 689 | write: |address, data, write_count| { 690 | panic! {"Write function was called"} 691 | }, 692 | ..Default::default() 693 | }; 694 | 695 | let mut cpu_expected = MOS6502 { 696 | accumulator: 0x00, 697 | x_register: 0x00, 698 | ..cpu_initial 699 | }; 700 | cpu_expected.set_flag(StatusFlag::Zero, true); 701 | 702 | lax(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 703 | assert_eq!(cpu_initial, cpu_expected); 704 | } 705 | 706 | #[test] 707 | fn test_dcp() { 708 | let mut cpu_initial = MOS6502 { 709 | accumulator: 0xff, 710 | ..Default::default() 711 | }; 712 | 713 | let mut stub_bus = StubInterface6502 { 714 | read: |address, read_count| match (address, read_count) { 715 | (0x00ff, 1) => 0x00, 716 | (0x00ff, 2) => 0xff, 717 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 718 | }, 719 | write: |address, data, write_count| { 720 | assert_eq!(address, 0x00ff); 721 | assert_eq!(data, 0xff); 722 | }, 723 | ..Default::default() 724 | }; 725 | 726 | let mut cpu_expected = MOS6502 { ..cpu_initial }; 727 | cpu_expected.set_flag(StatusFlag::Zero, true); 728 | cpu_expected.set_flag(StatusFlag::Carry, true); 729 | 730 | dcp(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 731 | assert_eq!(cpu_initial, cpu_expected); 732 | } 733 | 734 | #[test] 735 | fn test_dcp_less() { 736 | let mut cpu_initial = MOS6502 { 737 | accumulator: 0x0f, 738 | ..Default::default() 739 | }; 740 | 741 | let mut stub_bus = StubInterface6502 { 742 | read: |address, read_count| match (address, read_count) { 743 | (0x00ff, 1) => 0x11, 744 | (0x00ff, 2) => 0x10, 745 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 746 | }, 747 | write: |address, data, write_count| { 748 | assert_eq!(address, 0x00ff); 749 | assert_eq!(data, 0x10); 750 | }, 751 | ..Default::default() 752 | }; 753 | 754 | let mut cpu_expected = MOS6502 { ..cpu_initial }; 755 | cpu_expected.set_flag(StatusFlag::Negative, true); 756 | 757 | dcp(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 758 | assert_eq!(cpu_initial, cpu_expected); 759 | } 760 | 761 | #[test] 762 | fn test_isc() { 763 | let mut cpu_initial = MOS6502 { 764 | accumulator: 0x10, 765 | ..Default::default() 766 | }; 767 | cpu_initial.set_flag(StatusFlag::Carry, true); 768 | 769 | let mut stub_bus = StubInterface6502 { 770 | read: |address, read_count| match (address, read_count) { 771 | (0x00ff, 1) => 0x07, 772 | (0x00ff, 2) => 0x08, 773 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 774 | }, 775 | write: |address, data, write_count| { 776 | assert_eq!(address, 0x00ff); 777 | assert_eq!(data, 0x08); 778 | }, 779 | ..Default::default() 780 | }; 781 | 782 | let cpu_expected = MOS6502 { 783 | accumulator: 0x08, 784 | ..cpu_initial 785 | }; 786 | 787 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 788 | assert_eq!(cpu_initial, cpu_expected); 789 | } 790 | 791 | #[test] 792 | fn test_isc_overflow() { 793 | let mut cpu_initial = MOS6502 { 794 | accumulator: 0x81, 795 | ..Default::default() 796 | }; 797 | cpu_initial.set_flag(StatusFlag::Carry, true); 798 | 799 | let mut stub_bus = StubInterface6502 { 800 | read: |address, read_count| match (address, read_count) { 801 | (0x00ff, 1) => 0x01, 802 | (0x00ff, 2) => 0x02, 803 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 804 | }, 805 | write: |address, data, write_count| { 806 | assert_eq!(address, 0x00ff); 807 | assert_eq!(data, 0x02); 808 | }, 809 | ..Default::default() 810 | }; 811 | 812 | let mut cpu_expected = MOS6502 { 813 | accumulator: 0x7f, 814 | ..cpu_initial 815 | }; 816 | cpu_expected.set_flag(StatusFlag::Overflow, true); 817 | 818 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 819 | assert_eq!(cpu_initial, cpu_expected); 820 | } 821 | 822 | #[test] 823 | fn test_isc_zero() { 824 | let mut cpu_initial = MOS6502 { 825 | accumulator: 0x10, 826 | ..Default::default() 827 | }; 828 | cpu_initial.set_flag(StatusFlag::Carry, true); 829 | 830 | let mut stub_bus = StubInterface6502 { 831 | read: |address, read_count| match (address, read_count) { 832 | (0x00ff, 1) => 0x0f, 833 | (0x00ff, 2) => 0x10, 834 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 835 | }, 836 | write: |address, data, write_count| { 837 | assert_eq!(address, 0x00ff); 838 | assert_eq!(data, 0x10); 839 | }, 840 | ..Default::default() 841 | }; 842 | 843 | let mut cpu_expected = MOS6502 { 844 | accumulator: 0x00, 845 | ..cpu_initial 846 | }; 847 | cpu_expected.set_flag(StatusFlag::Zero, true); 848 | 849 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 850 | assert_eq!(cpu_initial, cpu_expected); 851 | } 852 | 853 | #[test] 854 | fn test_isc_carry_negative() { 855 | let mut cpu_initial = MOS6502 { 856 | accumulator: 0x10, 857 | ..Default::default() 858 | }; 859 | cpu_initial.set_flag(StatusFlag::Carry, true); 860 | 861 | let mut stub_bus = StubInterface6502 { 862 | read: |address, read_count| match (address, read_count) { 863 | (0x00ff, 1) => 0x10, 864 | (0x00ff, 2) => 0x11, 865 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 866 | }, 867 | write: |address, data, write_count| { 868 | assert_eq!(address, 0x00ff); 869 | assert_eq!(data, 0x11); 870 | }, 871 | ..Default::default() 872 | }; 873 | 874 | let mut cpu_expected = MOS6502 { 875 | accumulator: 0xff, 876 | ..cpu_initial 877 | }; 878 | cpu_expected.set_flag(StatusFlag::Carry, false); 879 | cpu_expected.set_flag(StatusFlag::Negative, true); 880 | 881 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 882 | assert_eq!(cpu_initial, cpu_expected); 883 | } 884 | 885 | #[test] 886 | #[cfg(feature = "binary_coded_decimal")] 887 | fn test_isc_decimal() { 888 | let mut cpu_initial = MOS6502 { 889 | accumulator: 0x12, 890 | ..Default::default() 891 | }; 892 | cpu_initial.set_flag(StatusFlag::Decimal, true); 893 | cpu_initial.set_flag(StatusFlag::Carry, true); 894 | 895 | let mut stub_bus = StubInterface6502 { 896 | read: |address, read_count| match (address, read_count) { 897 | (0x00ff, 1) => 0x05, 898 | (0x00ff, 2) => 0x06, 899 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 900 | }, 901 | write: |address, data, write_count| { 902 | assert_eq!(address, 0x00ff); 903 | assert_eq!(data, 0x06); 904 | }, 905 | ..Default::default() 906 | }; 907 | 908 | let cpu_expected = MOS6502 { 909 | accumulator: 0x06, 910 | ..cpu_initial 911 | }; 912 | 913 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 914 | assert_eq!(cpu_initial, cpu_expected); 915 | } 916 | 917 | #[test] 918 | #[cfg(feature = "binary_coded_decimal")] 919 | fn test_isc_decimal_carry_negative() { 920 | let mut cpu_initial = MOS6502 { 921 | accumulator: 0x12, 922 | x_register: 0x00, 923 | y_register: 0x00, 924 | program_counter: 0x0000, 925 | stack_pointer: 0xfd, 926 | status_register: 0x00, 927 | ..Default::default() 928 | }; 929 | cpu_initial.set_flag(StatusFlag::Decimal, true); 930 | cpu_initial.set_flag(StatusFlag::Carry, true); 931 | 932 | let mut stub_bus = StubInterface6502 { 933 | read: |address, read_count| match (address, read_count) { 934 | (0x00ff, 1) => 0x17, 935 | (0x00ff, 2) => 0x18, 936 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 937 | }, 938 | write: |address, data, write_count| { 939 | assert_eq!(address, 0x00ff); 940 | assert_eq!(data, 0x18); 941 | }, 942 | ..Default::default() 943 | }; 944 | 945 | let mut cpu_expected = MOS6502 { 946 | accumulator: 0x94, 947 | ..cpu_initial 948 | }; 949 | cpu_expected.set_flag(StatusFlag::Carry, false); 950 | cpu_expected.set_flag(StatusFlag::Negative, true); 951 | 952 | isc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 953 | assert_eq!(cpu_initial, cpu_expected); 954 | } 955 | 956 | #[test] 957 | fn test_anc() { 958 | let mut cpu_initial = MOS6502 { 959 | accumulator: 0x95, 960 | ..Default::default() 961 | }; 962 | 963 | let mut stub_bus = StubInterface6502 { 964 | read: |address, read_count| match address { 965 | 0x00ff => 0x80, 966 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 967 | }, 968 | write: |address, data, write_count| { 969 | panic! {"Write function was called"} 970 | }, 971 | ..Default::default() 972 | }; 973 | 974 | let mut cpu_expected = MOS6502 { 975 | accumulator: 0x80, 976 | ..cpu_initial 977 | }; 978 | cpu_expected.set_flag(StatusFlag::Carry, true); 979 | cpu_expected.set_flag(StatusFlag::Negative, true); 980 | 981 | anc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 982 | assert_eq!(cpu_initial, cpu_expected); 983 | } 984 | 985 | #[test] 986 | fn test_anc_zero_flag() { 987 | let mut cpu_initial = MOS6502 { 988 | accumulator: 0xf0, 989 | ..Default::default() 990 | }; 991 | 992 | let mut stub_bus = StubInterface6502 { 993 | read: |address, read_count| match address { 994 | 0x00ff => 0x0f, 995 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 996 | }, 997 | write: |address, data, write_count| { 998 | panic! {"Write function was called"} 999 | }, 1000 | ..Default::default() 1001 | }; 1002 | 1003 | let mut cpu_expected = MOS6502 { 1004 | accumulator: 0x00, 1005 | ..cpu_initial 1006 | }; 1007 | cpu_expected.set_flag(StatusFlag::Zero, true); 1008 | 1009 | anc(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1010 | assert_eq!(cpu_initial, cpu_expected); 1011 | } 1012 | 1013 | #[test] 1014 | fn test_alr() { 1015 | let mut cpu_initial = MOS6502 { 1016 | accumulator: 0x95, 1017 | ..Default::default() 1018 | }; 1019 | 1020 | let mut stub_bus = StubInterface6502 { 1021 | read: |address, read_count| match address { 1022 | 0x00ff => 0x01, 1023 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1024 | }, 1025 | write: |address, data, write_count| { 1026 | panic! {"Write function was called"} 1027 | }, 1028 | ..Default::default() 1029 | }; 1030 | 1031 | let mut cpu_expected = MOS6502 { 1032 | accumulator: 0x00, 1033 | ..cpu_initial 1034 | }; 1035 | cpu_expected.set_flag(StatusFlag::Carry, true); 1036 | cpu_expected.set_flag(StatusFlag::Zero, true); 1037 | 1038 | alr(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1039 | assert_eq!(cpu_initial, cpu_expected); 1040 | } 1041 | 1042 | #[test] 1043 | fn test_alr_negative_flag() { 1044 | let mut cpu_initial = MOS6502 { 1045 | accumulator: 0xf0, 1046 | ..Default::default() 1047 | }; 1048 | cpu_initial.set_flag(StatusFlag::Negative, true); 1049 | 1050 | let mut stub_bus = StubInterface6502 { 1051 | read: |address, read_count| match address { 1052 | 0x00ff => 0xf0, 1053 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1054 | }, 1055 | write: |address, data, write_count| { 1056 | panic! {"Write function was called"} 1057 | }, 1058 | ..Default::default() 1059 | }; 1060 | 1061 | let mut cpu_expected = MOS6502 { 1062 | accumulator: 0x78, 1063 | ..cpu_initial 1064 | }; 1065 | cpu_expected.set_flag(StatusFlag::Negative, false); 1066 | 1067 | alr(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1068 | assert_eq!(cpu_initial, cpu_expected); 1069 | } 1070 | 1071 | #[test] 1072 | fn test_arr() { 1073 | let mut cpu_initial = MOS6502 { 1074 | accumulator: 0x95, 1075 | ..Default::default() 1076 | }; 1077 | 1078 | let mut stub_bus = StubInterface6502 { 1079 | read: |address, read_count| match address { 1080 | 0x00ff => 0x01, 1081 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1082 | }, 1083 | write: |address, data, write_count| { 1084 | panic! {"Write function was called"} 1085 | }, 1086 | ..Default::default() 1087 | }; 1088 | 1089 | let mut cpu_expected = MOS6502 { 1090 | accumulator: 0x00, 1091 | ..cpu_initial 1092 | }; 1093 | cpu_expected.set_flag(StatusFlag::Zero, true); 1094 | 1095 | arr(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1096 | assert_eq!(cpu_initial, cpu_expected); 1097 | } 1098 | 1099 | #[test] 1100 | fn test_arr_negative_overflow_flags() { 1101 | let mut cpu_initial = MOS6502 { 1102 | accumulator: 0x70, 1103 | ..Default::default() 1104 | }; 1105 | cpu_initial.set_flag(StatusFlag::Carry, true); 1106 | 1107 | let mut stub_bus = StubInterface6502 { 1108 | read: |address, read_count| match address { 1109 | 0x00ff => 0x70, 1110 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1111 | }, 1112 | write: |address, data, write_count| { 1113 | panic! {"Write function was called"} 1114 | }, 1115 | ..Default::default() 1116 | }; 1117 | 1118 | let mut cpu_expected = MOS6502 { 1119 | accumulator: 0xb8, 1120 | ..cpu_initial 1121 | }; 1122 | cpu_expected.set_flag(StatusFlag::Carry, false); 1123 | cpu_expected.set_flag(StatusFlag::Negative, true); 1124 | cpu_expected.set_flag(StatusFlag::Overflow, true); 1125 | 1126 | arr(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1127 | assert_eq!(cpu_initial, cpu_expected); 1128 | } 1129 | 1130 | #[test] 1131 | fn test_xaa() { 1132 | let mut cpu_initial = MOS6502 { 1133 | accumulator: 0x01, 1134 | x_register: 0x95, 1135 | ..Default::default() 1136 | }; 1137 | 1138 | let mut stub_bus = StubInterface6502 { 1139 | read: |address, read_count| match address { 1140 | 0x00ff => 0x80, 1141 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1142 | }, 1143 | write: |address, data, write_count| { 1144 | panic! {"Write function was called"} 1145 | }, 1146 | ..Default::default() 1147 | }; 1148 | 1149 | let mut cpu_expected = MOS6502 { 1150 | accumulator: 0x80, 1151 | ..cpu_initial 1152 | }; 1153 | cpu_expected.set_flag(StatusFlag::Negative, true); 1154 | 1155 | xaa(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1156 | 1157 | assert_eq!(cpu_initial, cpu_expected); 1158 | } 1159 | 1160 | #[test] 1161 | fn test_xaa_zero_flag() { 1162 | let mut cpu_initial = MOS6502 { 1163 | accumulator: 0x00, 1164 | x_register: 0xf0, 1165 | ..Default::default() 1166 | }; 1167 | 1168 | let mut stub_bus = StubInterface6502 { 1169 | read: |address, read_count| match address { 1170 | 0x00ff => 0x0f, 1171 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1172 | }, 1173 | write: |address, data, write_count| { 1174 | panic! {"Write function was called"} 1175 | }, 1176 | ..Default::default() 1177 | }; 1178 | 1179 | let mut cpu_expected = MOS6502 { 1180 | accumulator: 0x00, 1181 | ..cpu_initial 1182 | }; 1183 | cpu_expected.set_flag(StatusFlag::Zero, true); 1184 | 1185 | xaa(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1186 | 1187 | assert_eq!(cpu_initial, cpu_expected); 1188 | } 1189 | 1190 | #[test] 1191 | fn test_axs() { 1192 | let mut cpu_initial = MOS6502 { 1193 | accumulator: 0xff, 1194 | x_register: 0x0f, 1195 | ..Default::default() 1196 | }; 1197 | 1198 | let mut stub_bus = StubInterface6502 { 1199 | read: |address, read_count| match address { 1200 | 0x00ff => 0x0f, 1201 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1202 | }, 1203 | write: |address, data, write_count| { 1204 | panic! {"Write function was called"} 1205 | }, 1206 | ..Default::default() 1207 | }; 1208 | 1209 | let mut cpu_expected = MOS6502 { 1210 | x_register: 0x00, 1211 | ..cpu_initial 1212 | }; 1213 | cpu_expected.set_flag(StatusFlag::Zero, true); 1214 | cpu_expected.set_flag(StatusFlag::Carry, true); 1215 | 1216 | axs(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1217 | assert_eq!(cpu_initial, cpu_expected); 1218 | } 1219 | 1220 | #[test] 1221 | fn test_axs_less() { 1222 | let mut cpu_initial = MOS6502 { 1223 | accumulator: 0xff, 1224 | x_register: 0x0f, 1225 | ..Default::default() 1226 | }; 1227 | 1228 | let mut stub_bus = StubInterface6502 { 1229 | read: |address, read_count| match address { 1230 | 0x00ff => 0x10, 1231 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1232 | }, 1233 | write: |address, data, write_count| { 1234 | panic! {"Write function was called"} 1235 | }, 1236 | ..Default::default() 1237 | }; 1238 | 1239 | let mut cpu_expected = MOS6502 { 1240 | x_register: 0xff, 1241 | ..cpu_initial 1242 | }; 1243 | cpu_expected.set_flag(StatusFlag::Negative, true); 1244 | 1245 | axs(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x00ff)); 1246 | assert_eq!(cpu_initial, cpu_expected); 1247 | } 1248 | 1249 | #[test] 1250 | fn test_ahx() { 1251 | let mut cpu_initial = MOS6502 { 1252 | accumulator: 0xff, 1253 | x_register: 0x0f, 1254 | ..Default::default() 1255 | }; 1256 | 1257 | let mut stub_bus = StubInterface6502 { 1258 | read: |address, read_count| match address { 1259 | 0x01ff => 0x0f, 1260 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1261 | }, 1262 | write: |address, data, write_count| { 1263 | assert_eq!(0x01ff, address); 1264 | assert_eq!(0x01, data); 1265 | }, 1266 | ..Default::default() 1267 | }; 1268 | 1269 | let mut cpu_expected = MOS6502 { ..cpu_initial }; 1270 | 1271 | ahx(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x01ff)); 1272 | assert_eq!(cpu_initial, cpu_expected); 1273 | } 1274 | 1275 | #[test] 1276 | fn test_shx() { 1277 | let mut cpu_initial = MOS6502 { 1278 | x_register: 0x0f, 1279 | ..Default::default() 1280 | }; 1281 | 1282 | let mut stub_bus = StubInterface6502 { 1283 | read: |address, read_count| match address { 1284 | 0x0fff => 0x0f, 1285 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1286 | }, 1287 | write: |address, data, write_count| { 1288 | assert_eq!(0x01ff, address); 1289 | assert_eq!(0x01, data); 1290 | }, 1291 | ..Default::default() 1292 | }; 1293 | 1294 | let mut cpu_expected = MOS6502 { ..cpu_initial }; 1295 | 1296 | shx(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x01ff)); 1297 | assert_eq!(cpu_initial, cpu_expected); 1298 | } 1299 | 1300 | #[test] 1301 | fn test_shy() { 1302 | let mut cpu_initial = MOS6502 { 1303 | y_register: 0x03, 1304 | ..Default::default() 1305 | }; 1306 | 1307 | let mut stub_bus = StubInterface6502 { 1308 | read: |address, read_count| match address { 1309 | 0x05ff => 0x09, 1310 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1311 | }, 1312 | write: |address, data, write_count| { 1313 | assert_eq!(0x05ff, address); 1314 | assert_eq!(0x01, data); 1315 | }, 1316 | ..Default::default() 1317 | }; 1318 | 1319 | let mut cpu_expected = MOS6502 { ..cpu_initial }; 1320 | 1321 | shy(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x5ff)); 1322 | assert_eq!(cpu_initial, cpu_expected); 1323 | } 1324 | 1325 | #[test] 1326 | fn test_tas() { 1327 | let mut cpu_initial = MOS6502 { 1328 | accumulator: 0xf1, 1329 | x_register: 0x1f, 1330 | ..Default::default() 1331 | }; 1332 | 1333 | let mut stub_bus = StubInterface6502 { 1334 | read: |address, read_count| match address { 1335 | 0x01ff => 0x0f, 1336 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1337 | }, 1338 | write: |address, data, write_count| { 1339 | assert_eq!(0x01ff, address); 1340 | assert_eq!(0x01, data); 1341 | }, 1342 | ..Default::default() 1343 | }; 1344 | 1345 | let mut cpu_expected = MOS6502 { 1346 | stack_pointer: 0x11, 1347 | ..cpu_initial 1348 | }; 1349 | 1350 | tas(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x01ff)); 1351 | assert_eq!(cpu_initial, cpu_expected); 1352 | } 1353 | 1354 | #[test] 1355 | fn test_las() { 1356 | let mut cpu_initial = MOS6502 { 1357 | accumulator: 0x01, 1358 | x_register: 0x29, 1359 | stack_pointer: 0xf0, 1360 | ..Default::default() 1361 | }; 1362 | 1363 | let mut stub_bus = StubInterface6502 { 1364 | read: |address, read_count| match address { 1365 | 0x01ff => 0x0f, 1366 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1367 | }, 1368 | write: |address, data, write_count| { 1369 | panic! {"Write function was called"} 1370 | }, 1371 | ..Default::default() 1372 | }; 1373 | 1374 | let mut cpu_expected = MOS6502 { 1375 | accumulator: 0x00, 1376 | x_register: 0x00, 1377 | stack_pointer: 0x00, 1378 | ..cpu_initial 1379 | }; 1380 | cpu_expected.set_flag(StatusFlag::Zero, true); 1381 | 1382 | las(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x01ff)); 1383 | assert_eq!(cpu_initial, cpu_expected); 1384 | } 1385 | 1386 | #[test] 1387 | fn test_las_negative() { 1388 | let mut cpu_initial = MOS6502 { 1389 | stack_pointer: 0x8f, 1390 | ..Default::default() 1391 | }; 1392 | 1393 | let mut stub_bus = StubInterface6502 { 1394 | read: |address, read_count| match address { 1395 | 0x01ff => 0xf0, 1396 | _ => panic!("Unintended Address Accessed: 0x{:X}", address), 1397 | }, 1398 | write: |address, data, write_count| { 1399 | panic! {"Write function was called"} 1400 | }, 1401 | ..Default::default() 1402 | }; 1403 | 1404 | let mut cpu_expected = MOS6502 { 1405 | accumulator: 0x80, 1406 | x_register: 0x80, 1407 | stack_pointer: 0x80, 1408 | ..cpu_initial 1409 | }; 1410 | cpu_expected.set_flag(StatusFlag::Negative, true); 1411 | 1412 | las(&mut cpu_initial, &mut stub_bus, AddressModeValue::AbsoluteAddress(0x01ff)); 1413 | assert_eq!(cpu_initial, cpu_expected); 1414 | } 1415 | } 1416 | --------------------------------------------------------------------------------