More flexible bus system that takes VIC-20 address mapping and multiplexed buses like the MCS-4 into account.
Broken currently, subject to fixes
This commit is contained in:
parent
ddac8865fa
commit
ba1b0ddc22
14 changed files with 1000 additions and 68 deletions
|
|
@ -1,5 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use crate::component::{BaseComponent, Component};
|
||||
|
|
@ -8,7 +8,6 @@ use crate::pin::{Pin, PinValue};
|
|||
#[derive(Clone)]
|
||||
pub struct GenericClock {
|
||||
base: BaseComponent,
|
||||
frequency: u64,
|
||||
half_period: Duration,
|
||||
last_state: bool,
|
||||
}
|
||||
|
|
@ -21,7 +20,6 @@ impl GenericClock {
|
|||
|
||||
Self {
|
||||
base,
|
||||
frequency: frequency_hz,
|
||||
half_period: Duration::from_millis(half_period_ms),
|
||||
last_state: false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,231 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use crate::component::{BaseComponent, Component};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use crate::bus::GenericBus;
|
||||
use crate::component::Component;
|
||||
use crate::pin::{Pin, PinValue};
|
||||
|
||||
// Simple 4-bit and 12-bit types for the 4004
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct u4(u8);
|
||||
struct U4(u8);
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct u12(u16);
|
||||
impl u4 {
|
||||
fn wrapping_add(self, other: u4) -> u4 {
|
||||
u4((self.0 + other.0) & 0xF)
|
||||
struct U12(u16);
|
||||
impl U4 {
|
||||
fn wrapping_add(self, other: U4) -> U4 {
|
||||
U4((self.0 + other.0) & 0xF)
|
||||
}
|
||||
}
|
||||
impl u12 {
|
||||
fn wrapping_add(self, other: u12) -> u12 {
|
||||
u12((self.0 + other.0) & 0xFFF)
|
||||
impl U12 {
|
||||
fn wrapping_add(self, other: U12) -> U12 {
|
||||
U12((self.0 + other.0) & 0xFFF)
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Intel4004 {
|
||||
base: BaseComponent,
|
||||
program_counter: u12,
|
||||
accumulator: u4,
|
||||
base: crate::component::BaseComponent,
|
||||
program_counter: U12,
|
||||
accumulator: U4,
|
||||
carry: bool,
|
||||
last_clock_state: PinValue,
|
||||
registers: [U4; 16], // 4004 has 16 4-bit registers
|
||||
stack: [U12; 3], // 3-level subroutine stack
|
||||
stack_pointer: u8,
|
||||
bus: Option<Arc<Mutex<GenericBus>>>, // Connected bus
|
||||
}
|
||||
impl Intel4004 {
|
||||
pub fn new(name: String) -> Self {
|
||||
let mut base = BaseComponent::new(name);
|
||||
let mut base = crate::component::BaseComponent::new(name);
|
||||
|
||||
// Add essential pins
|
||||
base.add_pin("clk".to_string(), PinValue::Low, false); // Input pin
|
||||
base.add_pin("reset".to_string(), PinValue::Low, false); // Input pin
|
||||
base.add_pin("data".to_string(), PinValue::HighZ, false); // Bidirectional
|
||||
base.add_pin("clk".to_string(), PinValue::Low, false);
|
||||
base.add_pin("reset".to_string(), PinValue::Low, false);
|
||||
base.add_pin("sync".to_string(), PinValue::Low, true);
|
||||
base.add_pin("data".to_string(), PinValue::HighZ, false);
|
||||
|
||||
Self {
|
||||
base,
|
||||
program_counter: u12(0),
|
||||
accumulator: u4(0),
|
||||
program_counter: U12(0),
|
||||
accumulator: U4(0),
|
||||
carry: false,
|
||||
last_clock_state: PinValue::Low,
|
||||
registers: [U4(0); 16],
|
||||
stack: [U12(0); 3],
|
||||
stack_pointer: 0,
|
||||
bus: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_to_bus(&mut self, bus: Arc<Mutex<GenericBus>>) {
|
||||
self.bus = Some(bus);
|
||||
}
|
||||
|
||||
fn read_memory(&self, address: u16) -> u8 {
|
||||
if let Some(bus) = &self.bus {
|
||||
let bus = bus.lock().unwrap();
|
||||
bus.read(address as u64) as u8
|
||||
} else {
|
||||
0xFF
|
||||
}
|
||||
}
|
||||
|
||||
fn write_memory(&mut self, address: u16, value: u8) {
|
||||
if let Some(bus) = &self.bus {
|
||||
let mut bus = bus.lock().unwrap();
|
||||
bus.write(address as u64, value as u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_instruction(&mut self) -> u8 {
|
||||
let instruction = self.read_memory(self.program_counter.0);
|
||||
self.program_counter = self.program_counter.wrapping_add(U12(1));
|
||||
instruction
|
||||
}
|
||||
|
||||
fn execute(&mut self, instruction: u8) {
|
||||
match instruction {
|
||||
// NOP
|
||||
0x00 => self.nop(),
|
||||
|
||||
// JCN - Jump Conditional
|
||||
0x10 => self.jcn(),
|
||||
|
||||
// FIM - Fetch Immediate
|
||||
0x20 => self.fim(),
|
||||
|
||||
// SRC - Send Register Control
|
||||
//0x21 => self.src(),
|
||||
|
||||
// FIN - Fetch Indirect
|
||||
//0x30 => self.fin(),
|
||||
|
||||
// JIN - Jump Indirect
|
||||
//0x31 => self.jin(),
|
||||
|
||||
// LDM - Load Immediate
|
||||
0xD0 => self.ldm(),
|
||||
|
||||
// INC - Increment Register
|
||||
0x60 => self.inc(),
|
||||
|
||||
// ADD - Add
|
||||
0xF0 => self.add(),
|
||||
|
||||
// SUB - Subtract
|
||||
//0xF1 => self.sub(),
|
||||
|
||||
// LD - Load from Register
|
||||
//0xA0 => self.ld(),
|
||||
|
||||
// XCH - Exchange
|
||||
//0xB0 => self.xch(),
|
||||
|
||||
// BBL - Branch Back and Load
|
||||
//0xC0 => self.bbl(),
|
||||
|
||||
// WRM - Write Main Memory
|
||||
0xE0 => self.wrm(),
|
||||
|
||||
// RDM - Read Main Memory
|
||||
0xE1 => self.rdm(),
|
||||
|
||||
_ => println!("Unknown instruction: {:02X}", instruction),
|
||||
}
|
||||
}
|
||||
|
||||
// Instruction implementations
|
||||
fn nop(&mut self) {
|
||||
// No operation
|
||||
}
|
||||
|
||||
fn jcn(&mut self) {
|
||||
let condition = self.fetch_instruction();
|
||||
let address_low = self.fetch_instruction();
|
||||
let address_high = self.fetch_instruction();
|
||||
let address = ((address_high as u16) << 8) | address_low as u16;
|
||||
|
||||
let mut jump = false;
|
||||
|
||||
// Check conditions
|
||||
if condition & 0x01 != 0 && self.carry { jump = true; }
|
||||
if condition & 0x02 != 0 && self.accumulator.0 == 0 { jump = true; }
|
||||
if condition & 0x04 != 0 && self.test_pin() { jump = true; }
|
||||
if condition & 0x08 != 0 { jump = !jump; }
|
||||
|
||||
if jump {
|
||||
self.program_counter = U12(address);
|
||||
}
|
||||
}
|
||||
|
||||
fn fim(&mut self) {
|
||||
let register_pair = (self.fetch_instruction() & 0x0E) as usize; // Even registers only
|
||||
let data = self.fetch_instruction();
|
||||
|
||||
self.registers[register_pair] = U4(data >> 4);
|
||||
self.registers[register_pair + 1] = U4(data & 0x0F);
|
||||
}
|
||||
|
||||
fn ldm(&mut self) {
|
||||
let data = self.fetch_instruction();
|
||||
self.accumulator = U4(data & 0x0F);
|
||||
}
|
||||
|
||||
fn inc(&mut self) {
|
||||
let register = (self.fetch_instruction() & 0x0F) as usize;
|
||||
let new_value = self.registers[register].0.wrapping_add(1) & 0x0F;
|
||||
self.registers[register] = U4(new_value);
|
||||
}
|
||||
|
||||
fn add(&mut self) {
|
||||
let register = (self.fetch_instruction() & 0x0F) as usize;
|
||||
let result = self.accumulator.0 + self.registers[register].0;
|
||||
self.accumulator = U4(result & 0x0F);
|
||||
self.carry = result > 0x0F;
|
||||
}
|
||||
|
||||
fn wrm(&mut self) {
|
||||
// Write accumulator to main memory
|
||||
let address = self.get_memory_address();
|
||||
self.write_memory(address, self.accumulator.0);
|
||||
}
|
||||
|
||||
fn rdm(&mut self) {
|
||||
// Read from main memory to accumulator
|
||||
let address = self.get_memory_address();
|
||||
let data = self.read_memory(address);
|
||||
self.accumulator = U4(data & 0x0F);
|
||||
}
|
||||
|
||||
fn get_memory_address(&self) -> u16 {
|
||||
// Simplified address calculation
|
||||
// In real 4004, this uses register pairs
|
||||
0x200 + self.registers[0].0 as u16 // Example: use register 0 as offset
|
||||
}
|
||||
|
||||
fn test_pin(&self) -> bool {
|
||||
// Check test pin (simplified)
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> String {
|
||||
format!(
|
||||
"PC: {:03X} ACC: {:X} C: {} R0: {:X} R1: {:X}",
|
||||
self.program_counter.0,
|
||||
self.accumulator.0,
|
||||
self.carry as u8,
|
||||
self.registers[0].0,
|
||||
self.registers[1].0
|
||||
)
|
||||
}
|
||||
pub fn clk(&self) -> Option<Arc<Pin>> {
|
||||
self.base.get_pin("clk")
|
||||
}
|
||||
|
||||
pub fn reset(&self) -> Option<Arc<Pin>> {
|
||||
self.base.get_pin("reset")
|
||||
}
|
||||
|
||||
pub fn data(&self) -> Option<Arc<Pin>> {
|
||||
self.base.get_pin("data")
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Intel4004 {
|
||||
fn name(&self) -> &str {
|
||||
self.base.name()
|
||||
|
|
@ -63,28 +237,23 @@ impl Component for Intel4004 {
|
|||
self.base.get_pin(name)
|
||||
}
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
// Read clock pin (check if any connected component is driving it low)
|
||||
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 {
|
||||
// Simple fetch-execute cycle
|
||||
self.program_counter = self.program_counter.wrapping_add(u12(1));
|
||||
// Fetch and execute instruction
|
||||
let instruction = self.fetch_instruction();
|
||||
self.execute(instruction);
|
||||
|
||||
// Read reset pin
|
||||
let reset_pin = self.base.pins.get("reset").unwrap();
|
||||
let reset_value = reset_pin.read();
|
||||
// Update sync pin
|
||||
let sync_pin = self.base.pins.get("sync").unwrap();
|
||||
sync_pin.write(PinValue::High, true);
|
||||
|
||||
if reset_value == PinValue::High {
|
||||
self.program_counter = u12(0);
|
||||
self.accumulator = u4(0);
|
||||
println!("CPU {}: RESET", self.name());
|
||||
} else {
|
||||
// For now, just print state
|
||||
println!("CPU {}: PC={:03X}, ACC={:X}",
|
||||
self.name(), self.program_counter.0, self.accumulator.0);
|
||||
}
|
||||
println!("{}", self.get_state());
|
||||
} else if clk_value == PinValue::Low {
|
||||
let sync_pin = self.base.pins.get("sync").unwrap();
|
||||
sync_pin.write(PinValue::Low, true);
|
||||
}
|
||||
|
||||
self.last_clock_state = clk_value;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ impl Component for MOS6502 {
|
|||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||
todo!()
|
||||
}
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
fn get_pin(&self, _name: &str) -> Option<Arc<Pin>> {
|
||||
todo!()
|
||||
}
|
||||
// Implement Component trait methods...
|
||||
|
|
|
|||
303
src/components/memory/generic_ram.rs
Normal file
303
src/components/memory/generic_ram.rs
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use crate::{Component, Pin, PinValue};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenericRam<const SIZE: usize, const DATA_WIDTH: usize, const ADDR_WIDTH: usize> {
|
||||
base: crate::component::BaseComponent,
|
||||
pub(crate) memory: Vec<u64>,
|
||||
base_address: u64,
|
||||
address_mask: u64,
|
||||
read_delay: u32, // Read access time in cycles
|
||||
write_delay: u32, // Write access time in cycles
|
||||
current_operation: Option<RamOperation>,
|
||||
operation_cycles: u32,
|
||||
output_buffer: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum RamOperation {
|
||||
Read(u64), // address
|
||||
Write(u64, u64), // address, data
|
||||
}
|
||||
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize, const ADDR_WIDTH: usize>
|
||||
GenericRam<SIZE, DATA_WIDTH, ADDR_WIDTH>
|
||||
{
|
||||
pub fn new(name: String, base_address: u64, read_delay: u32, write_delay: u32) -> Self {
|
||||
let mut base = crate::component::BaseComponent::new(name);
|
||||
|
||||
// Add address pins
|
||||
for i in 0..ADDR_WIDTH {
|
||||
base.add_pin(format!("addr_{}", i), PinValue::Low, false);
|
||||
}
|
||||
|
||||
// Add data pins
|
||||
for i in 0..DATA_WIDTH {
|
||||
base.add_pin(format!("data_{}", i), PinValue::HighZ, false);
|
||||
}
|
||||
|
||||
// Add control pins
|
||||
base.add_pin("cs".to_string(), PinValue::High, false); // Chip Select (active low)
|
||||
base.add_pin("we".to_string(), PinValue::High, false); // Write Enable (active low)
|
||||
base.add_pin("oe".to_string(), PinValue::High, false); // Output Enable (active low)
|
||||
base.add_pin("clk".to_string(), PinValue::Low, false); // Clock for synchronization
|
||||
|
||||
// Calculate address mask based on size
|
||||
let address_mask = ((1 << ADDR_WIDTH) - 1) as u64;
|
||||
|
||||
Self {
|
||||
base,
|
||||
memory: vec![0; SIZE],
|
||||
base_address,
|
||||
address_mask,
|
||||
read_delay,
|
||||
write_delay,
|
||||
current_operation: None,
|
||||
operation_cycles: 0,
|
||||
output_buffer: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_address_bus(&self) -> u64 {
|
||||
let mut address = 0;
|
||||
for i in 0..ADDR_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("addr_{}", i)) {
|
||||
if pin.read() == PinValue::High {
|
||||
address |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
address
|
||||
}
|
||||
|
||||
fn read_data_bus(&self) -> u64 {
|
||||
let mut data = 0;
|
||||
for i in 0..DATA_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
if pin.read() == PinValue::High {
|
||||
data |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
fn write_data_bus(&self, data: u64) {
|
||||
for i in 0..DATA_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
let bit = (data >> i) & 1;
|
||||
let value = if bit == 1 { PinValue::High } else { PinValue::Low };
|
||||
pin.write(value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_data_bus_high_z(&self) {
|
||||
for i in 0..DATA_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
pin.write(PinValue::HighZ, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_selected(&self) -> bool {
|
||||
if let Some(cs_pin) = self.base.get_pin("cs") {
|
||||
cs_pin.read() == PinValue::Low // Active low
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_enabled(&self) -> bool {
|
||||
if let Some(we_pin) = self.base.get_pin("we") {
|
||||
we_pin.read() == PinValue::Low // Active low
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_output_enabled(&self) -> bool {
|
||||
if let Some(oe_pin) = self.base.get_pin("oe") {
|
||||
oe_pin.read() == PinValue::Low // Active low
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_clock_rising_edge(&self, last_clock: &mut PinValue) -> bool {
|
||||
if let Some(clk_pin) = self.base.get_pin("clk") {
|
||||
let current_clock = clk_pin.read();
|
||||
let rising_edge = *last_clock == PinValue::Low && current_clock == PinValue::High;
|
||||
*last_clock = current_clock;
|
||||
rising_edge
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn decode_address(&self, bus_address: u64) -> Option<usize> {
|
||||
// Check if address is in our range
|
||||
let relative_address = bus_address & self.address_mask;
|
||||
|
||||
if (bus_address & !self.address_mask) == self.base_address {
|
||||
// Address matches our base + mask
|
||||
let index = relative_address as usize;
|
||||
if index < SIZE {
|
||||
Some(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn start_read_operation(&mut self, address: u64) {
|
||||
if let Some(index) = self.decode_address(address) {
|
||||
self.current_operation = Some(RamOperation::Read(address));
|
||||
self.operation_cycles = 0;
|
||||
println!("RAM {}: Starting read from address {:X} (index {})",
|
||||
self.base.name, address, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn start_write_operation(&mut self, address: u64, data: u64) {
|
||||
if let Some(index) = self.decode_address(address) {
|
||||
self.current_operation = Some(RamOperation::Write(address, data));
|
||||
self.operation_cycles = 0;
|
||||
println!("RAM {}: Starting write {:X} to address {:X} (index {})",
|
||||
self.base.name, data, address, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_read_operation(&mut self, address: u64) {
|
||||
if let Some(index) = self.decode_address(address) {
|
||||
let data = self.memory[index];
|
||||
self.output_buffer = Some(data);
|
||||
println!("RAM {}: Read complete - data {:X} from address {:X}",
|
||||
self.base.name, data, address);
|
||||
}
|
||||
self.current_operation = None;
|
||||
}
|
||||
|
||||
fn complete_write_operation(&mut self, address: u64, data: u64) {
|
||||
if let Some(index) = self.decode_address(address) {
|
||||
self.memory[index] = data & ((1 << DATA_WIDTH) - 1); // Mask to data width
|
||||
println!("RAM {}: Write complete - data {:X} to address {:X}",
|
||||
self.base.name, data, address);
|
||||
}
|
||||
self.current_operation = None;
|
||||
}
|
||||
|
||||
fn update_operation(&mut self) {
|
||||
if let Some(operation) = self.current_operation {
|
||||
self.operation_cycles += 1;
|
||||
|
||||
match operation {
|
||||
RamOperation::Read(addr) => {
|
||||
if self.operation_cycles >= self.read_delay {
|
||||
self.complete_read_operation(addr);
|
||||
}
|
||||
}
|
||||
RamOperation::Write(addr, data) => {
|
||||
if self.operation_cycles >= self.write_delay {
|
||||
self.complete_write_operation(addr, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_bus_signals(&mut self, bus_address: u64, last_clock: &mut PinValue) {
|
||||
if !self.is_selected() {
|
||||
// Not selected - high impedance and clear any pending operations
|
||||
self.set_data_bus_high_z();
|
||||
self.current_operation = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let clock_rising_edge = self.is_clock_rising_edge(last_clock);
|
||||
|
||||
if clock_rising_edge {
|
||||
// On clock edge, check what operation is requested
|
||||
if self.is_write_enabled() {
|
||||
// Write operation
|
||||
let data = self.read_data_bus();
|
||||
self.start_write_operation(bus_address, data);
|
||||
} else if self.is_output_enabled() {
|
||||
// Read operation
|
||||
self.start_read_operation(bus_address);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ongoing operations
|
||||
self.update_operation();
|
||||
|
||||
// Output data if we have it and output is enabled
|
||||
if self.is_output_enabled() {
|
||||
if let Some(data) = self.output_buffer {
|
||||
self.write_data_bus(data);
|
||||
} else {
|
||||
self.set_data_bus_high_z();
|
||||
}
|
||||
} else {
|
||||
self.set_data_bus_high_z();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize, const ADDR_WIDTH: usize>
|
||||
crate::component::Component for GenericRam<SIZE, DATA_WIDTH, ADDR_WIDTH>
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
self.base.name()
|
||||
}
|
||||
|
||||
fn pins(&self) -> &std::collections::HashMap<String, Arc<Pin>> {
|
||||
self.base.pins()
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
self.base.get_pin(name)
|
||||
}
|
||||
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
let bus_address = self.read_address_bus();
|
||||
let mut last_clock = PinValue::Low;
|
||||
|
||||
self.handle_bus_signals(bus_address, &mut last_clock);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.base.run();
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
self.base.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// BusDevice implementation
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize, const ADDR_WIDTH: usize>
|
||||
crate::bus::BusDevice for GenericRam<SIZE, DATA_WIDTH, ADDR_WIDTH>
|
||||
{
|
||||
fn update(&mut self) {
|
||||
// Delegate to component update
|
||||
let _ = Component::update(self);
|
||||
}
|
||||
|
||||
fn connect_to_bus(&mut self, bus_pins: Vec<Arc<Pin>>) {
|
||||
// Connect to bus pins - in a real system, this would connect address, data, and control pins
|
||||
// This is simplified for the example
|
||||
}
|
||||
|
||||
fn get_address_range(&self) -> Option<std::ops::Range<u64>> {
|
||||
Some(self.base_address..self.base_address + SIZE as u64)
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
118
src/components/memory/generic_rom.rs
Normal file
118
src/components/memory/generic_rom.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use crate::{Component, Pin, PinValue};
|
||||
use crate::bus::BusDevice;
|
||||
|
||||
pub struct GenericRom<const SIZE: usize, const DATA_WIDTH: usize> {
|
||||
pub(crate) base: crate::component::BaseComponent,
|
||||
memory: HashMap<u64, u64>, // Generic memory storage
|
||||
data_width: usize,
|
||||
address_range: std::ops::Range<u64>,
|
||||
pub latched_address: ()
|
||||
}
|
||||
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize> GenericRom<SIZE, DATA_WIDTH> {
|
||||
pub fn new(name: String, base_address: u64) -> Self {
|
||||
let mut base = crate::component::BaseComponent::new(name);
|
||||
|
||||
// Add data pins based on width
|
||||
for i in 0..DATA_WIDTH {
|
||||
base.add_pin(format!("data_{}", i), PinValue::HighZ, false);
|
||||
}
|
||||
|
||||
// Add control pins
|
||||
base.add_pin("cs".to_string(), PinValue::Low, false);
|
||||
base.add_pin("oe".to_string(), PinValue::Low, false);
|
||||
|
||||
let address_range = base_address..base_address + SIZE as u64;
|
||||
|
||||
Self {
|
||||
base,
|
||||
memory: HashMap::new(),
|
||||
data_width: DATA_WIDTH,
|
||||
address_range,
|
||||
latched_address: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_data(&mut self, data: &[u64]) {
|
||||
for (i, &word) in data.iter().enumerate() {
|
||||
if i < SIZE {
|
||||
self.memory.insert(self.address_range.start + i as u64, word);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn is_selected(&self, address: u64) -> bool {
|
||||
if let Some(cs_pin) = self.base.get_pin("cs") {
|
||||
cs_pin.read() == PinValue::High && self.address_range.contains(&address)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn get_current_address(&self) -> u64 {
|
||||
// In a real system, this would read address from connected address pins
|
||||
// For now, return a default address
|
||||
0
|
||||
}
|
||||
|
||||
fn output_data(&self, data: u64) {
|
||||
for i in 0..DATA_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
let bit_value = (data >> i) & 1;
|
||||
let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low };
|
||||
pin.write(pin_value, true); // Drive the pin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_data_pins_high_z(&self) {
|
||||
for i in 0..DATA_WIDTH {
|
||||
if let Some(pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
pin.write(PinValue::HighZ, false); // High impedance, not driving
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize, const DATA_WIDTH: usize> BusDevice for GenericRom<SIZE, DATA_WIDTH> {
|
||||
fn update(&mut self) {
|
||||
// Check if we're selected and output is enabled
|
||||
if let (Some(cs_pin), Some(oe_pin)) = (self.base.get_pin("cs"), self.base.get_pin("oe")) {
|
||||
let is_selected = cs_pin.read() == PinValue::High;
|
||||
let output_enabled = oe_pin.read() == PinValue::Low; // Active low
|
||||
|
||||
if is_selected && output_enabled {
|
||||
// Read address from address pins (simplified - in real system, address would come from bus)
|
||||
// For now, we'll use a dummy address - this would be connected to address pins in a real system
|
||||
let address = self.get_current_address();
|
||||
|
||||
// Read data from ROM
|
||||
let data = self.read(address);
|
||||
|
||||
// Output data to data pins
|
||||
self.output_data(data);
|
||||
} else {
|
||||
// Put data pins in high impedance when not selected
|
||||
self.set_data_pins_high_z();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_to_bus(&mut self, bus_pins: Vec<Arc<Pin>>) {
|
||||
// Connect data pins
|
||||
for (i, bus_pin) in bus_pins.iter().enumerate() {
|
||||
if let Some(rom_pin) = self.base.get_pin(&format!("data_{}", i)) {
|
||||
crate::connection::connect_pins(&rom_pin, bus_pin).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_address_range(&self) -> Option<std::ops::Range<u64>> {
|
||||
Some(self.address_range.clone())
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
56
src/components/memory/intel_4001.rs
Normal file
56
src/components/memory/intel_4001.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use crate::components::memory::generic_rom::GenericRom;
|
||||
use crate::{Component, PinValue};
|
||||
use crate::systems::intel_mcs_4::MCS4Phase;
|
||||
|
||||
// 4001 ROM: 256 bytes, 4-bit data width
|
||||
pub type Intel4001 = GenericRom<256, 4>;
|
||||
impl Intel4001 {
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
// Read the current bus phase from the pins
|
||||
let phase = self.detect_bus_phase();
|
||||
|
||||
match phase {
|
||||
MCS4Phase::AddressLow => {
|
||||
self.latch_address_low();
|
||||
}
|
||||
MCS4Phase::AddressHigh => {
|
||||
self.latch_address_high();
|
||||
if self.is_selected() {
|
||||
self.prepare_data_output();
|
||||
}
|
||||
}
|
||||
MCS4Phase::DataRead => {
|
||||
if self.is_selected() {
|
||||
self.output_data();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Put pins in high impedance
|
||||
self.set_pins_high_z();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn detect_bus_phase(&self) -> MCS4Phase {
|
||||
// Detect phase based on sync and other control signals
|
||||
if let Some(sync_pin) = self.base.get_pin("pin_0") {
|
||||
match sync_pin.read() {
|
||||
PinValue::High => MCS4Phase::AddressLow, // Sync high = address phase
|
||||
PinValue::Low => MCS4Phase::DataRead, // Sync low = data phase
|
||||
_ => MCS4Phase::AddressLow,
|
||||
}
|
||||
} else {
|
||||
MCS4Phase::AddressLow
|
||||
}
|
||||
}
|
||||
fn latch_address_low(&mut self) {
|
||||
let addr_low = self.read_nibble_from_pins(9..12);
|
||||
self.latched_address = (self.latched_address & 0xFF0) | (addr_low as u16);
|
||||
}
|
||||
fn latch_address_high(&mut self) {
|
||||
let addr_high = self.read_nibble_from_pins(9..12);
|
||||
let addr_mid = self.read_nibble_from_pins(13..16);
|
||||
self.latched_address = ((addr_mid as u16) << 8) | ((addr_high as u16) << 4) | (self.latched_address & 0x00F);
|
||||
}
|
||||
}
|
||||
51
src/components/memory/intel_4002.rs
Normal file
51
src/components/memory/intel_4002.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use crate::components::memory::generic_ram::GenericRam;
|
||||
|
||||
// 4002 RAM: 80 nibbles (4-bit words), 12-bit address bus
|
||||
// Organized as 4 registers × 20 characters × 4 bits
|
||||
pub type Intel4002 = GenericRam<80, 4, 12>;
|
||||
|
||||
impl Intel4002 {
|
||||
pub fn new_4002(name: String, base_address: u16) -> Self {
|
||||
// 4002 has specific timing characteristics
|
||||
let ram = Self::new(
|
||||
name,
|
||||
base_address as u64,
|
||||
2, // 2-cycle read access
|
||||
3 // 3-cycle write access
|
||||
);
|
||||
|
||||
ram
|
||||
}
|
||||
|
||||
// 4002-specific methods
|
||||
pub fn write_to_register_character(&mut self, register: u8, character: u8, value: u8) -> Result<(), String> {
|
||||
if register >= 4 || character >= 20 {
|
||||
return Err("Invalid register or character index".to_string());
|
||||
}
|
||||
|
||||
let address = self.calculate_address(register, character);
|
||||
let index = self.decode_address(address as u64)
|
||||
.ok_or("Address out of range".to_string())?;
|
||||
|
||||
self.memory[index] = (value & 0x0F) as u64;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_from_register_character(&self, register: u8, character: u8) -> Result<u8, String> {
|
||||
if register >= 4 || character >= 20 {
|
||||
return Err("Invalid register or character index".to_string());
|
||||
}
|
||||
|
||||
let address = self.calculate_address(register, character);
|
||||
let index = self.decode_address(address as u64)
|
||||
.ok_or("Address out of range".to_string())?;
|
||||
|
||||
Ok(self.memory[index] as u8)
|
||||
}
|
||||
|
||||
fn calculate_address(&self, register: u8, character: u8) -> u16 {
|
||||
// 4002 address calculation:
|
||||
// Each register has 20 characters, each character is 4 bits
|
||||
(register as u16 * 20) + (character as u16)
|
||||
}
|
||||
}
|
||||
4
src/components/memory/mod.rs
Normal file
4
src/components/memory/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod intel_4002;
|
||||
pub mod intel_4001;
|
||||
pub mod generic_rom;
|
||||
mod generic_ram;
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod cpu;
|
||||
pub mod clock;
|
||||
pub mod memory;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue