Fixes for 6502 CPUs.
They are not complete yet
This commit is contained in:
parent
2a78581a2d
commit
4d1a87e2af
4 changed files with 437 additions and 212 deletions
|
|
@ -1,106 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
use crate::component::Component;
|
||||
use crate::pin::Pin;
|
||||
use crate::components::cpu::mos_6502::MOS6502;
|
||||
|
||||
pub struct CMOS65C02 {
|
||||
pub(crate) base_6502: MOS6502,
|
||||
// 65C02-specific state
|
||||
stopped: bool,
|
||||
waiting: bool,
|
||||
}
|
||||
impl CMOS65C02 {
|
||||
pub fn new(name: String) -> Self {
|
||||
let base_6502 = MOS6502::new(name);
|
||||
|
||||
Self {
|
||||
base_6502,
|
||||
stopped: false,
|
||||
waiting: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit methods from MOS6502
|
||||
pub fn get_register_a(&self) -> u8 {
|
||||
self.base_6502.get_register_a()
|
||||
}
|
||||
|
||||
pub fn set_register_a(&mut self, value: u8) {
|
||||
self.base_6502.set_register_a(value)
|
||||
}
|
||||
|
||||
// 65C02-specific instructions
|
||||
pub fn bra(&mut self, address: u16) {
|
||||
// Branch always - new in 65C02
|
||||
self.base_6502.registers.pc = address;
|
||||
}
|
||||
|
||||
pub fn phx(&mut self) {
|
||||
// Push X - new in 65C02
|
||||
// Stack push implementation would go here
|
||||
let x = self.base_6502.registers.x;
|
||||
println!("PHX: Pushing X register ({:02X}) to stack", x);
|
||||
}
|
||||
|
||||
pub fn ply(&mut self) {
|
||||
// Pull Y - new in 65C02
|
||||
// Stack pull implementation would go here
|
||||
println!("PLY: Pulling value from stack to Y register");
|
||||
}
|
||||
|
||||
pub fn stp(&mut self) {
|
||||
// Stop instruction - new in 65C02
|
||||
self.stopped = true;
|
||||
println!("STP: Processor stopped");
|
||||
}
|
||||
|
||||
pub fn wai(&mut self) {
|
||||
// Wait for interrupt - new in 65C02
|
||||
self.waiting = true;
|
||||
println!("WAI: Processor waiting for interrupt");
|
||||
}
|
||||
|
||||
// Enhanced addressing modes or modified instructions
|
||||
pub fn jmp_indirect(&mut self, address: u16) {
|
||||
// 65C02 fixes the JMP indirect bug
|
||||
// Implementation would go here
|
||||
println!("JMP (indirect) to {:04X}", address);
|
||||
self.base_6502.registers.pc = address;
|
||||
}
|
||||
|
||||
// Additional 65C02-specific methods
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.stopped
|
||||
}
|
||||
|
||||
pub fn is_waiting(&self) -> bool {
|
||||
self.waiting
|
||||
}
|
||||
}
|
||||
// Implement Component by delegating to the base 6502
|
||||
impl Component for CMOS65C02 {
|
||||
fn name(&self) -> &str {
|
||||
self.base_6502.name()
|
||||
}
|
||||
fn pins(&self) -> &std::collections::HashMap<String, Arc<Pin>> {
|
||||
self.base_6502.pins()
|
||||
}
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
self.base_6502.get_pin(name)
|
||||
}
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
if self.stopped {
|
||||
// Don't execute instructions if stopped
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Delegate to base implementation
|
||||
self.base_6502.update()
|
||||
}
|
||||
fn run(&mut self) {
|
||||
self.base_6502.run()
|
||||
}
|
||||
fn stop(&mut self) {
|
||||
self.base_6502.stop()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,9 @@
|
|||
pub mod cpu_traits;
|
||||
// CPU components module
|
||||
pub mod mos_6502;
|
||||
pub mod wdc_65c02;
|
||||
pub mod intel_4004;
|
||||
mod mos_6502;
|
||||
mod cmos_65c02;
|
||||
|
||||
// Re-export the CPU types
|
||||
pub use mos_6502::MOS6502;
|
||||
pub use wdc_65c02::WDC65C02;
|
||||
pub use intel_4004::Intel4004;
|
||||
|
|
@ -1,141 +1,296 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
// 6502 addressing modes
|
||||
pub enum AddressingMode {
|
||||
Immediate,
|
||||
ZeroPage,
|
||||
ZeroPageX,
|
||||
ZeroPageY,
|
||||
Absolute,
|
||||
AbsoluteX,
|
||||
AbsoluteY,
|
||||
Indirect,
|
||||
IndirectX,
|
||||
IndirectY,
|
||||
Implied,
|
||||
Accumulator,
|
||||
Relative,
|
||||
}
|
||||
// 6502 registers
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Registers {
|
||||
pub a: u8, // Accumulator
|
||||
pub x: u8, // X index
|
||||
pub y: u8, // Y index
|
||||
pub sp: u8, // Stack pointer
|
||||
pub pc: u16, // Program counter
|
||||
pub sr: u8, // Status register
|
||||
}
|
||||
// Bit positions in status register
|
||||
pub const CARRY_FLAG: u8 = 1 << 0;
|
||||
pub const ZERO_FLAG: u8 = 1 << 1;
|
||||
pub const INTERRUPT_DISABLE: u8 = 1 << 2;
|
||||
pub const DECIMAL_MODE: u8 = 1 << 3;
|
||||
pub const BREAK_COMMAND: u8 = 1 << 4;
|
||||
pub const UNUSED_FLAG: u8 = 1 << 5;
|
||||
pub const OVERFLOW_FLAG: u8 = 1 << 6;
|
||||
pub const NEGATIVE_FLAG: u8 = 1 << 7;
|
||||
/// MOS Technology 6502 CPU - 8-bit microprocessor
|
||||
pub struct MOS6502 {
|
||||
base: BaseComponent,
|
||||
pub(crate) registers: Registers,
|
||||
// Registers
|
||||
accumulator: u8,
|
||||
x_register: u8,
|
||||
y_register: u8,
|
||||
stack_pointer: u8,
|
||||
program_counter: u16,
|
||||
status_register: u8,
|
||||
|
||||
// Internal state
|
||||
cycle_count: u64,
|
||||
last_clock_state: PinValue,
|
||||
// Memory interface would be added here
|
||||
is_reset: bool,
|
||||
is_running: bool,
|
||||
}
|
||||
|
||||
impl MOS6502 {
|
||||
pub fn new(name: String) -> Self {
|
||||
let mut base = BaseComponent::new(name);
|
||||
let pin_names = vec![
|
||||
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", // 16 address lines
|
||||
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", // 8 data lines
|
||||
"RW", // Read/Write
|
||||
"IRQ", // Interrupt Request
|
||||
"NMI", // Non-Maskable Interrupt
|
||||
"RES", // Reset
|
||||
"CLK", // Clock
|
||||
"SYNC", // Sync
|
||||
"RDY", // Ready
|
||||
];
|
||||
|
||||
// Add 6502 pins
|
||||
base.add_pin("clk".to_string(), PinValue::Low, false);
|
||||
base.add_pin("reset".to_string(), PinValue::Low, false);
|
||||
base.add_pin("irq".to_string(), PinValue::High, false);
|
||||
base.add_pin("nmi".to_string(), PinValue::High, false);
|
||||
base.add_pin("rw".to_string(), PinValue::High, true); // Read/Write
|
||||
base.add_pin("sync".to_string(), PinValue::Low, true); // Sync
|
||||
// Add address and data pins...
|
||||
let pins = BaseComponent::create_pin_map(&pin_names, &name);
|
||||
|
||||
Self {
|
||||
base,
|
||||
registers: Registers::default(),
|
||||
MOS6502 {
|
||||
base: BaseComponent::new(name, pins),
|
||||
accumulator: 0,
|
||||
x_register: 0,
|
||||
y_register: 0,
|
||||
stack_pointer: 0xFD,
|
||||
program_counter: 0xFFFC, // Reset vector location
|
||||
status_register: 0x20, // Always set bit 5
|
||||
cycle_count: 0,
|
||||
last_clock_state: PinValue::Low,
|
||||
is_reset: false,
|
||||
is_running: false,
|
||||
}
|
||||
}
|
||||
// Common 6502 methods that will be inherited
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.registers.pc = 0xFFFC;
|
||||
self.registers.sp = 0xFD;
|
||||
self.registers.sr = 0x34; // I flag set, others cleared
|
||||
self.cycle_count = 0;
|
||||
self.accumulator = 0;
|
||||
self.x_register = 0;
|
||||
self.y_register = 0;
|
||||
self.stack_pointer = 0xFD;
|
||||
self.program_counter = 0xFFFC;
|
||||
self.status_register = 0x20;
|
||||
self.is_reset = true;
|
||||
|
||||
// Set initial pin states
|
||||
self.set_address_bus(0xFFFC);
|
||||
self.set_data_bus(0xFF);
|
||||
self.set_rw_pin(true); // Start in read mode
|
||||
}
|
||||
pub fn get_register_a(&self) -> u8 {
|
||||
self.registers.a
|
||||
|
||||
fn set_address_bus(&self, address: u16) {
|
||||
for i in 0..16 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
let bit_value = (address >> i) & 1;
|
||||
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low };
|
||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
||||
}
|
||||
pub fn set_register_a(&mut self, value: u8) {
|
||||
self.registers.a = value;
|
||||
self.update_zero_negative_flags(value);
|
||||
}
|
||||
// Common flag handling
|
||||
pub fn update_zero_negative_flags(&mut self, value: u8) {
|
||||
self.set_flag(ZERO_FLAG, value == 0);
|
||||
self.set_flag(NEGATIVE_FLAG, value & 0x80 != 0);
|
||||
}
|
||||
pub fn set_flag(&mut self, flag: u8, condition: bool) {
|
||||
if condition {
|
||||
self.registers.sr |= flag;
|
||||
}
|
||||
|
||||
fn set_data_bus(&self, data: u8) {
|
||||
for i in 0..8 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
let bit_value = (data >> i) & 1;
|
||||
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low };
|
||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rw_pin(&self, read: bool) {
|
||||
if let Ok(pin) = self.base.get_pin("RW") {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
let pin_value = if read { PinValue::High } else { PinValue::Low };
|
||||
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_data_bus(&self) -> u8 {
|
||||
let mut data = 0;
|
||||
|
||||
for i in 0..8 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
if pin_guard.read() == PinValue::High {
|
||||
data |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
pub(crate) fn read_control_pins(&self) -> (bool, bool, bool, bool) {
|
||||
let irq = if let Ok(pin) = self.base.get_pin("IRQ") {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::Low // IRQ is active low
|
||||
} else {
|
||||
self.registers.sr &= !flag;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let nmi = if let Ok(pin) = self.base.get_pin("NMI") {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::Low // NMI is active low
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let reset = if let Ok(pin) = self.base.get_pin("RES") {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::Low // RESET is active low
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let rdy = if let Ok(pin) = self.base.get_pin("RDY") {
|
||||
if let Ok(pin_guard) = pin.lock() {
|
||||
pin_guard.read() == PinValue::High // RDY is active high
|
||||
} else {
|
||||
true // Default to ready if pin doesn't exist
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
(irq, nmi, reset, rdy)
|
||||
}
|
||||
|
||||
pub fn get_flag(&self, flag: u8) -> bool {
|
||||
self.registers.sr & flag != 0
|
||||
}
|
||||
fn execute_instruction(&mut self) {
|
||||
// Simplified instruction execution - just increment PC for compilation
|
||||
self.program_counter = self.program_counter.wrapping_add(1);
|
||||
self.cycle_count += 1;
|
||||
|
||||
// Common instruction implementations
|
||||
pub fn lda(&mut self, value: u8) {
|
||||
self.set_register_a(value);
|
||||
// Minimal implementation to satisfy compilation
|
||||
self.set_address_bus(self.program_counter);
|
||||
self.set_rw_pin(true); // Always reading for now
|
||||
}
|
||||
|
||||
pub fn tax(&mut self) {
|
||||
self.registers.x = self.registers.a;
|
||||
self.update_zero_negative_flags(self.registers.x);
|
||||
}
|
||||
// More common instructions...
|
||||
}
|
||||
|
||||
impl Component for MOS6502 {
|
||||
fn name(&self) -> &str {
|
||||
todo!()
|
||||
fn name(&self) -> String {
|
||||
self.base.name()
|
||||
}
|
||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||
todo!()
|
||||
|
||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
||||
self.base.pins()
|
||||
}
|
||||
fn get_pin(&self, _name: &str) -> Option<Arc<Pin>> {
|
||||
todo!()
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||
self.base.get_pin(name)
|
||||
}
|
||||
// Implement Component trait methods...
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
// Clock handling and instruction execution
|
||||
let clk_pin = self.base.pins.get("clk").unwrap();
|
||||
let clk_value = clk_pin.read();
|
||||
// Detect rising edge
|
||||
if clk_value == PinValue::High && self.last_clock_state == PinValue::Low {
|
||||
self.cycle_count += 1;
|
||||
// Instruction execution would happen here
|
||||
|
||||
fn update(&mut self) {
|
||||
if !self.is_running {
|
||||
return;
|
||||
}
|
||||
self.last_clock_state = clk_value;
|
||||
Ok(())
|
||||
|
||||
let (irq, nmi, reset, rdy) = self.read_control_pins();
|
||||
|
||||
if reset && !self.is_reset {
|
||||
self.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
self.is_reset = reset;
|
||||
|
||||
if rdy {
|
||||
self.execute_instruction();
|
||||
}
|
||||
// If RDY is low, the CPU waits
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
todo!()
|
||||
self.is_running = true;
|
||||
self.reset();
|
||||
|
||||
while self.is_running {
|
||||
self.update();
|
||||
thread::sleep(Duration::from_micros(1));
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
todo!()
|
||||
self.is_running = false;
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.is_running
|
||||
}
|
||||
}
|
||||
|
||||
// 6502-specific methods
|
||||
impl MOS6502 {
|
||||
pub fn get_program_counter(&self) -> u16 {
|
||||
self.program_counter
|
||||
}
|
||||
|
||||
pub fn set_program_counter(&mut self, address: u16) {
|
||||
self.program_counter = address;
|
||||
}
|
||||
|
||||
pub fn get_accumulator(&self) -> u8 {
|
||||
self.accumulator
|
||||
}
|
||||
|
||||
pub fn get_x_register(&self) -> u8 {
|
||||
self.x_register
|
||||
}
|
||||
|
||||
pub fn get_y_register(&self) -> u8 {
|
||||
self.y_register
|
||||
}
|
||||
|
||||
pub fn get_stack_pointer(&self) -> u8 {
|
||||
self.stack_pointer
|
||||
}
|
||||
|
||||
pub fn get_status_register(&self) -> u8 {
|
||||
self.status_register
|
||||
}
|
||||
|
||||
pub fn get_cycle_count(&self) -> u64 {
|
||||
self.cycle_count
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_6502_creation() {
|
||||
let cpu = MOS6502::new("CPU_6502".to_string());
|
||||
assert_eq!(cpu.name(), "CPU_6502");
|
||||
assert!(!cpu.is_running());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_6502_registers() {
|
||||
let mut cpu = MOS6502::new("CPU_6502".to_string());
|
||||
|
||||
cpu.set_program_counter(0x1234);
|
||||
assert_eq!(cpu.get_program_counter(), 0x1234);
|
||||
|
||||
assert_eq!(cpu.get_accumulator(), 0);
|
||||
assert_eq!(cpu.get_x_register(), 0);
|
||||
assert_eq!(cpu.get_y_register(), 0);
|
||||
assert_eq!(cpu.get_stack_pointer(), 0xFD);
|
||||
assert_eq!(cpu.get_status_register(), 0x20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_6502_reset() {
|
||||
let mut cpu = MOS6502::new("CPU_6502".to_string());
|
||||
|
||||
cpu.set_program_counter(0x1000);
|
||||
cpu.reset();
|
||||
|
||||
assert_eq!(cpu.get_program_counter(), 0xFFFC);
|
||||
}
|
||||
// Other trait methods...
|
||||
}
|
||||
171
src/components/cpu/wdc_65c02.rs
Normal file
171
src/components/cpu/wdc_65c02.rs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::component::Component;
|
||||
use crate::components::cpu::MOS6502;
|
||||
use crate::pin::Pin;
|
||||
|
||||
/// WDC 65C02 - CMOS version of 6502 with additional instructions
|
||||
pub struct WDC65C02 {
|
||||
base: MOS6502,
|
||||
// 65C02-specific state
|
||||
stop_mode: bool,
|
||||
wait_mode: bool,
|
||||
}
|
||||
|
||||
impl WDC65C02 {
|
||||
pub fn new(name: String) -> Self {
|
||||
WDC65C02 {
|
||||
base: MOS6502::new(name),
|
||||
stop_mode: false,
|
||||
wait_mode: false,
|
||||
}
|
||||
}
|
||||
|
||||
// 65C02 specific methods
|
||||
pub fn enter_stop_mode(&mut self) {
|
||||
self.stop_mode = true;
|
||||
self.wait_mode = false;
|
||||
}
|
||||
|
||||
pub fn enter_wait_mode(&mut self) {
|
||||
self.wait_mode = true;
|
||||
self.stop_mode = false;
|
||||
}
|
||||
|
||||
pub fn exit_low_power_modes(&mut self) {
|
||||
self.stop_mode = false;
|
||||
self.wait_mode = false;
|
||||
}
|
||||
|
||||
pub fn is_in_stop_mode(&self) -> bool {
|
||||
self.stop_mode
|
||||
}
|
||||
|
||||
pub fn is_in_wait_mode(&self) -> bool {
|
||||
self.wait_mode
|
||||
}
|
||||
|
||||
// 65C02 additional instructions would be implemented here
|
||||
fn execute_65c02_instruction(&mut self) {
|
||||
if self.stop_mode {
|
||||
// CPU is stopped - no operation
|
||||
return;
|
||||
}
|
||||
|
||||
if self.wait_mode {
|
||||
// CPU is waiting for interrupt - no operation
|
||||
return;
|
||||
}
|
||||
|
||||
// For compilation, just call the base 6502 implementation
|
||||
// In a full implementation, this would handle 65C02-specific instructions
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for WDC65C02 {
|
||||
fn name(&self) -> String {
|
||||
self.base.name()
|
||||
}
|
||||
|
||||
fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
|
||||
self.base.pins()
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
|
||||
self.base.get_pin(name)
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
if self.stop_mode || self.wait_mode {
|
||||
// In low-power modes, only check for interrupts
|
||||
let (irq, nmi, reset, _) = self.base.read_control_pins();
|
||||
|
||||
if nmi || reset || (irq && !self.base.get_status_register() & 0x04 == 0) {
|
||||
// Exit low-power modes on interrupt or reset
|
||||
self.exit_low_power_modes();
|
||||
} else {
|
||||
return; // Stay in low-power mode
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate to base 6502 implementation
|
||||
self.base.update();
|
||||
|
||||
// 65C02-specific processing would go here
|
||||
self.execute_65c02_instruction();
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.base.run();
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
self.base.stop();
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
}
|
||||
|
||||
// 65C02-specific enhancements
|
||||
impl WDC65C02 {
|
||||
pub fn get_base_cpu(&self) -> &MOS6502 {
|
||||
&self.base
|
||||
}
|
||||
|
||||
pub fn get_base_cpu_mut(&mut self) -> &mut MOS6502 {
|
||||
&mut self.base
|
||||
}
|
||||
|
||||
// Additional 65C02 functionality can be added here
|
||||
// - New addressing modes
|
||||
// - Additional instructions (STZ, BRA, etc.)
|
||||
// - Improved interrupt handling
|
||||
// - Low-power mode control
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_65c02_creation() {
|
||||
let cpu = WDC65C02::new("CPU_65C02".to_string());
|
||||
assert_eq!(cpu.name(), "CPU_65C02");
|
||||
assert!(!cpu.is_running());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_65c02_low_power_modes() {
|
||||
let mut cpu = WDC65C02::new("CPU_65C02".to_string());
|
||||
|
||||
assert!(!cpu.is_in_stop_mode());
|
||||
assert!(!cpu.is_in_wait_mode());
|
||||
|
||||
cpu.enter_stop_mode();
|
||||
assert!(cpu.is_in_stop_mode());
|
||||
assert!(!cpu.is_in_wait_mode());
|
||||
|
||||
cpu.enter_wait_mode();
|
||||
assert!(!cpu.is_in_stop_mode());
|
||||
assert!(cpu.is_in_wait_mode());
|
||||
|
||||
cpu.exit_low_power_modes();
|
||||
assert!(!cpu.is_in_stop_mode());
|
||||
assert!(!cpu.is_in_wait_mode());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_65c02_inheritance() {
|
||||
let mut cpu = WDC65C02::new("CPU_65C02".to_string());
|
||||
|
||||
// Verify that 65C02 inherits 6502 functionality
|
||||
cpu.get_base_cpu_mut().set_program_counter(0x1000);
|
||||
assert_eq!(cpu.get_base_cpu().get_program_counter(), 0x1000);
|
||||
|
||||
assert_eq!(cpu.get_base_cpu().get_accumulator(), 0);
|
||||
assert_eq!(cpu.get_base_cpu().get_x_register(), 0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue