Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for NMOS quirks and CMOS extensions #38

Merged
merged 5 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
Binary file added bin/klaus_65C02.bin
Binary file not shown.
288 changes: 260 additions & 28 deletions src/cpu/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::cpu::fetch::Fetch;
use crate::cpu::registers::{flags, Alu};
use crate::cpu::{InterruptHandler, MemoryIO, Mos6502, Stack};

use super::Mos6502Variant;

pub trait Execute {
/// Execute the given opcode, returning either the number of cycles used or an error.
fn execute(&mut self, opcode: u8) -> Result<u8, ()>;
Expand All @@ -11,7 +13,7 @@ impl Execute for Mos6502 {
fn execute(&mut self, opcode: u8) -> Result<u8, ()> {
match opcode {
// === LOAD ===
0xA1 | 0xA5 | 0xA9 | 0xAD | 0xB1 | 0xB5 | 0xB9 | 0xBD => {
0xA1 | 0xA5 | 0xA9 | 0xAD | 0xB1 | 0xB2 | 0xB5 | 0xB9 | 0xBD => {
// LDA
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.a = value;
Expand All @@ -36,7 +38,7 @@ impl Execute for Mos6502 {
}

// === STORE ===
0x81 | 0x85 | 0x8D | 0x91 | 0x95 | 0x99 | 0x9D => {
0x81 | 0x85 | 0x8D | 0x91 | 0x92 | 0x95 | 0x99 | 0x9D => {
// STA
let (address, cycles) = self.fetch_operand_address(opcode);
self.write(address, self.registers.a);
Expand Down Expand Up @@ -133,6 +135,10 @@ impl Execute for Mos6502 {
// ASL
let (address, cycles) = self.fetch_operand_address(opcode);
let value = self.read(address);

if let Mos6502Variant::NMOS = self.variant {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to make this a march statement instead? in case there are other variants we wanna add in the future 👀 :3

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe thats premature optimization? 🍃

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait thoughts on matches!(thing, pattern) instead of the if let? probably just a stylistic thing, especially since you dont need to match on any parameters if that makes sense

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasn't aware of matches!() but seems like exactly what i want! i alternated between if let and == in this and wasn't rly happy with it

im a bit against a full match statement bc that would kind of imply that quirks are likely to have varying behaviors for every version, where these quirks are more just special cases for one version at a time -- e.g. if NMOS has a quirk and it was fixed in CMOS, it's unlikely to show again up in e.g. the 65816 or something later

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. you'd never really have a situation like this

match version {
  NMOS => do one thing
  CMOS => do another thing
  65816 => do a secret third thing
}

its more like, the little if statements sprinkled throughout account for each chip's separate set of errata, so something special-cased in for an NMOS quirk but skipped on CMOS wouldn't really be applicable if we added in a third generation

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeep, my 🍃 ass only realized that after i left the comment, makes total sense :3

self.write(address, value);
}
let result = value << 1;

self.registers.sr.write(flags::CARRY, value & 0x80 != 0);
Expand All @@ -154,6 +160,11 @@ impl Execute for Mos6502 {
// LSR
let (address, cycles) = self.fetch_operand_address(opcode);
let value = self.read(address);

if let Mos6502Variant::NMOS = self.variant {
self.write(address, value);
}

let result = value >> 1;

self.registers.sr.write(flags::CARRY, value & 0x01 != 0);
Expand All @@ -176,6 +187,11 @@ impl Execute for Mos6502 {
// ROL
let (address, cycles) = self.fetch_operand_address(opcode);
let value = self.read(address);

if let Mos6502Variant::NMOS = self.variant {
self.write(address, value);
}

let result = (value << 1) | (self.registers.sr.read(flags::CARRY) as u8);

self.registers.sr.write(flags::CARRY, value & 0x80 != 0);
Expand All @@ -198,6 +214,11 @@ impl Execute for Mos6502 {
// ROR
let (address, cycles) = self.fetch_operand_address(opcode);
let value = self.read(address);

if let Mos6502Variant::NMOS = self.variant {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nmos just got a lil quirky with it 🤪

self.write(address, value);
}

let result = value >> 1 | (self.registers.sr.read(flags::CARRY) as u8) << 7;

self.registers.sr.write(flags::CARRY, value & 0x01 != 0);
Expand All @@ -207,7 +228,7 @@ impl Execute for Mos6502 {
}

// === LOGIC ===
0x21 | 0x25 | 0x29 | 0x2D | 0x31 | 0x35 | 0x39 | 0x3D => {
0x21 | 0x25 | 0x29 | 0x2D | 0x31 | 0x32 | 0x35 | 0x39 | 0x3D => {
// AND
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.a &= value;
Expand All @@ -227,15 +248,15 @@ impl Execute for Mos6502 {
Ok(cycles)
}

0x41 | 0x45 | 0x49 | 0x4D | 0x51 | 0x55 | 0x59 | 0x5D => {
0x41 | 0x45 | 0x49 | 0x4D | 0x51 | 0x52 | 0x55 | 0x59 | 0x5D => {
// EOR
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.a ^= value;
self.registers.sr.set_nz(self.registers.a);
Ok(cycles)
}

0x01 | 0x05 | 0x09 | 0x0D | 0x11 | 0x15 | 0x19 | 0x1D => {
0x01 | 0x05 | 0x09 | 0x0D | 0x11 | 0x12 | 0x15 | 0x19 | 0x1D => {
// ORA
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.a |= value;
Expand All @@ -244,14 +265,14 @@ impl Execute for Mos6502 {
}

// === ARITHMETIC ===
0x61 | 0x65 | 0x69 | 0x6D | 0x71 | 0x75 | 0x79 | 0x7D => {
0x61 | 0x65 | 0x69 | 0x6D | 0x71 | 0x72 | 0x75 | 0x79 | 0x7D => {
// ADC
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.alu_add(value);
Ok(cycles)
}

0xC1 | 0xC5 | 0xC9 | 0xCD | 0xD1 | 0xD5 | 0xD9 | 0xDD => {
0xC1 | 0xC5 | 0xC9 | 0xCD | 0xD1 | 0xD2 | 0xD5 | 0xD9 | 0xDD => {
// CMP
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.alu_compare(self.registers.a, value);
Expand All @@ -272,7 +293,7 @@ impl Execute for Mos6502 {
Ok(cycles)
}

0xE1 | 0xE5 | 0xE9 | 0xED | 0xF1 | 0xF5 | 0xF9 | 0xFD => {
0xE1 | 0xE5 | 0xE9 | 0xED | 0xF1 | 0xF2 | 0xF5 | 0xF9 | 0xFD => {
// SBC
let (value, cycles) = self.fetch_operand_value(opcode);
self.registers.alu_subtract(value);
Expand Down Expand Up @@ -341,7 +362,15 @@ impl Execute for Mos6502 {
0x4C => (self.fetch_word(), 3),
0x6C => {
let indirect = self.fetch_word();
(self.read_word(indirect), 5)

if self.variant == Mos6502Variant::NMOS && indirect & 0xFF == 0xFF {
let lo = self.read(indirect);
let hi = self.read(indirect & 0xFF00);
((hi as u16) << 8 | lo as u16, 5)
} else {
// normal behavior
(self.read_word(indirect), 5)
}
}
_ => unreachable!(),
};
Expand Down Expand Up @@ -426,26 +455,19 @@ impl Execute for Mos6502 {
Ok(2)
}

// === ILLEGAL OPCODES ===
// TODO: Verify cycle counts
0x04 | 0x0C | 0x14 | 0x1A | 0x1C | 0x34 | 0x3A | 0x3C | 0x44 | 0x54 | 0x5A | 0x5C | 0x64
| 0x74 | 0x7A | 0x7C | 0x80 | 0x82 | 0x89 | 0xC2 | 0xD4 | 0xDA | 0xDC | 0xE2 | 0xF4
| 0xFA | 0xFC => {
// NOP
match opcode {
0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xFA => {
// No address
Ok(2)
}
_ => {
// Address
let (_value, cycles) = self.fetch_operand_value(opcode);
Ok(cycles)
}
}
}
_ => match self.variant {
Mos6502Variant::NMOS => self.execute_nmos_extensions(opcode),
Mos6502Variant::CMOS => self.execute_cmos_extensions(opcode),
},
}
}
}

0x02 | 0x12 | 0x22 | 0x32 | 0x42 | 0x52 | 0x62 | 0x72 | 0x92 | 0xB2 | 0xD2 | 0xF2 => {
impl Mos6502 {
fn execute_nmos_extensions(&mut self, opcode: u8) -> Result<u8, ()> {
match opcode {
// === ILLEGAL OPCODES ===
0x02 | 0x22 | 0x42 | 0x62 => {
// STP or KIL or JAM or HLT depending on who you ask
println!("Execution stopped");
Err(())
Expand Down Expand Up @@ -712,6 +734,216 @@ impl Execute for Mos6502 {

Ok(cycles)
}

// TODO: Verify cycle counts
_ => {
// NOP
match opcode {
0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xFA => {
// No address
Ok(2)
}
_ => {
// Address
let (_value, cycles) = self.fetch_operand_value(opcode);
Ok(cycles)
}
}
}
}
}

fn execute_cmos_extensions(&mut self, opcode: u8) -> Result<u8, ()> {
match opcode {
0x89 | 0x34 | 0x3C => {
// BIT (3 extra addressing modes)
let (value, cycles) = self.fetch_operand_value(opcode);

if opcode != 0x89 {
// N, V flags not set for immediate
self.registers.sr.write(flags::NEGATIVE, value & 0x80 != 0);
self.registers.sr.write(flags::OVERFLOW, value & 0x40 != 0);
}

self
.registers
.sr
.write(flags::ZERO, value & self.registers.a == 0);
Ok(cycles)
}

0x3A => {
// DEC (like DEX/DEY but for accumulator)
self.registers.a = self.registers.a.wrapping_sub(1);
self.registers.sr.set_nz(self.registers.a);
Ok(2)
}

0x1A => {
// INC (like INX/INY but for accumulator)
self.registers.a = self.registers.a.wrapping_add(1);
self.registers.sr.set_nz(self.registers.a);
Ok(2)
}

0x7C => {
// JMP (abs,X)
let address = self.fetch_word();
let pointer = address + self.registers.x as u16;
let address = self.read_word(pointer);
self.registers.pc.load(address);
Ok(6)
}

0x80 => {
// BRA (branch Always)
let offset = self.fetch() as i8;
self.registers.pc.offset(offset);
Ok(3)
}

// New Stack Instructions
0xDA => {
// PHX (push X onto stack)
self.push(self.registers.x);
Ok(3)
}
0x5A => {
// PHY (push Y onto stack)
self.push(self.registers.y);
Ok(3)
}
0xFA => {
// PLX (pull X from stack)
let value = self.pop();
self.registers.x = value;
self.registers.sr.set_nz(value);
Ok(4)
}
0x7A => {
// PLY (pull Y from stack)
let value = self.pop();
self.registers.y = value;
self.registers.sr.set_nz(value);
Ok(4)
}

0x64 | 0x74 | 0x9C | 0x9E => {
// STZ (store zero)
// Note: 0x9C breaks the typical addressing mode pattern
let (address, cycles) = match opcode {
0x9C => (self.fetch_word(), 4),
0x9E => {
let base = self.fetch_word();
let indexed = base + self.registers.x as u16;
(indexed, 4)
}
_ => self.fetch_operand_address(opcode),
};

self.write(address, 0);
Ok(cycles)
}

0x14 | 0x1C => {
// TRB (test and reset bits)
let (address, cycles) = match opcode {
0x14 => (self.fetch() as u16, 3),
0x1C => (self.fetch_word(), 4),
_ => unreachable!(),
};
let value = self.read(address);

self
.registers
.sr
.write(flags::ZERO, value & self.registers.a == 0);

self.write(address, value & !self.registers.a);
Ok(cycles)
}

0x04 | 0x0C => {
// TSB (test and set bits)
let (address, cycles) = match opcode {
0x04 => (self.fetch() as u16, 3),
0x0C => (self.fetch_word(), 4),
_ => unreachable!(),
};
let value = self.read(address);

self
.registers
.sr
.write(flags::ZERO, value & self.registers.a == 0);

self.write(address, value | self.registers.a);
Ok(cycles)
}

0x0F | 0x1F | 0x2F | 0x3F | 0x4F | 0x5F | 0x6F | 0x7F | 0x8F | 0x9F | 0xAF | 0xBF | 0xCF
| 0xDF | 0xEF | 0xFF => {
// BBS and BBR
let address = self.fetch() as u16;
let value = self.read(address);
let offset = self.fetch() as i8;

let bit = (opcode >> 4) & 0b111;
let bit_value = ((1 << bit) & value) != 0;
let target_value = opcode & 0x80 != 0;

if target_value == bit_value {
self.registers.pc.offset(offset);
Ok(3)
} else {
Ok(2)
}
}

0x07 | 0x17 | 0x27 | 0x37 | 0x47 | 0x57 | 0x67 | 0x77 | 0x87 | 0x97 | 0xA7 | 0xB7 | 0xC7
| 0xD7 | 0xE7 | 0xF7 => {
// RMB and SMB
let address = self.fetch() as u16;
let value = self.read(address);

let bit = (opcode >> 4) & 0b111;

let value = if opcode & 0x80 == 0 {
value & !(1 << bit)
} else {
value | (1 << bit)
};
self.write(address, value);

Ok(2)
}

0x02 | 0x22 | 0x42 | 0x62 | 0x82 | 0xA2 | 0xC2 | 0xE2 => {
// NOP (2-byte)
self.fetch();
Ok(2)
}
0x44 => {
self.fetch();
Ok(3)
}
0x54 | 0xD4 | 0xF4 => {
self.fetch();
Ok(4)
}
0x5C => {
self.fetch_word();
Ok(8)
}
0xDC | 0xFC => {
self.fetch_word();
Ok(4)
}

_ => {
// NOP
Ok(1)
}
}
}
}
Loading
Loading