799 lines
30 KiB
Rust
799 lines
30 KiB
Rust
//! # JSON Configuration System
|
|
//!
|
|
//! This module provides a flexible, JSON-based system for configuring and creating MCS-4 systems.
|
|
//!
|
|
//! ## Key Features
|
|
//!
|
|
//! - **JSON System Definition**: Complete system configurations stored in JSON files
|
|
//! - **Component Factory**: Dynamic creation of components from configuration
|
|
//! - **Automatic Wiring**: Pin-level connection management between components
|
|
//! - **Extensible Design**: Easy addition of new component types and connection patterns
|
|
//! - **Runtime Configuration**: Support for multiple system configurations
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! ```rust
|
|
//! use rusty_emu::system_config::SystemFactory;
|
|
//!
|
|
//! // Create system from JSON configuration
|
|
//! let factory = SystemFactory::new();
|
|
//! let system = factory.create_from_json("configs/mcs4_basic.json").expect("Could not create system!");
|
|
//!
|
|
//! // Get system information
|
|
//! let info = system.get_system_info();
|
|
//! println!("Created system: {} with {} components", info.name, info.component_count);
|
|
//! ```
|
|
//!
|
|
//! ## Configuration File Format
|
|
//!
|
|
//! ```json
|
|
//! {
|
|
//! "name": "MySystem",
|
|
//! "description": "Custom MCS-4 system",
|
|
//! "components": {
|
|
//! "CPU": {
|
|
//! "single": {
|
|
//! "component_type": "intel_4004",
|
|
//! "name": "CPU_4004",
|
|
//! "properties": {"clock_speed": 750000}
|
|
//! }
|
|
//! }
|
|
//! },
|
|
//! "connections": {
|
|
//! "clock_signals": {
|
|
//! "connection_type": "clock",
|
|
//! "source": {"component": "CLOCK", "pin": "CLK"},
|
|
//! "targets": [{"component": "CPU", "pin": "PHI1"}]
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
|
|
use crate::component::Component;
|
|
use crate::components::cpu::intel_4004::Intel4004;
|
|
use crate::components::memory::intel_4001::Intel4001;
|
|
use crate::components::memory::intel_4002::Intel4002;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
/// CPU register data for display purposes
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct CpuRegisterData {
|
|
pub program_counter: u16,
|
|
pub accumulator: u8,
|
|
pub carry: bool,
|
|
pub stack_pointer: u8,
|
|
pub index_registers: [u8; 16], // Intel 4004 has 16 index registers (R0-R15)
|
|
}
|
|
|
|
/// RAM data for display purposes (chip name -> banks of nibbles)
|
|
pub type RamData = Vec<(String, Vec<Vec<u8>>)>;
|
|
|
|
/// ROM data for display purposes (chip name -> bytes)
|
|
pub type RomData = Vec<(String, Vec<u8>)>;
|
|
|
|
/// JSON-based system configuration structures
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SystemConfig {
|
|
pub name: String,
|
|
pub description: String,
|
|
pub version: String,
|
|
pub metadata: HashMap<String, serde_json::Value>,
|
|
pub components: HashMap<String, ComponentConfig>,
|
|
pub connections: HashMap<String, ConnectionConfig>,
|
|
pub layout: Option<LayoutConfig>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum ComponentConfig {
|
|
#[serde(rename = "single")]
|
|
Single(SingleComponentConfig),
|
|
#[serde(rename = "array")]
|
|
Array(ArrayComponentConfig),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SingleComponentConfig {
|
|
pub component_type: String,
|
|
pub name: String,
|
|
pub properties: HashMap<String, serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ArrayComponentConfig {
|
|
pub component_type: String,
|
|
pub count: usize,
|
|
pub naming_pattern: String,
|
|
pub properties: HashMap<String, serde_json::Value>,
|
|
pub overrides: Option<HashMap<String, HashMap<String, serde_json::Value>>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ConnectionConfig {
|
|
pub connection_type: String,
|
|
pub source: PinReference,
|
|
pub targets: Vec<PinReference>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PinReference {
|
|
pub component: String,
|
|
pub pin: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct LayoutConfig {
|
|
pub grid_size: [usize; 2],
|
|
pub positions: HashMap<String, [usize; 2]>,
|
|
}
|
|
|
|
/// System factory for creating systems from JSON configuration
|
|
#[derive(Debug)]
|
|
pub struct SystemFactory {
|
|
component_registry: HashMap<
|
|
String,
|
|
fn(config: &ComponentConfig, name: String) -> Result<Box<dyn Component>, String>,
|
|
>,
|
|
}
|
|
|
|
impl SystemFactory {
|
|
pub fn new() -> Self {
|
|
println!("Starting SystemFactory::new");
|
|
let mut factory = SystemFactory {
|
|
component_registry: HashMap::new(),
|
|
};
|
|
factory.register_default_components();
|
|
factory
|
|
}
|
|
|
|
fn register_default_components(&mut self) {
|
|
println!("Starting register_default_components");
|
|
// Register component creation functions
|
|
self.component_registry.insert(
|
|
"intel_4004".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(single) = config {
|
|
let clock_speed = single
|
|
.properties
|
|
.get("clock_speed")
|
|
.and_then(|v| v.as_f64())
|
|
.unwrap_or(750000.0);
|
|
Ok(Box::new(
|
|
crate::components::cpu::intel_4004::Intel4004::new(name, clock_speed),
|
|
))
|
|
} else {
|
|
Err("Intel 4004 must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
|
|
self.component_registry.insert(
|
|
"generic_clock".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(single) = config {
|
|
let frequency = single
|
|
.properties
|
|
.get("frequency")
|
|
.and_then(|v| v.as_f64())
|
|
.unwrap_or(750000.0);
|
|
Ok(Box::new(
|
|
crate::components::clock::generic_clock::GenericClock::new(name, frequency),
|
|
))
|
|
} else {
|
|
Err("Generic clock must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
|
|
self.component_registry.insert(
|
|
"two_phase_clock".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(single) = config {
|
|
let frequency = single
|
|
.properties
|
|
.get("frequency")
|
|
.and_then(|v| v.as_f64())
|
|
.unwrap_or(750000.0);
|
|
Ok(Box::new(
|
|
crate::components::clock::two_phase_clock::TwoPhaseClock::new(
|
|
name, frequency,
|
|
),
|
|
))
|
|
} else {
|
|
Err("Two-phase clock must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
|
|
self.component_registry.insert(
|
|
"intel_4001".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(_single) = config {
|
|
Ok(Box::new(
|
|
crate::components::memory::intel_4001::Intel4001::new(name),
|
|
))
|
|
} else {
|
|
Err("Intel 4001 must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
|
|
self.component_registry.insert(
|
|
"intel_4002".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(single) = config {
|
|
let access_time = single
|
|
.properties
|
|
.get("access_time")
|
|
.and_then(|v| v.as_u64())
|
|
.unwrap_or(500);
|
|
|
|
Ok(Box::new(
|
|
crate::components::memory::intel_4002::Intel4002::new_with_access_time(
|
|
name,
|
|
access_time,
|
|
),
|
|
))
|
|
} else {
|
|
Err("Intel 4002 must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
|
|
self.component_registry.insert(
|
|
"intel_4003".to_string(),
|
|
|config: &ComponentConfig, name: String| {
|
|
if let ComponentConfig::Single(_single) = config {
|
|
Ok(Box::new(
|
|
crate::components::memory::intel_4003::Intel4003::new(name),
|
|
))
|
|
} else {
|
|
Err("Intel 4003 must be single component".to_string())
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
pub fn create_from_json(&self, json_path: &str) -> Result<ConfigurableSystem, String> {
|
|
let config: SystemConfig = self.load_json_config(json_path)?;
|
|
let mut components = self.create_components(&config)?;
|
|
self.connect_components(&config, &mut components)?;
|
|
Ok(ConfigurableSystem::new(config, components))
|
|
}
|
|
|
|
fn load_json_config(&self, path: &str) -> Result<SystemConfig, String> {
|
|
let content = std::fs::read_to_string(path)
|
|
.map_err(|e| format!("Failed to read config file '{}': {}", path, e))?;
|
|
|
|
serde_json::from_str(&content)
|
|
.map_err(|e| format!("Failed to parse JSON config '{}': {}", path, e))
|
|
}
|
|
|
|
fn create_components(
|
|
&self,
|
|
config: &SystemConfig,
|
|
) -> Result<HashMap<String, Arc<Mutex<Box<dyn Component>>>>, String> {
|
|
let mut components = HashMap::new();
|
|
|
|
for (id, component_config) in &config.components {
|
|
let component_names = self.expand_component_names(id, component_config);
|
|
|
|
for component_name in component_names {
|
|
let component =
|
|
self.create_single_component(component_config, component_name.clone())?;
|
|
components.insert(component_name, Arc::new(Mutex::new(component)));
|
|
}
|
|
}
|
|
|
|
Ok(components)
|
|
}
|
|
|
|
fn expand_component_names(&self, _id: &str, config: &ComponentConfig) -> Vec<String> {
|
|
match config {
|
|
ComponentConfig::Single(single) => vec![single.name.clone()],
|
|
ComponentConfig::Array(array) => {
|
|
let mut names = Vec::new();
|
|
for i in 0..array.count {
|
|
let name = array.naming_pattern.replace("{:02}", &format!("{:02}", i));
|
|
names.push(name);
|
|
}
|
|
names
|
|
}
|
|
}
|
|
}
|
|
|
|
fn create_single_component(
|
|
&self,
|
|
config: &ComponentConfig,
|
|
name: String,
|
|
) -> Result<Box<dyn Component>, String> {
|
|
match config {
|
|
ComponentConfig::Single(single) => {
|
|
if let Some(creator) = self.component_registry.get(&single.component_type) {
|
|
creator(config, name)
|
|
} else {
|
|
Err(format!("Unknown component type: {}", single.component_type))
|
|
}
|
|
}
|
|
ComponentConfig::Array(array) => {
|
|
if let Some(creator) = self.component_registry.get(&array.component_type) {
|
|
creator(config, name)
|
|
} else {
|
|
Err(format!("Unknown component type: {}", array.component_type))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn connect_components(
|
|
&self,
|
|
config: &SystemConfig,
|
|
components: &mut HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
|
|
) -> Result<(), String> {
|
|
for (connection_id, connection_config) in &config.connections {
|
|
println!(
|
|
"DEBUG: Connecting: {} - {} -> {:?}",
|
|
connection_id, connection_config.source.component, connection_config.targets
|
|
);
|
|
|
|
// Get source pin
|
|
let source_component = components
|
|
.get(&connection_config.source.component)
|
|
.ok_or_else(|| {
|
|
format!(
|
|
"Source component not found: {}",
|
|
connection_config.source.component
|
|
)
|
|
})?;
|
|
|
|
let source_pin = {
|
|
let component = source_component.lock().unwrap();
|
|
component
|
|
.get_pin(&connection_config.source.pin)
|
|
.map_err(|e| format!("Failed to get source pin: {}", e))?
|
|
};
|
|
|
|
// Connect to all targets
|
|
for target_ref in &connection_config.targets {
|
|
let target_component = components.get(&target_ref.component).ok_or_else(|| {
|
|
format!("Target component not found: {}", target_ref.component)
|
|
})?;
|
|
|
|
let target_pin = {
|
|
let component = target_component.lock().unwrap();
|
|
component
|
|
.get_pin(&target_ref.pin)
|
|
.map_err(|e| format!("Failed to get target pin: {}", e))?
|
|
};
|
|
|
|
// Connect the pins - source connects to target for proper signal flow
|
|
let mut source_pin_guard = source_pin.lock().unwrap();
|
|
source_pin_guard.connect_to(target_pin.clone());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Thread handles for running components
|
|
type ComponentHandles = Vec<(String, std::thread::JoinHandle<()>)>;
|
|
|
|
/// A configurable system created from JSON configuration
|
|
pub struct ConfigurableSystem {
|
|
config: SystemConfig,
|
|
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
|
|
is_running: Arc<Mutex<bool>>,
|
|
component_handles: Arc<Mutex<Option<ComponentHandles>>>,
|
|
}
|
|
|
|
impl ConfigurableSystem {
|
|
pub fn new(
|
|
config: SystemConfig,
|
|
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
|
|
) -> Self {
|
|
ConfigurableSystem {
|
|
config,
|
|
components,
|
|
is_running: Arc::new(Mutex::new(false)),
|
|
component_handles: Arc::new(Mutex::new(None)),
|
|
}
|
|
}
|
|
|
|
/// Start the system without blocking.
|
|
///
|
|
/// This method spawns component threads and returns immediately,
|
|
/// allowing the caller to retain access to the system via the mutex.
|
|
/// The system will continue running in the background until `stop()` is called.
|
|
pub fn start(&mut self) {
|
|
let mut is_running = self.is_running.lock().unwrap();
|
|
if *is_running {
|
|
println!("System is already running");
|
|
return;
|
|
}
|
|
*is_running = true;
|
|
drop(is_running); // Release lock before spawning threads
|
|
|
|
println!("Starting configurable system: {}", self.config.name);
|
|
println!("Description: {}", self.config.description);
|
|
|
|
let mut handles = vec![];
|
|
let is_running_clone = Arc::clone(&self.is_running);
|
|
|
|
for (name, component) in &self.components {
|
|
let comp_clone = Arc::clone(component);
|
|
let name_clone = name.clone();
|
|
let running_clone = Arc::clone(&is_running_clone);
|
|
|
|
let handle = std::thread::spawn(move || {
|
|
println!("DEBUG: Starting component thread: {}", name_clone);
|
|
if let Ok(mut comp) = comp_clone.lock() {
|
|
println!("DEBUG: Running component: {}", name_clone);
|
|
comp.run();
|
|
}
|
|
println!("DEBUG: Component {} stopped", name_clone);
|
|
|
|
// Signal that system should stop when any component stops
|
|
if let Ok(mut running) = running_clone.lock() {
|
|
*running = false;
|
|
}
|
|
});
|
|
|
|
handles.push((name.clone(), handle));
|
|
}
|
|
|
|
// Store handles for later cleanup
|
|
if let Ok(mut stored_handles) = self.component_handles.lock() {
|
|
*stored_handles = Some(handles);
|
|
}
|
|
|
|
println!("All components started. System running in background...");
|
|
}
|
|
|
|
/// Run the system (blocking version for backward compatibility).
|
|
///
|
|
/// This method starts the system and then monitors it until stopped.
|
|
/// Note: This method holds the mutex lock for the entire duration,
|
|
/// which can cause issues with console/GUI access. Use `start()` instead
|
|
/// for non-blocking operation.
|
|
pub fn run(&mut self) {
|
|
self.start();
|
|
|
|
// Monitor system state during execution
|
|
self.monitor_system_state();
|
|
|
|
// Stop and cleanup
|
|
self.cleanup();
|
|
}
|
|
|
|
/// Clean up component threads and stop all components.
|
|
fn cleanup(&mut self) {
|
|
// Signal stop
|
|
if let Ok(mut is_running) = self.is_running.lock() {
|
|
*is_running = false;
|
|
}
|
|
|
|
// Stop all components
|
|
println!("\nStopping system components...");
|
|
for (name, component) in &self.components {
|
|
if let Ok(mut comp) = component.lock() {
|
|
comp.stop();
|
|
println!("Stopped component: {}", name);
|
|
}
|
|
}
|
|
|
|
// Wait for threads
|
|
if let Ok(mut handles_guard) = self.component_handles.lock() {
|
|
if let Some(handles) = handles_guard.take() {
|
|
for (name, handle) in handles {
|
|
match handle.join() {
|
|
Ok(_) => println!("Component {} thread finished", name),
|
|
Err(_) => eprintln!("Component {} thread panicked", name),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("Configurable system stopped.");
|
|
}
|
|
|
|
/// Monitor and display system state during execution with focus on RAM
|
|
fn monitor_system_state(&self) {
|
|
println!("┌─────────────────────────────────────────────────────────────┐");
|
|
println!("│ RAM-CENTERED SYSTEM MONITOR │");
|
|
println!("├─────────────────────────────────────────────────────────────┤");
|
|
println!("│ Monitoring RAM contents and CPU state... │");
|
|
println!("└─────────────────────────────────────────────────────────────┘");
|
|
|
|
// Monitor for a longer duration with less frequent updates
|
|
for i in 0..20 {
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
|
|
// Check if system should stop
|
|
let should_continue = self.is_running.lock()
|
|
.map(|guard| *guard)
|
|
.unwrap_or(false);
|
|
|
|
if !should_continue {
|
|
println!("\nSystem stop requested - terminating monitoring");
|
|
break;
|
|
}
|
|
|
|
if i % 5 == 0 {
|
|
// Show header every 5 cycles
|
|
println!("\n┌─────────────────────────────────────────────────────────────┐");
|
|
println!(
|
|
"│ CYCLE {:2} │",
|
|
i
|
|
);
|
|
println!("└─────────────────────────────────────────────────────────────┘");
|
|
}
|
|
self.display_current_state();
|
|
}
|
|
|
|
println!("\n┌─────────────────────────────────────────────────────────────┐");
|
|
println!("│ MONITORING COMPLETE │");
|
|
println!("└─────────────────────────────────────────────────────────────┘");
|
|
}
|
|
|
|
/// Display current system state focusing on RAM contents and CPU state
|
|
fn display_current_state(&self) {
|
|
println!("┌─────────────────────────────────────────────────────────────┐");
|
|
println!("│ SYSTEM STATE MONITOR │");
|
|
println!("├─────────────────────────────────────────────────────────────┤");
|
|
|
|
// Display RAM contents prominently
|
|
if let Some(ram_component) = self.components.get("RAM_4002") {
|
|
if let Ok(ram) = ram_component.lock() {
|
|
println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │");
|
|
|
|
// Display RAM in a grid format for better readability
|
|
for bank in 0..4 {
|
|
print!("│ Bank {:2}: ", bank);
|
|
for addr in 0..4 {
|
|
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) {
|
|
if let Ok(pin_guard) = pin.lock() {
|
|
// Read actual pin value if possible
|
|
match pin_guard.read() {
|
|
crate::pin::PinValue::High => "1",
|
|
crate::pin::PinValue::Low => "0",
|
|
crate::pin::PinValue::HighZ => "Z",
|
|
}
|
|
} else {
|
|
"L"
|
|
}
|
|
} else {
|
|
"?"
|
|
};
|
|
print!("{:1}", value);
|
|
}
|
|
println!(" │");
|
|
}
|
|
}
|
|
} else {
|
|
println!("│ RAM_4002 component not found │");
|
|
}
|
|
|
|
// Display CPU state
|
|
if let Some(cpu_component) = self.components.get("CPU_4004") {
|
|
if let Ok(_cpu) = cpu_component.lock() {
|
|
println!("│ CPU State: │");
|
|
println!(
|
|
"│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │",
|
|
0, // Placeholder - would need CPU-specific method
|
|
false, // Placeholder - would need CPU-specific method
|
|
0
|
|
); // Placeholder - would need CPU-specific method
|
|
}
|
|
}
|
|
|
|
// Display clock state
|
|
if let Some(clock_component) = self.components.get("SYSTEM_CLOCK") {
|
|
if let Ok(_clock) = clock_component.lock() {
|
|
println!("│ Clock: Running │");
|
|
}
|
|
}
|
|
|
|
// Component status summary
|
|
let running_count = self
|
|
.components
|
|
.values()
|
|
.filter(|comp| comp.lock().map_or(false, |c| c.is_running()))
|
|
.count();
|
|
println!(
|
|
"│ Components: {}/{} running │",
|
|
running_count,
|
|
self.components.len()
|
|
);
|
|
|
|
println!("└─────────────────────────────────────────────────────────────┘");
|
|
}
|
|
|
|
pub fn stop(&mut self) {
|
|
if let Ok(mut is_running) = self.is_running.lock() {
|
|
*is_running = false;
|
|
}
|
|
// Also cleanup threads
|
|
self.cleanup();
|
|
}
|
|
|
|
pub fn is_running(&self) -> bool {
|
|
self.is_running.lock()
|
|
.map(|guard| *guard)
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
/// Get access to components for monitoring purposes
|
|
/// Returns: Reference to the components HashMap for read-only access
|
|
pub fn get_components(&self) -> &HashMap<String, Arc<Mutex<Box<dyn Component>>>> {
|
|
&self.components
|
|
}
|
|
|
|
pub fn get_system_info(&self) -> SystemInfo {
|
|
let rom_size = self
|
|
.config
|
|
.metadata
|
|
.get("rom_size")
|
|
.and_then(|v| v.as_u64())
|
|
.unwrap_or(256) as usize;
|
|
let ram_size = self
|
|
.config
|
|
.metadata
|
|
.get("ram_size")
|
|
.and_then(|v| v.as_u64())
|
|
.unwrap_or(40) as usize;
|
|
|
|
SystemInfo {
|
|
name: self.config.name.clone(),
|
|
description: self.config.description.clone(),
|
|
component_count: self.components.len(),
|
|
cpu_speed: self
|
|
.config
|
|
.metadata
|
|
.get("cpu_speed")
|
|
.and_then(|v| v.as_f64())
|
|
.unwrap_or(750000.0),
|
|
rom_size,
|
|
ram_size,
|
|
}
|
|
}
|
|
|
|
/// Load program data into ROM components
|
|
/// Parameters: program_data - Binary program data to load
|
|
/// Returns: Ok(()) on success, Err(String) on failure
|
|
pub fn load_program_data(&mut self, program_data: &[u8]) -> Result<(), String> {
|
|
println!(
|
|
"DEBUG: Loading {} bytes of program data into ROM components",
|
|
program_data.len()
|
|
);
|
|
|
|
// Load program data into ROM_4001_1 (first 256 bytes)
|
|
if let Some(rom1_component) = self.components.get_mut("ROM_4001_1") {
|
|
if let Ok(_rom1) = rom1_component.lock() {
|
|
let rom1_data = &program_data[..program_data.len().min(256)];
|
|
println!("DEBUG: Loading {} bytes into ROM_4001_1", rom1_data.len());
|
|
println!("DEBUG: Program data: {:02X?}", rom1_data);
|
|
// TODO: Load data using Intel4001's load_rom_data method when downcast is working
|
|
println!("DEBUG: Program data should be loaded into ROM_4001_1");
|
|
}
|
|
} else {
|
|
println!("DEBUG: Warning - ROM_4001_1 component not found");
|
|
}
|
|
|
|
// Load remaining data into ROM_4001_2 if program is larger than 256 bytes
|
|
if program_data.len() > 256 {
|
|
if let Some(rom2_component) = self.components.get_mut("ROM_4001_2") {
|
|
if let Ok(_rom2) = rom2_component.lock() {
|
|
let rom2_data = &program_data[256..program_data.len().min(512)];
|
|
println!("DEBUG: Loading {} bytes into ROM_4001_2", rom2_data.len());
|
|
// TODO: Load data using Intel4001's load_rom_data method when available
|
|
}
|
|
} else {
|
|
println!("DEBUG: Warning - ROM_4001_2 component not found");
|
|
}
|
|
}
|
|
|
|
println!("DEBUG: Program loading completed");
|
|
Ok(())
|
|
}
|
|
|
|
/// Get CPU register data from Intel 4004 CPU
|
|
/// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise
|
|
pub fn get_cpu_register_data(&self) -> Option<CpuRegisterData> {
|
|
// Look for Intel 4004 CPU components
|
|
for (_name, component) in &self.components {
|
|
if let Ok(comp) = component.lock() {
|
|
// Try to downcast to Intel4004
|
|
if let Some(cpu) = comp.as_any().downcast_ref::<Intel4004>() {
|
|
let mut index_registers = [0u8; 16];
|
|
for i in 0..16 {
|
|
index_registers[i] = cpu.get_register(i as u8).unwrap_or(0);
|
|
}
|
|
|
|
return Some(CpuRegisterData {
|
|
program_counter: cpu.get_program_counter(),
|
|
accumulator: cpu.get_accumulator(),
|
|
carry: cpu.get_carry(),
|
|
stack_pointer: cpu.get_stack_pointer(),
|
|
index_registers,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Get RAM data from Intel 4002 RAM chips
|
|
/// Parameters: max_rows - Maximum number of rows to return per bank
|
|
/// Returns: Vector of (chip_name, banks) where each bank is a vector of nibbles
|
|
pub fn get_ram_data(&self, max_rows: usize) -> RamData {
|
|
let mut ram_data = Vec::new();
|
|
|
|
for (name, component) in &self.components {
|
|
if let Ok(comp) = component.lock() {
|
|
// Try to downcast to Intel4002
|
|
if let Some(ram) = comp.as_any().downcast_ref::<Intel4002>() {
|
|
let mut banks = Vec::new();
|
|
|
|
// Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip)
|
|
// But actually it has 4 main memory registers, each with 16 4-bit characters
|
|
// and 4 status characters = 4 x 20 = 80 nibbles
|
|
for bank in 0..4 {
|
|
let mut bank_data = Vec::new();
|
|
// Read up to max_rows * 4 nibbles per bank
|
|
// The read_ram_direct takes a single address parameter
|
|
for addr in 0..(max_rows * 4).min(20) {
|
|
// Calculate the combined address for this bank and offset
|
|
let full_addr = (bank * 20 + addr) as u8;
|
|
// Use read_ram_direct to get the nibble at this address
|
|
let value = ram.read_ram_direct(full_addr).unwrap_or(0);
|
|
bank_data.push(value);
|
|
}
|
|
banks.push(bank_data);
|
|
}
|
|
|
|
ram_data.push((name.clone(), banks));
|
|
}
|
|
}
|
|
}
|
|
|
|
ram_data
|
|
}
|
|
|
|
/// Get ROM data from Intel 4001 ROM chips
|
|
/// Parameters: max_rows - Maximum number of bytes to return per chip
|
|
/// Returns: Vector of (chip_name, bytes)
|
|
pub fn get_rom_data(&self, max_rows: usize) -> RomData {
|
|
let mut rom_data = Vec::new();
|
|
|
|
for (name, component) in &self.components {
|
|
if let Ok(comp) = component.lock() {
|
|
// Try to downcast to Intel4001
|
|
if let Some(rom) = comp.as_any().downcast_ref::<Intel4001>() {
|
|
let mut data = Vec::new();
|
|
|
|
// Intel 4001 has 256 bytes of ROM
|
|
for addr in 0..max_rows.min(256) {
|
|
let value = rom.read_rom(addr as u8).unwrap_or(0);
|
|
data.push(value);
|
|
}
|
|
|
|
rom_data.push((name.clone(), data));
|
|
}
|
|
}
|
|
}
|
|
|
|
rom_data
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct SystemInfo {
|
|
pub name: String,
|
|
pub description: String,
|
|
pub component_count: usize,
|
|
pub cpu_speed: f64,
|
|
pub rom_size: usize,
|
|
pub ram_size: usize,
|
|
}
|