rusty_emu/src/system_config.rs

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,
}