├── 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 | [](https://crates.io/crates/emulator_6502)
3 | [](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 | [](https://travis-ci.org/GarettCooper/emulator_6502)
2 | [](https://crates.io/crates/emulator_6502)
3 | [](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 |
--------------------------------------------------------------------------------