Files
nesemu/src/native_core/cpu/helpers.rs
se.cherkasov bdf23de8db
Some checks failed
CI / rust (push) Has been cancelled
Initial commit: NES emulator with GTK4 desktop frontend
Full NES emulation: CPU, PPU, APU, 47 mappers, iNES/NES 2.0 parsing.
GTK4 desktop client with HeaderBar, pixel-perfect Cairo rendering,
drag-and-drop ROM loading, and keyboard shortcuts.
187 tests covering core emulation, mappers, and runtime.
2026-03-13 11:48:45 +03:00

246 lines
7.4 KiB
Rust

use super::*;
impl Cpu6502 {
pub(super) fn fetch<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let value = bus.read(self.pc);
self.pc = self.pc.wrapping_add(1);
value
}
pub(super) fn fetch_u16<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
let lo = self.fetch(bus) as u16;
let hi = self.fetch(bus) as u16;
(hi << 8) | lo
}
pub(super) fn read_u16<B: CpuBus>(&mut self, bus: &mut B, addr: u16) -> u16 {
let lo = bus.read(addr) as u16;
let hi = bus.read(addr.wrapping_add(1)) as u16;
(hi << 8) | lo
}
pub(super) fn read_u16_bug<B: CpuBus>(&mut self, bus: &mut B, addr: u16) -> u16 {
let lo = bus.read(addr) as u16;
let hi_addr = (addr & 0xFF00) | (addr.wrapping_add(1) & 0x00FF);
let hi = bus.read(hi_addr) as u16;
(hi << 8) | lo
}
pub(super) fn addr_zp<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.fetch(bus) as u16
}
pub(super) fn addr_zpx<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.fetch(bus).wrapping_add(self.x) as u16
}
pub(super) fn addr_zpy<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.fetch(bus).wrapping_add(self.y) as u16
}
pub(super) fn addr_abs<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.fetch_u16(bus)
}
pub(super) fn addr_abx<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.addr_abx_cross(bus).0
}
pub(super) fn addr_aby<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.addr_aby_cross(bus).0
}
pub(super) fn addr_indx<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
let ptr = self.fetch(bus).wrapping_add(self.x);
let lo = bus.read(ptr as u16) as u16;
let hi = bus.read(ptr.wrapping_add(1) as u16) as u16;
(hi << 8) | lo
}
pub(super) fn addr_indy<B: CpuBus>(&mut self, bus: &mut B) -> u16 {
self.addr_indy_cross(bus).0
}
pub(super) fn addr_abx_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u16, bool) {
let base = self.fetch_u16(bus);
let addr = base.wrapping_add(self.x as u16);
(addr, (base & 0xFF00) != (addr & 0xFF00))
}
pub(super) fn addr_aby_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u16, bool) {
let base = self.fetch_u16(bus);
let addr = base.wrapping_add(self.y as u16);
(addr, (base & 0xFF00) != (addr & 0xFF00))
}
pub(super) fn addr_indy_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u16, bool) {
let ptr = self.fetch(bus);
let lo = bus.read(ptr as u16) as u16;
let hi = bus.read(ptr.wrapping_add(1) as u16) as u16;
let base = (hi << 8) | lo;
let addr = base.wrapping_add(self.y as u16);
(addr, (base & 0xFF00) != (addr & 0xFF00))
}
pub(super) fn read_zp<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let addr = self.addr_zp(bus);
bus.read(addr)
}
pub(super) fn read_zpx<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let addr = self.addr_zpx(bus);
bus.read(addr)
}
pub(super) fn read_zpy<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let addr = self.addr_zpy(bus);
bus.read(addr)
}
pub(super) fn read_abs<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let addr = self.addr_abs(bus);
bus.read(addr)
}
pub(super) fn read_abx_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u8, bool) {
let (addr, crossed) = self.addr_abx_cross(bus);
(bus.read(addr), crossed)
}
pub(super) fn read_aby_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u8, bool) {
let (addr, crossed) = self.addr_aby_cross(bus);
(bus.read(addr), crossed)
}
pub(super) fn read_indx<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
let addr = self.addr_indx(bus);
bus.read(addr)
}
pub(super) fn read_indy_cross<B: CpuBus>(&mut self, bus: &mut B) -> (u8, bool) {
let (addr, crossed) = self.addr_indy_cross(bus);
(bus.read(addr), crossed)
}
pub(super) fn push<B: CpuBus>(&mut self, bus: &mut B, value: u8) {
let addr = 0x0100 | self.sp as u16;
bus.write(addr, value);
self.sp = self.sp.wrapping_sub(1);
}
pub(super) fn pop<B: CpuBus>(&mut self, bus: &mut B) -> u8 {
self.sp = self.sp.wrapping_add(1);
let addr = 0x0100 | self.sp as u16;
bus.read(addr)
}
pub(super) fn set_zn(&mut self, value: u8) {
if value == 0 {
self.p |= FLAG_ZERO;
} else {
self.p &= !FLAG_ZERO;
}
if (value & 0x80) != 0 {
self.p |= FLAG_NEGATIVE;
} else {
self.p &= !FLAG_NEGATIVE;
}
}
pub(super) fn set_flag(&mut self, mask: u8, enabled: bool) {
if enabled {
self.p |= mask;
} else {
self.p &= !mask;
}
}
pub(super) fn compare(&mut self, lhs: u8, rhs: u8) {
let result = lhs.wrapping_sub(rhs);
self.set_flag(FLAG_CARRY, lhs >= rhs);
self.set_zn(result);
}
pub(super) fn bit(&mut self, value: u8) {
self.set_flag(FLAG_ZERO, (self.a & value) == 0);
self.set_flag(FLAG_OVERFLOW, (value & 0x40) != 0);
self.set_flag(FLAG_NEGATIVE, (value & 0x80) != 0);
}
pub(super) fn adc(&mut self, value: u8) {
let carry = u16::from((self.p & FLAG_CARRY) != 0);
let a = self.a as u16;
let b = value as u16;
let sum = a + b + carry;
let result = sum as u8;
self.set_flag(FLAG_CARRY, sum > 0xFF);
let overflow = ((self.a ^ result) & (value ^ result) & 0x80) != 0;
self.set_flag(FLAG_OVERFLOW, overflow);
self.a = result;
self.set_zn(self.a);
}
pub(super) fn asl(&mut self, value: u8) -> u8 {
self.set_flag(FLAG_CARRY, (value & 0x80) != 0);
let out = value << 1;
self.set_zn(out);
out
}
pub(super) fn lsr(&mut self, value: u8) -> u8 {
self.set_flag(FLAG_CARRY, (value & 0x01) != 0);
let out = value >> 1;
self.set_zn(out);
out
}
pub(super) fn rol(&mut self, value: u8) -> u8 {
let carry_in = u8::from((self.p & FLAG_CARRY) != 0);
self.set_flag(FLAG_CARRY, (value & 0x80) != 0);
let out = (value << 1) | carry_in;
self.set_zn(out);
out
}
pub(super) fn ror(&mut self, value: u8) -> u8 {
let carry_in = if (self.p & FLAG_CARRY) != 0 { 0x80 } else { 0 };
self.set_flag(FLAG_CARRY, (value & 0x01) != 0);
let out = (value >> 1) | carry_in;
self.set_zn(out);
out
}
pub(super) fn branch<B: CpuBus>(&mut self, bus: &mut B, condition: bool) -> u8 {
let offset = self.fetch(bus) as i8;
if !condition {
return 2;
}
let old_pc = self.pc;
self.pc = self.pc.wrapping_add_signed(offset as i16);
if (old_pc & 0xFF00) != (self.pc & 0xFF00) {
4
} else {
3
}
}
pub(super) fn service_interrupt<B: CpuBus>(
&mut self,
bus: &mut B,
vector_addr: u16,
break_flag: bool,
) {
self.push(bus, (self.pc >> 8) as u8);
self.push(bus, self.pc as u8);
let mut status = (self.p | FLAG_UNUSED) & !FLAG_BREAK;
if break_flag {
status |= FLAG_BREAK;
}
self.push(bus, status);
self.p = (self.p | FLAG_IRQ_DISABLE | FLAG_UNUSED) & !FLAG_BREAK;
self.pc = self.read_u16(bus, vector_addr);
}
}