77697dbb99
This pull request addresses several issues with the Add with Carry (ADC) operation in our 6502 emulator, ensuring correct behavior in both decimal and non-decimal modes. It is similar to #106, which improved on our SBC operation. My hope is that these changes improve the accuracy of our 6502 emulator, particularly for software that relies on correct decimal mode arithmetic or precise overflow detection. - Implement correct decimal mode addition in ADC operation - Accurate overflow detection for non-decimal mode - Proper carry flag calculation for both modes - Preserve overflow flag in decimal mode (as per 6502 specification) The previous implementation didn't correctly handle Binary-Coded Decimal (BCD) arithmetic. The new implementation: 1. Performs addition on low and high nibbles separately 2. Adjusts results when they exceed 9 (valid BCD range is 0-9) 3. Propagates carries between nibbles 4. Sets the carry flag correctly based on the final BCD result - In non-decimal mode: Implemented using the formula `(!(A ^ M) & (A ^ R)) & 0x80 != 0`, where A is the accumulator, M is the memory operand, and R is the result. - In decimal mode: The overflow flag is preserved, matching the behavior of the actual 6502 processor. - In non-decimal mode: Set if the unsigned result is less than either the accumulator or the memory operand. - In decimal mode: Set based on whether the BCD addition resulted in a value greater than 99. |
||
---|---|---|
.github | ||
assets | ||
examples | ||
notes | ||
src | ||
.gitattributes | ||
.gitignore | ||
AUTHORS.txt | ||
Cargo.toml | ||
COPYRIGHT | ||
LICENSE | ||
README.md |
mos6502
An emulator for the MOS 6502 CPU written in Rust.
Tested and validated by solid65.
It builds on stable Rust and supports #[no_std]
targets.
What is the MOS 6502?
The MOS Technology 6502 (typically pronounced "sixty-five-oh-two" or "six-five-oh-two") is an 8-bit microprocessor that was designed by a small team led by Chuck Peddle for MOS Technology. [...]
When it was introduced in 1975, the 6502 was the least expensive microprocessor on the market by a considerable margin. It initially sold for less than one-sixth the cost of competing designs from larger companies, such as the 6800 or Intel 8080. Its introduction caused rapid decreases in pricing across the entire processor market. Along with the Zilog Z80, it sparked a series of projects that resulted in the home computer revolution of the early 1980s.
Source: Wikipedia
How to use this library
use mos6502::memory::Bus;
use mos6502::memory::Memory;
use mos6502::instruction::Nmos6502;
use mos6502::cpu;
fn main() {
// Calculate the greatest common divisor of 56 and 49
// using Euclid's algorithm.
let zero_page_data = [56, 49];
let program = [
// (F)irst | (S)econd
// .algo
0xa5, 0x00, // Load from F to A
// .algo_
0x38, // Set carry flag
0xe5, 0x01, // Substract S from number in A (from F)
0xf0, 0x07, // Jump to .end if diff is zero
0x30, 0x08, // Jump to .swap if diff is negative
0x85, 0x00, // Load A to F
0x4c, 0x12, 0x00, // Jump to .algo_
// .end
0xa5, 0x00, // Load from S to A
0xff,
// .swap
0xa6, 0x00, // load F to X
0xa4, 0x01, // load S to Y
0x86, 0x01, // Store X to F
0x84, 0x00, // Store Y to S
0x4c, 0x10, 0x00, // Jump to .algo
];
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
cpu.memory.set_bytes(0x00, &zero_page_data);
cpu.memory.set_bytes(0x10, &program);
cpu.registers.program_counter = 0x10;
cpu.run();
// The expected GCD is 7
assert_eq!(7, cpu.registers.accumulator);
}
The same can be achieved, by compiling the euclid example yourself.
First install a 6502 assembler and linker, e.g. cc65.
brew install cc65
Then compile and link the assembly file:
cd examples/asm/euclid
ca65 euclid.a65
ld65 -C ../linker.cfg -o euclid.bin euclid.o
This will create a binary file euclid.bin
that you can load into the emulator:
use mos6502::memory::Bus;
use mos6502::memory::Memory;
use mos6502::instruction::Nmos6502;
use mos6502::cpu;
use std::fs::read;
fn main() {
// Calculate the greatest common divisor of 56 and 49
// using Euclid's algorithm.
let zero_page_data = [56, 49];
// Load the binary file from disk
let program = match read("examples/asm/euclid/euclid.bin") {
Ok(data) => data,
Err(err) => {
println!("Error reading euclid.bin: {}", err);
return;
}
};
let mut cpu = cpu::CPU::new(Memory::new(), Nmos6502);
cpu.memory.set_bytes(0x00, &zero_page_data);
cpu.memory.set_bytes(0x10, &program);
cpu.registers.program_counter = 0x10;
cpu.run();
// The expected GCD is 7
assert_eq!(7, cpu.registers.accumulator);
}
Credits
This started off as a fork of amw-zero/6502-rs, which seems to be unmaintained at this point.