Skip to main content

mos6502/
lib.rs

1// Copyright (C) 2014 The 6502-rs Developers
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions
6// are met:
7// 1. Redistributions of source code must retain the above copyright
8//    notice, this list of conditions and the following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright
10//    notice, this list of conditions and the following disclaimer in the
11//    documentation and/or other materials provided with the distribution.
12// 3. Neither the names of the copyright holders nor the names of any
13//    contributors may be used to endorse or promote products derived from this
14//    software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26// POSSIBILITY OF SUCH DAMAGE.
27
28//! An emulator for the MOS 6502 CPU and its variants.
29//!
30//! The 6502 was one of the most influential microprocessors in history, powering
31//! iconic systems like the Apple II, Commodore 64, Atari 2600, and Nintendo
32//! Entertainment System. Its low cost democratized computing and helped birth
33//! the personal computer industry.
34//!
35//! ## Variants
36//!
37//! This emulator supports multiple 6502 variants, each with subtle differences
38//! in behavior. Choose the [variant] that matches your target system:
39//!
40//! - **NMOS 6502**: Original MOS Technology processor
41//! - **65C02**: CMOS version with bug fixes and new instructions. Several
42//!   historical 65C02 chips are available as named type aliases:
43//!   - [`instruction::Cmos6502`] — WDC W65C02 original (WAI/STP only)
44//!   - [`instruction::W65C02S`] - Modern W65C02S, with low-power and Rockwell extensions.
45//! - **Ricoh 2A03**: Nintendo's NES variant without decimal mode
46//! - **Revision A**: Very early variant missing the ROR instruction
47//!
48//! [variant]: crate::Variant
49
50#![warn(clippy::all, clippy::pedantic)]
51#![warn(
52    absolute_paths_not_starting_with_crate,
53    rustdoc::invalid_html_tags,
54    missing_copy_implementations,
55    missing_debug_implementations,
56    semicolon_in_expressions_from_macros,
57    unreachable_pub,
58    unused_crate_dependencies,
59    unused_extern_crates,
60    variant_size_differences,
61    clippy::missing_const_for_fn
62)]
63#![deny(anonymous_parameters, macro_use_extern_crate)]
64#![allow(clippy::module_name_repetitions, clippy::needless_doctest_main)]
65// Registers and ops follow the 6502 naming convention and have similar names at
66// times
67#![allow(clippy::similar_names)]
68#![allow(clippy::match_same_arms)]
69#![allow(clippy::too_many_lines)]
70#![no_std]
71
72#[doc = include_str!("../README.md")]
73pub mod cpu;
74pub mod instruction;
75pub mod memory;
76pub mod registers;
77
78/// Output of arithmetic instructions (ADC/SBC)
79#[derive(Copy, Clone, Debug, PartialEq, Eq)]
80#[allow(clippy::struct_excessive_bools)]
81pub struct ArithmeticOutput {
82    result: u8,
83    did_carry: bool,
84    overflow: bool,
85    negative: bool,
86    zero: bool,
87}
88
89/// Trait for different 6502 CPU variants with their historical differences.
90///
91/// The 6502 family evolved over decades with various manufacturers creating
92/// specialized versions for different applications:
93///
94/// - **Revision A (1975)**: Very early 6502 variant missing the ROR instruction
95///   entirely. Found in early KIM-1 systems and some Apple-1 computers. The ROR
96///   instruction was intentionally unimplemented due to design constraints, not
97///   a "bug" as commonly believed. Production ended around June 1976.
98///
99/// - **NMOS 6502 (1976)**: Complete MOS Technology processor with working ROR
100///   instruction, used in Apple II, Commodore 64, Atari 2600. Has unreliable
101///   decimal mode flags but full BCD support. This became the standard reference
102///   implementation.
103///
104/// - **65C02 (1983)**: WDC's CMOS version with bug fixes, additional instructions,
105///   and reliable decimal mode flags. Development began in 1981 with samples
106///   released in early 1983. Used in Apple IIc/IIe and many embedded systems.
107///
108/// - **Ricoh 2A03 (1983)**: Nintendo's cost-reduced variant for NES/Famicom
109///   (released July 15, 1983). Removed decimal mode entirely to avoid patent
110///   issues. Used as a core in their custom ASIC which also included sound
111///   generation and other features.
112///
113/// Choose the variant that matches your target system for accurate emulation.
114/// Note that software written for later variants may not run on earlier ones
115/// due to missing instructions (particularly ROR on Revision A).
116pub trait Variant {
117    fn decode(
118        opcode: u8,
119    ) -> Option<(
120        crate::instruction::Instruction,
121        crate::instruction::AddressingMode,
122    )>;
123
124    /// Returns the cycle penalty for ADC/SBC in decimal mode for this variant.
125    ///
126    /// - NMOS/Ricoh: 0 (no penalty)
127    /// - 65C02: 1 (decimal mode adds an extra cycle)
128    /// - Future variants may have different values
129    #[must_use]
130    fn penalty_cycles_for_decimal_mode() -> u8 {
131        0 // Default: no penalty
132    }
133
134    /// Returns the cycle penalty for JMP (indirect) for this variant.
135    ///
136    /// - NMOS/Ricoh: 0 (5 cycles total)
137    /// - 65C02: 1 (6 cycles total, also fixes page-crossing bug)
138    /// - Future HMOS 7501: 0 (5 cycles)
139    #[must_use]
140    fn penalty_cycles_for_indirect_jmp() -> u8 {
141        0 // Default: no penalty
142    }
143
144    /// Execute Add with Carry (ADC) in binary mode
145    ///
146    /// # Arguments
147    /// * `accumulator` - Current accumulator value
148    /// * `value` - Value to add
149    /// * `carry_set` - Carry flag set at the time of execution
150    ///
151    /// # Returns
152    /// Tuple of (result, `carry_out`, overflow, negative, zero)
153    fn adc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput;
154
155    /// Execute Add with Carry (ADC) in decimal mode (BCD)
156    ///
157    /// # Arguments
158    /// * `accumulator` - Current accumulator value
159    /// * `value` - Value to add
160    /// * `carry_set` - Carry flag set at the time of execution
161    ///
162    /// # Returns
163    /// Tuple of (result, `carry_out`, overflow, negative, zero)
164    fn adc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput;
165
166    /// Execute Subtract with Carry (SBC) in binary mode
167    ///
168    /// # Arguments
169    /// * `accumulator` - Current accumulator value
170    /// * `value` - Value to subtract
171    /// * `carry_set` - Carry flag set at the time of execution
172    ///
173    /// # Returns
174    /// Tuple of (result, `carry_out`, overflow, negative, zero)
175    fn sbc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput;
176
177    /// Execute Subtract with Carry (SBC) in decimal mode (BCD)
178    ///
179    /// # Arguments
180    /// * `accumulator` - Current accumulator value
181    /// * `value` - Value to subtract
182    /// * `carry_set` - Carry flag set at the time of execution
183    ///
184    /// # Returns
185    /// Tuple of (result, `carry_out`, overflow, negative, zero)
186    fn sbc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput;
187}