feat(cpu): add A1/A2/A3 address cycle timing and fix pin propagation
This commit is contained in:
parent
3cdf99630c
commit
20884b57c3
17 changed files with 4165 additions and 270 deletions
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/rusty_emu.iml" filepath="$PROJECT_DIR$/.idea/rusty_emu.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
18
.idea/runConfigurations/Terminal_Debug.xml
generated
Normal file
18
.idea/runConfigurations/Terminal_Debug.xml
generated
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Terminal Debug" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="buildProfileId" value="dev" />
|
||||||
|
<option name="command" value="run --package rusty_emu --bin rusty_emu" />
|
||||||
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
|
<envs />
|
||||||
|
<option name="emulateTerminal" value="false" />
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="requiredFeatures" value="true" />
|
||||||
|
<option name="allFeatures" value="true" />
|
||||||
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
<option name="backtrace" value="SHORT" />
|
||||||
|
<option name="isRedirectInput" value="false" />
|
||||||
|
<option name="redirectInputPath" value="" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
19
.idea/runConfigurations/gui/Console_Debug.xml
generated
Normal file
19
.idea/runConfigurations/gui/Console_Debug.xml
generated
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Console Debug" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="command" value="run --package rusty_emu --bin rusty_emu" />
|
||||||
|
<option name="env">
|
||||||
|
<map />
|
||||||
|
</option>
|
||||||
|
<option name="is_redirect_input" value="false" />
|
||||||
|
<option name="is_redirect_input_value" value="" />
|
||||||
|
<option name="redirect_input_path" value="" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||||
|
<option name="with_sudo" value="false" />
|
||||||
|
<option name="cargoArguments" value="-- --console --system basic" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Console Debug" run_configuration_type="CargoCommandRunConfiguration" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
19
.idea/runConfigurations/gui/GUI_Debug.xml
generated
Normal file
19
.idea/runConfigurations/gui/GUI_Debug.xml
generated
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="GUI Debug" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="command" value="run --package rusty_emu --bin rusty_emu" />
|
||||||
|
<option name="env">
|
||||||
|
<map />
|
||||||
|
</option>
|
||||||
|
<option name="is_redirect_input" value="false" />
|
||||||
|
<option name="is_redirect_input_value" value="" />
|
||||||
|
<option name="redirect_input_path" value="" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||||
|
<option name="with_sudo" value="false" />
|
||||||
|
<option name="cargoArguments" value="-- --gui --system basic" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="GUI Debug" run_configuration_type="CargoCommandRunConfiguration" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
12
.idea/rusty_emu.iml
generated
Normal file
12
.idea/rusty_emu.iml
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
.kilocode/mcp.json
Normal file
11
.kilocode/mcp.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"jetbrains": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@jetbrains/mcp-proxy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3880
Cargo.lock
generated
Normal file
3880
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,7 @@
|
||||||
},
|
},
|
||||||
"CLOCK": {
|
"CLOCK": {
|
||||||
"single": {
|
"single": {
|
||||||
"component_type": "generic_clock",
|
"component_type": "two_phase_clock",
|
||||||
"name": "CLOCK_750KHZ",
|
"name": "CLOCK_750KHZ",
|
||||||
"properties": {
|
"properties": {
|
||||||
"frequency": 750000
|
"frequency": 750000
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
"connection_type": "clock",
|
"connection_type": "clock",
|
||||||
"source": {
|
"source": {
|
||||||
"component": "CLOCK_750KHZ",
|
"component": "CLOCK_750KHZ",
|
||||||
"pin": "CLK"
|
"pin": "PHI1"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
"connection_type": "clock",
|
"connection_type": "clock",
|
||||||
"source": {
|
"source": {
|
||||||
"component": "CLOCK_750KHZ",
|
"component": "CLOCK_750KHZ",
|
||||||
"pin": "CLK"
|
"pin": "PHI2"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
"connection_type": "control",
|
"connection_type": "control",
|
||||||
"source": {
|
"source": {
|
||||||
"component": "CPU_4004",
|
"component": "CPU_4004",
|
||||||
"pin": "CM_ROM"
|
"pin": "CM"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
|
|
@ -191,7 +191,7 @@
|
||||||
"connection_type": "control",
|
"connection_type": "control",
|
||||||
"source": {
|
"source": {
|
||||||
"component": "CPU_4004",
|
"component": "CPU_4004",
|
||||||
"pin": "CM_RAM"
|
"pin": "CM"
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,19 +46,19 @@ impl TimingConstants {
|
||||||
/// This is the standard access time for ROM and RAM operations
|
/// This is the standard access time for ROM and RAM operations
|
||||||
/// based on the original Intel 4004 timing specifications.
|
/// based on the original Intel 4004 timing specifications.
|
||||||
pub const DEFAULT_ACCESS_TIME: Duration = Duration::from_nanos(500); // 500ns default
|
pub const DEFAULT_ACCESS_TIME: Duration = Duration::from_nanos(500); // 500ns default
|
||||||
|
|
||||||
/// Fast access time for shift register operations (200ns)
|
/// Fast access time for shift register operations (200ns)
|
||||||
///
|
///
|
||||||
/// The Intel 4003 shift register has faster access times compared to
|
/// The Intel 4003 shift register has faster access times compared to
|
||||||
/// ROM and RAM components due to its simpler design.
|
/// ROM and RAM components due to its simpler design.
|
||||||
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
|
pub const FAST_ACCESS_TIME: Duration = Duration::from_nanos(200); // 200ns for shift registers
|
||||||
|
|
||||||
/// Address setup time before clock edge (100ns)
|
/// Address setup time before clock edge (100ns)
|
||||||
///
|
///
|
||||||
/// Minimum time that address must be stable before the clock edge
|
/// Minimum time that address must be stable before the clock edge
|
||||||
/// to ensure proper latching by the component.
|
/// to ensure proper latching by the component.
|
||||||
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
|
pub const ADDRESS_SETUP: Duration = Duration::from_nanos(100); // Address setup time
|
||||||
|
|
||||||
/// Data valid delay after clock edge (200ns)
|
/// Data valid delay after clock edge (200ns)
|
||||||
///
|
///
|
||||||
/// Time from clock edge until data is guaranteed to be valid on the bus
|
/// Time from clock edge until data is guaranteed to be valid on the bus
|
||||||
|
|
@ -352,19 +352,19 @@ pub enum Intel400xComponentType {
|
||||||
/// The 4001 is a 256-byte ROM chip that stores program instructions.
|
/// The 4001 is a 256-byte ROM chip that stores program instructions.
|
||||||
/// It responds to address transmission and provides read-only data access.
|
/// It responds to address transmission and provides read-only data access.
|
||||||
Intel4001,
|
Intel4001,
|
||||||
|
|
||||||
/// Intel 4002 RAM (Random Access Memory) chip
|
/// Intel 4002 RAM (Random Access Memory) chip
|
||||||
///
|
///
|
||||||
/// The 4002 is a 320-bit RAM chip organized as four registers of 20 bits each.
|
/// The 4002 is a 320-bit RAM chip organized as four registers of 20 bits each.
|
||||||
/// It supports both read and write operations and includes I/O ports.
|
/// It supports both read and write operations and includes I/O ports.
|
||||||
Intel4002,
|
Intel4002,
|
||||||
|
|
||||||
/// Intel 4003 Shift Register chip
|
/// Intel 4003 Shift Register chip
|
||||||
///
|
///
|
||||||
/// The 4003 is a 10-bit static shift register used for I/O expansion.
|
/// The 4003 is a 10-bit static shift register used for I/O expansion.
|
||||||
/// It operates differently from memory chips and ignores address transmission.
|
/// It operates differently from memory chips and ignores address transmission.
|
||||||
Intel4003,
|
Intel4003,
|
||||||
|
|
||||||
/// Intel 4004 CPU (Central Processing Unit) chip
|
/// Intel 4004 CPU (Central Processing Unit) chip
|
||||||
///
|
///
|
||||||
/// The 4004 is the 4-bit microprocessor that controls the system.
|
/// The 4004 is the 4-bit microprocessor that controls the system.
|
||||||
|
|
@ -383,25 +383,24 @@ pub enum Intel400xComponentType {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Intel400xUnifiedState {
|
pub enum Intel400xUnifiedState {
|
||||||
// Common states (shared across all components)
|
// Common states (shared across all components)
|
||||||
|
|
||||||
/// Idle state - component is waiting for operation
|
/// Idle state - component is waiting for operation
|
||||||
///
|
///
|
||||||
/// In this state, the component is not actively processing any operation
|
/// In this state, the component is not actively processing any operation
|
||||||
/// and is waiting for a chip select or other control signal.
|
/// and is waiting for a chip select or other control signal.
|
||||||
Idle,
|
Idle,
|
||||||
|
|
||||||
/// Address phase - latching address from CPU
|
/// Address phase - latching address from CPU
|
||||||
///
|
///
|
||||||
/// The component is receiving address information in two 4-bit phases
|
/// The component is receiving address information in two 4-bit phases
|
||||||
/// (high nibble first, then low nibble) from the CPU.
|
/// (high nibble first, then low nibble) from the CPU.
|
||||||
AddressPhase,
|
AddressPhase,
|
||||||
|
|
||||||
/// Wait latency - waiting for access time to elapse
|
/// Wait latency - waiting for access time to elapse
|
||||||
///
|
///
|
||||||
/// After receiving the complete address, the component waits for the
|
/// After receiving the complete address, the component waits for the
|
||||||
/// required access time before proceeding with the operation.
|
/// required access time before proceeding with the operation.
|
||||||
WaitLatency,
|
WaitLatency,
|
||||||
|
|
||||||
/// Drive data - transferring data on the bus
|
/// Drive data - transferring data on the bus
|
||||||
///
|
///
|
||||||
/// The component is actively driving data onto the bus (for read operations)
|
/// The component is actively driving data onto the bus (for read operations)
|
||||||
|
|
@ -409,21 +408,20 @@ pub enum Intel400xUnifiedState {
|
||||||
DriveData,
|
DriveData,
|
||||||
|
|
||||||
// Component-specific states
|
// Component-specific states
|
||||||
|
|
||||||
// Intel 4002 (RAM) specific states
|
// Intel 4002 (RAM) specific states
|
||||||
|
|
||||||
/// Read data state - RAM is reading from memory
|
/// Read data state - RAM is reading from memory
|
||||||
///
|
///
|
||||||
/// The RAM component is reading data from the specified address and
|
/// The RAM component is reading data from the specified address and
|
||||||
/// preparing to drive it onto the data bus.
|
/// preparing to drive it onto the data bus.
|
||||||
ReadData,
|
ReadData,
|
||||||
|
|
||||||
/// Write data state - RAM is writing to memory
|
/// Write data state - RAM is writing to memory
|
||||||
///
|
///
|
||||||
/// The RAM component is accepting data from the bus and writing it
|
/// The RAM component is accepting data from the bus and writing it
|
||||||
/// to the specified memory location.
|
/// to the specified memory location.
|
||||||
WriteData,
|
WriteData,
|
||||||
|
|
||||||
/// Output port state - RAM is driving I/O ports
|
/// Output port state - RAM is driving I/O ports
|
||||||
///
|
///
|
||||||
/// The RAM component is outputting data to its I/O ports rather than
|
/// The RAM component is outputting data to its I/O ports rather than
|
||||||
|
|
@ -431,13 +429,12 @@ pub enum Intel400xUnifiedState {
|
||||||
OutputPort,
|
OutputPort,
|
||||||
|
|
||||||
// Intel 4003 (Shift Register) specific states
|
// Intel 4003 (Shift Register) specific states
|
||||||
|
|
||||||
/// Shift data state - shifting register contents
|
/// Shift data state - shifting register contents
|
||||||
///
|
///
|
||||||
/// The 4003 shift register is shifting its contents serially,
|
/// The 4003 shift register is shifting its contents serially,
|
||||||
/// either in or out depending on the operation.
|
/// either in or out depending on the operation.
|
||||||
ShiftData,
|
ShiftData,
|
||||||
|
|
||||||
/// Load data state - loading parallel data
|
/// Load data state - loading parallel data
|
||||||
///
|
///
|
||||||
/// The 4003 shift register is loading data in parallel format
|
/// The 4003 shift register is loading data in parallel format
|
||||||
|
|
@ -445,13 +442,12 @@ pub enum Intel400xUnifiedState {
|
||||||
LoadData,
|
LoadData,
|
||||||
|
|
||||||
// Intel 4004 (CPU) specific states
|
// Intel 4004 (CPU) specific states
|
||||||
|
|
||||||
/// Fetch instruction state - CPU is fetching instruction
|
/// Fetch instruction state - CPU is fetching instruction
|
||||||
///
|
///
|
||||||
/// The CPU is fetching the next instruction from memory and
|
/// The CPU is fetching the next instruction from memory and
|
||||||
/// preparing to decode and execute it.
|
/// preparing to decode and execute it.
|
||||||
FetchInstruction,
|
FetchInstruction,
|
||||||
|
|
||||||
/// Execute instruction state - CPU is executing instruction
|
/// Execute instruction state - CPU is executing instruction
|
||||||
///
|
///
|
||||||
/// The CPU is executing the currently fetched instruction and
|
/// The CPU is executing the currently fetched instruction and
|
||||||
|
|
@ -474,79 +470,79 @@ pub enum Intel400xEvent {
|
||||||
/// Generated when the first phase of the two-phase clock transitions
|
/// Generated when the first phase of the two-phase clock transitions
|
||||||
/// from low to high. Used for timing control in various operations.
|
/// from low to high. Used for timing control in various operations.
|
||||||
ClockPhi1Rising,
|
ClockPhi1Rising,
|
||||||
|
|
||||||
/// Φ1 (Phase 1) clock falling edge
|
/// Φ1 (Phase 1) clock falling edge
|
||||||
///
|
///
|
||||||
/// Generated when the first phase of the two-phase clock transitions
|
/// Generated when the first phase of the two-phase clock transitions
|
||||||
/// from high to low. Often triggers address latching operations.
|
/// from high to low. Often triggers address latching operations.
|
||||||
ClockPhi1Falling,
|
ClockPhi1Falling,
|
||||||
|
|
||||||
/// Φ2 (Phase 2) clock rising edge
|
/// Φ2 (Phase 2) clock rising edge
|
||||||
///
|
///
|
||||||
/// Generated when the second phase of the two-phase clock transitions
|
/// Generated when the second phase of the two-phase clock transitions
|
||||||
/// from low to high. Used for data transfer operations.
|
/// from low to high. Used for data transfer operations.
|
||||||
ClockPhi2Rising,
|
ClockPhi2Rising,
|
||||||
|
|
||||||
/// Φ2 (Phase 2) clock falling edge
|
/// Φ2 (Phase 2) clock falling edge
|
||||||
///
|
///
|
||||||
/// Generated when the second phase of the two-phase clock transitions
|
/// Generated when the second phase of the two-phase clock transitions
|
||||||
/// from high to low. Often triggers state transitions.
|
/// from high to low. Often triggers state transitions.
|
||||||
ClockPhi2Falling,
|
ClockPhi2Falling,
|
||||||
|
|
||||||
/// SYNC signal becomes active (high)
|
/// SYNC signal becomes active (high)
|
||||||
///
|
///
|
||||||
/// The SYNC signal indicates the start of a new instruction cycle.
|
/// The SYNC signal indicates the start of a new instruction cycle.
|
||||||
/// Components use this to synchronize their operations with the CPU.
|
/// Components use this to synchronize their operations with the CPU.
|
||||||
SyncActive,
|
SyncActive,
|
||||||
|
|
||||||
/// SYNC signal becomes inactive (low)
|
/// SYNC signal becomes inactive (low)
|
||||||
///
|
///
|
||||||
/// The SYNC signal indicates the end of an instruction cycle.
|
/// The SYNC signal indicates the end of an instruction cycle.
|
||||||
/// Components may use this to reset their internal state.
|
/// Components may use this to reset their internal state.
|
||||||
SyncInactive,
|
SyncInactive,
|
||||||
|
|
||||||
/// Chip select becomes active
|
/// Chip select becomes active
|
||||||
///
|
///
|
||||||
/// Generated when a component is selected for an operation.
|
/// Generated when a component is selected for an operation.
|
||||||
/// This typically initiates a transition from Idle to AddressPhase.
|
/// This typically initiates a transition from Idle to AddressPhase.
|
||||||
ChipSelect,
|
ChipSelect,
|
||||||
|
|
||||||
/// Chip select becomes inactive
|
/// Chip select becomes inactive
|
||||||
///
|
///
|
||||||
/// Generated when a component is deselected.
|
/// Generated when a component is deselected.
|
||||||
/// This typically forces a transition back to the Idle state.
|
/// This typically forces a transition back to the Idle state.
|
||||||
ChipDeselect,
|
ChipDeselect,
|
||||||
|
|
||||||
/// RESET signal becomes active (high)
|
/// RESET signal becomes active (high)
|
||||||
///
|
///
|
||||||
/// Generated when the system reset signal is asserted.
|
/// Generated when the system reset signal is asserted.
|
||||||
/// All components transition to their initial state.
|
/// All components transition to their initial state.
|
||||||
ResetActive,
|
ResetActive,
|
||||||
|
|
||||||
/// RESET signal becomes inactive (low)
|
/// RESET signal becomes inactive (low)
|
||||||
///
|
///
|
||||||
/// Generated when the system reset signal is released.
|
/// Generated when the system reset signal is released.
|
||||||
/// Components begin normal operation from their reset state.
|
/// Components begin normal operation from their reset state.
|
||||||
ResetInactive,
|
ResetInactive,
|
||||||
|
|
||||||
/// Address latching complete
|
/// Address latching complete
|
||||||
///
|
///
|
||||||
/// Generated when both high and low address nibbles have been latched.
|
/// Generated when both high and low address nibbles have been latched.
|
||||||
/// Triggers transition from AddressPhase to WaitLatency.
|
/// Triggers transition from AddressPhase to WaitLatency.
|
||||||
AddressComplete,
|
AddressComplete,
|
||||||
|
|
||||||
/// Access latency has elapsed
|
/// Access latency has elapsed
|
||||||
///
|
///
|
||||||
/// Generated when the required access time has passed after address latching.
|
/// Generated when the required access time has passed after address latching.
|
||||||
/// Triggers transition from WaitLatency to operation-specific states.
|
/// Triggers transition from WaitLatency to operation-specific states.
|
||||||
LatencyElapsed,
|
LatencyElapsed,
|
||||||
|
|
||||||
/// Data is ready for transfer
|
/// Data is ready for transfer
|
||||||
///
|
///
|
||||||
/// Generated when data is available for reading or writing.
|
/// Generated when data is available for reading or writing.
|
||||||
/// Used in component-specific data transfer operations.
|
/// Used in component-specific data transfer operations.
|
||||||
DataReady,
|
DataReady,
|
||||||
|
|
||||||
/// Operation is complete
|
/// Operation is complete
|
||||||
///
|
///
|
||||||
/// Generated when the current operation has finished successfully.
|
/// Generated when the current operation has finished successfully.
|
||||||
|
|
@ -779,22 +775,22 @@ pub trait Intel400xComponentBehavior {
|
||||||
pub struct Intel400xStateMachine {
|
pub struct Intel400xStateMachine {
|
||||||
/// The type of component this state machine is managing
|
/// The type of component this state machine is managing
|
||||||
component_type: Intel400xComponentType,
|
component_type: Intel400xComponentType,
|
||||||
|
|
||||||
/// The current state of the state machine
|
/// The current state of the state machine
|
||||||
pub(crate) current_state: Intel400xUnifiedState,
|
pub(crate) current_state: Intel400xUnifiedState,
|
||||||
|
|
||||||
/// The previous state before the last transition
|
/// The previous state before the last transition
|
||||||
previous_state: Intel400xUnifiedState,
|
previous_state: Intel400xUnifiedState,
|
||||||
|
|
||||||
/// The high nibble (bits 7-4) of the currently latching address
|
/// The high nibble (bits 7-4) of the currently latching address
|
||||||
address_high_nibble: Option<u8>,
|
address_high_nibble: Option<u8>,
|
||||||
|
|
||||||
/// The low nibble (bits 3-0) of the currently latching address
|
/// The low nibble (bits 3-0) of the currently latching address
|
||||||
address_low_nibble: Option<u8>,
|
address_low_nibble: Option<u8>,
|
||||||
|
|
||||||
/// The time when the address was completely latched
|
/// The time when the address was completely latched
|
||||||
address_latch_time: Option<Instant>,
|
address_latch_time: Option<Instant>,
|
||||||
|
|
||||||
/// The access time to wait after address latching before data access
|
/// The access time to wait after address latching before data access
|
||||||
access_time: Duration,
|
access_time: Duration,
|
||||||
}
|
}
|
||||||
|
|
@ -1514,7 +1510,7 @@ mod tests {
|
||||||
// First need to transition to AddressPhase state
|
// First need to transition to AddressPhase state
|
||||||
process_event_direct(&mut sm, Intel400xEvent::ChipSelect);
|
process_event_direct(&mut sm, Intel400xEvent::ChipSelect);
|
||||||
assert_eq!(sm.get_state(), Intel400xUnifiedState::AddressPhase);
|
assert_eq!(sm.get_state(), Intel400xUnifiedState::AddressPhase);
|
||||||
|
|
||||||
// Now transition to WaitLatency state
|
// Now transition to WaitLatency state
|
||||||
process_event_direct(&mut sm, Intel400xEvent::AddressComplete);
|
process_event_direct(&mut sm, Intel400xEvent::AddressComplete);
|
||||||
assert_eq!(sm.get_state(), Intel400xUnifiedState::WaitLatency);
|
assert_eq!(sm.get_state(), Intel400xUnifiedState::WaitLatency);
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,19 @@ enum InstructionPhase {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
enum MemoryState {
|
enum MemoryState {
|
||||||
Idle, // No memory operation in progress
|
Idle, // No memory operation in progress
|
||||||
AddressPhase, // Currently latching address nibbles
|
AddressPhase, // Currently driving address nibbles to bus
|
||||||
WaitLatency, // Address latched, waiting for access time
|
WaitLatency, // Address sent, waiting for ROM to respond
|
||||||
DriveData, // Latency elapsed, driving data on bus
|
DriveData, // Data ready, reading from bus
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Address cycle counter for Intel 4004 timing
|
||||||
|
/// The Intel 4004 sends address in multiple cycles (A1, A2, A3)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum AddressCycle {
|
||||||
|
None, // No address cycle in progress
|
||||||
|
A1, // First address cycle - driving high nibble
|
||||||
|
A2, // Second address cycle - driving low nibble
|
||||||
|
A3, // Third address cycle - reading data from ROM
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intel 4004 instruction set enumeration
|
/// Intel 4004 instruction set enumeration
|
||||||
|
|
@ -126,8 +136,9 @@ pub struct Intel4004 {
|
||||||
|
|
||||||
// Memory operation state machine
|
// Memory operation state machine
|
||||||
memory_state: MemoryState, // Current state of memory operation
|
memory_state: MemoryState, // Current state of memory operation
|
||||||
address_high_nibble: Option<u8>, // High nibble of 8-bit address
|
address_cycle: AddressCycle, // Current address cycle (A1, A2, A3)
|
||||||
address_low_nibble: Option<u8>, // Low nibble of 8-bit address
|
address_high_nibble: Option<u8>, // High nibble of 8-bit address (for ROM, this is PC[7:4])
|
||||||
|
address_low_nibble: Option<u8>, // Low nibble of 8-bit address (for ROM, this is PC[3:0])
|
||||||
full_address_ready: bool, // Whether complete address is assembled
|
full_address_ready: bool, // Whether complete address is assembled
|
||||||
|
|
||||||
// Instruction execution state
|
// Instruction execution state
|
||||||
|
|
@ -186,6 +197,7 @@ impl Intel4004 {
|
||||||
|
|
||||||
// Memory operation state machine
|
// Memory operation state machine
|
||||||
memory_state: MemoryState::Idle,
|
memory_state: MemoryState::Idle,
|
||||||
|
address_cycle: AddressCycle::None,
|
||||||
address_high_nibble: None,
|
address_high_nibble: None,
|
||||||
address_low_nibble: None,
|
address_low_nibble: None,
|
||||||
full_address_ready: false,
|
full_address_ready: false,
|
||||||
|
|
@ -230,6 +242,7 @@ impl Intel4004 {
|
||||||
|
|
||||||
// Reset memory operation state
|
// Reset memory operation state
|
||||||
self.memory_state = MemoryState::Idle;
|
self.memory_state = MemoryState::Idle;
|
||||||
|
self.address_cycle = AddressCycle::None;
|
||||||
self.address_latch_time = None;
|
self.address_latch_time = None;
|
||||||
self.address_high_nibble = None;
|
self.address_high_nibble = None;
|
||||||
self.address_low_nibble = None;
|
self.address_low_nibble = None;
|
||||||
|
|
@ -379,24 +392,27 @@ impl Intel4004 {
|
||||||
/// Handle Φ1 rising edge - Address and control phase
|
/// Handle Φ1 rising edge - Address and control phase
|
||||||
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
||||||
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
||||||
|
///
|
||||||
|
/// Intel 4004 Address Timing:
|
||||||
|
/// - A1 cycle: CPU drives PC high nibble (bits 7-4) onto data bus
|
||||||
|
/// - A2 cycle: CPU drives PC low nibble (bits 3-0) onto data bus
|
||||||
|
/// - A3 cycle: CPU reads data from ROM (instruction byte)
|
||||||
fn handle_phi1_rising(&mut self) {
|
fn handle_phi1_rising(&mut self) {
|
||||||
// Check for memory operation start on Φ1 rising edge with SYNC high
|
// Check for memory operation start on Φ1 rising edge with SYNC high
|
||||||
let (sync, cm, _) = self.read_control_pins();
|
let (sync, cm, _) = self.read_control_pins();
|
||||||
|
|
||||||
if sync
|
if sync
|
||||||
&& cm
|
&& cm
|
||||||
&& self.memory_state == MemoryState::Idle
|
&& self.memory_state == MemoryState::Idle
|
||||||
&& self.instruction_phase == InstructionPhase::Fetch
|
&& self.instruction_phase == InstructionPhase::Fetch
|
||||||
|
&& self.address_cycle == AddressCycle::None
|
||||||
{
|
{
|
||||||
// Start memory address phase on Φ1 rising edge
|
// Start memory address phase on Φ1 rising edge
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] Starting memory operation - SYNC: {}, CM: {}, Phase: {:?}",
|
|
||||||
sync, cm, self.instruction_phase
|
|
||||||
);
|
|
||||||
self.start_memory_address_phase();
|
self.start_memory_address_phase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle memory address phase operations during Φ1
|
// Drive address nibbles onto the bus during A1 and A2 cycles
|
||||||
self.handle_memory_address_operations();
|
self.handle_address_driving();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle Φ1 falling edge - End of address phase
|
/// Handle Φ1 falling edge - End of address phase
|
||||||
|
|
@ -412,6 +428,9 @@ impl Intel4004 {
|
||||||
|
|
||||||
/// Handle Φ2 falling edge - End of data phase
|
/// Handle Φ2 falling edge - End of data phase
|
||||||
fn handle_phi2_falling(&mut self) {
|
fn handle_phi2_falling(&mut self) {
|
||||||
|
// Advance address cycle state machine at end of each clock cycle
|
||||||
|
self.advance_address_cycle();
|
||||||
|
|
||||||
// Handle memory cleanup operations when Φ2 falls
|
// Handle memory cleanup operations when Φ2 falls
|
||||||
self.handle_memory_cleanup_operations();
|
self.handle_memory_cleanup_operations();
|
||||||
}
|
}
|
||||||
|
|
@ -444,6 +463,7 @@ impl Intel4004 {
|
||||||
|
|
||||||
// Reset memory operation state
|
// Reset memory operation state
|
||||||
self.memory_state = MemoryState::Idle;
|
self.memory_state = MemoryState::Idle;
|
||||||
|
self.address_cycle = AddressCycle::None;
|
||||||
self.address_latch_time = None;
|
self.address_latch_time = None;
|
||||||
self.address_high_nibble = None;
|
self.address_high_nibble = None;
|
||||||
self.address_low_nibble = None;
|
self.address_low_nibble = None;
|
||||||
|
|
@ -467,37 +487,88 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start memory address phase
|
/// Start memory address phase - begin A1 cycle
|
||||||
fn start_memory_address_phase(&mut self) {
|
fn start_memory_address_phase(&mut self) {
|
||||||
self.memory_state = MemoryState::AddressPhase;
|
self.memory_state = MemoryState::AddressPhase;
|
||||||
|
self.address_cycle = AddressCycle::A1; // Start with A1 cycle
|
||||||
self.full_address_ready = false;
|
self.full_address_ready = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle memory address-related operations during Φ1
|
/// Handle driving address nibbles onto the data bus during A1/A2 cycles
|
||||||
fn handle_memory_address_operations(&mut self) {
|
/// This is the CPU ACTIVELY DRIVING the bus (not reading from it)
|
||||||
|
///
|
||||||
|
/// IMPORTANT: Each address cycle (A1, A2) spans a full PHI1+PHI2 clock period
|
||||||
|
/// - A1: Drive high nibble during PHI1, hold during PHI2, transition to A2 on next PHI1
|
||||||
|
/// - A2: Drive low nibble during PHI1, hold during PHI2, transition to A3 on next PHI1
|
||||||
|
/// - A3: Tri-state bus, ROM drives data
|
||||||
|
fn handle_address_driving(&mut self) {
|
||||||
match self.memory_state {
|
match self.memory_state {
|
||||||
|
MemoryState::AddressPhase => {
|
||||||
|
let pc = self.program_counter.value();
|
||||||
|
|
||||||
|
match self.address_cycle {
|
||||||
|
AddressCycle::A1 => {
|
||||||
|
// A1 cycle: Drive PC high nibble (bits 7-4) onto data bus
|
||||||
|
let pc_high = ((pc >> 4) & 0x0F) as u8;
|
||||||
|
self.write_data_bus(pc_high);
|
||||||
|
|
||||||
|
// NOTE: We do NOT transition to A2 here!
|
||||||
|
// The transition happens in handle_phi2_falling after this cycle completes
|
||||||
|
}
|
||||||
|
AddressCycle::A2 => {
|
||||||
|
// A2 cycle: Drive PC low nibble (bits 3-0) onto data bus
|
||||||
|
let pc_low = (pc & 0x0F) as u8;
|
||||||
|
self.write_data_bus(pc_low);
|
||||||
|
|
||||||
|
// NOTE: We do NOT transition to A3 here!
|
||||||
|
// The transition happens in handle_phi2_falling after this cycle completes
|
||||||
|
}
|
||||||
|
AddressCycle::A3 | AddressCycle::None => {
|
||||||
|
// A3 or None: Should not be in AddressPhase, tri-state bus
|
||||||
|
self.tri_state_data_bus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MemoryState::Idle => {
|
MemoryState::Idle => {
|
||||||
// In idle state, ensure bus is tri-stated
|
// In idle state, ensure bus is tri-stated
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryState::AddressPhase => {
|
|
||||||
// Currently latching address nibbles during Φ1
|
|
||||||
self.handle_address_latching();
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryState::WaitLatency => {
|
MemoryState::WaitLatency => {
|
||||||
// Address latched, waiting for access latency
|
// Address sent, waiting for ROM to respond
|
||||||
|
// Tri-state the bus so ROM can drive it
|
||||||
|
self.tri_state_data_bus();
|
||||||
self.handle_latency_wait();
|
self.handle_latency_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryState::DriveData => {
|
MemoryState::DriveData => {
|
||||||
// Data phase should be handled by Φ2, not Φ1
|
// Data phase - ROM should be driving the bus, CPU reads
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advance address cycle state machine at end of clock cycle (PHI2 falling)
|
||||||
|
/// This ensures each address phase lasts a complete clock cycle
|
||||||
|
fn advance_address_cycle(&mut self) {
|
||||||
|
if self.memory_state == MemoryState::AddressPhase {
|
||||||
|
match self.address_cycle {
|
||||||
|
AddressCycle::A1 => {
|
||||||
|
// A1 complete, advance to A2
|
||||||
|
self.address_cycle = AddressCycle::A2;
|
||||||
|
}
|
||||||
|
AddressCycle::A2 => {
|
||||||
|
// A2 complete, advance to A3 (wait for ROM data)
|
||||||
|
self.address_cycle = AddressCycle::A3;
|
||||||
|
self.memory_state = MemoryState::WaitLatency;
|
||||||
|
self.address_latch_time = Some(Instant::now());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle memory data-related operations during Φ2
|
/// Handle memory data-related operations during Φ2
|
||||||
fn handle_memory_data_operations(&mut self) {
|
fn handle_memory_data_operations(&mut self) {
|
||||||
match self.memory_state {
|
match self.memory_state {
|
||||||
|
|
@ -537,41 +608,6 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle address nibble latching during address phase
|
|
||||||
fn handle_address_latching(&mut self) {
|
|
||||||
let nibble = self.read_data_bus();
|
|
||||||
|
|
||||||
if self.address_high_nibble.is_none() {
|
|
||||||
// First cycle: latch high nibble (bits 7-4)
|
|
||||||
self.address_high_nibble = Some(nibble);
|
|
||||||
} else if self.address_low_nibble.is_none() {
|
|
||||||
// Second cycle: latch low nibble (bits 3-0) and transition to latency wait
|
|
||||||
self.address_low_nibble = Some(nibble);
|
|
||||||
self.assemble_full_address();
|
|
||||||
self.start_latency_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assemble complete 8-bit address from high and low nibbles
|
|
||||||
fn assemble_full_address(&mut self) {
|
|
||||||
if let (Some(high), Some(low)) = (self.address_high_nibble, self.address_low_nibble) {
|
|
||||||
// Assemble 8-bit address: (high << 4) | low
|
|
||||||
self.address_latch = (high << 4) | low;
|
|
||||||
self.full_address_ready = true;
|
|
||||||
self.address_latch_time = Some(Instant::now());
|
|
||||||
|
|
||||||
// Clear nibble storage for next address
|
|
||||||
self.address_high_nibble = None;
|
|
||||||
self.address_low_nibble = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transition to latency wait state
|
|
||||||
fn start_latency_wait(&mut self) {
|
|
||||||
self.memory_state = MemoryState::WaitLatency;
|
|
||||||
self.address_latch_time = Some(Instant::now());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle latency timing during wait state
|
/// Handle latency timing during wait state
|
||||||
fn handle_latency_wait(&mut self) {
|
fn handle_latency_wait(&mut self) {
|
||||||
if let Some(latch_time) = self.address_latch_time {
|
if let Some(latch_time) = self.address_latch_time {
|
||||||
|
|
@ -596,12 +632,6 @@ impl Intel4004 {
|
||||||
// All conditions met: drive data on bus
|
// All conditions met: drive data on bus
|
||||||
let data = self.data_latch;
|
let data = self.data_latch;
|
||||||
self.write_data_bus(data);
|
self.write_data_bus(data);
|
||||||
|
|
||||||
if self.cycle_count % 1000 == 0 {
|
|
||||||
// Log every 1000 cycles
|
|
||||||
println!("DEBUG: [{}] CPU State | PC: 0x{:03X} | Cycles: {} | ACC: 0x{:X} | SYNC: {} | CM: {} | Ready: {}",
|
|
||||||
self.base.name(), self.program_counter.value(), self.cycle_count, self.accumulator, sync, cm, self.full_address_ready);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Conditions not met, tri-state
|
// Conditions not met, tri-state
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
|
|
@ -611,6 +641,7 @@ impl Intel4004 {
|
||||||
/// Reset memory state machine to idle
|
/// Reset memory state machine to idle
|
||||||
fn return_to_idle(&mut self) {
|
fn return_to_idle(&mut self) {
|
||||||
self.memory_state = MemoryState::Idle;
|
self.memory_state = MemoryState::Idle;
|
||||||
|
self.address_cycle = AddressCycle::None;
|
||||||
self.address_latch_time = None;
|
self.address_latch_time = None;
|
||||||
self.address_high_nibble = None;
|
self.address_high_nibble = None;
|
||||||
self.address_low_nibble = None;
|
self.address_low_nibble = None;
|
||||||
|
|
@ -997,67 +1028,44 @@ impl Intel4004 {
|
||||||
// I/O and RAM Instructions
|
// I/O and RAM Instructions
|
||||||
Instruction::Wrm => {
|
Instruction::Wrm => {
|
||||||
// Write accumulator to RAM at current RAM address
|
// Write accumulator to RAM at current RAM address
|
||||||
// This would interface with RAM chips - for now, just log
|
// This would interface with RAM chips
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] WRM - Write ACC 0x{:X} to RAM address 0x{:02X}",
|
|
||||||
self.accumulator, self.address_latch
|
|
||||||
);
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Wmp => {
|
Instruction::Wmp => {
|
||||||
// Write memory pointer - set RAM address from accumulator
|
// Write memory pointer - set RAM address from accumulator
|
||||||
self.address_latch = self.accumulator;
|
self.address_latch = self.accumulator;
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] WMP - Set RAM address to 0x{:02X}",
|
|
||||||
self.address_latch
|
|
||||||
);
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Wrr => {
|
Instruction::Wrr => {
|
||||||
// Write ROM port and register - handled by memory interface
|
// Write ROM port and register - handled by memory interface
|
||||||
println!("DEBUG: [CPU] WRR - Write to ROM port");
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Wpm => {
|
Instruction::Wpm => {
|
||||||
// Write program memory - handled by memory interface
|
// Write program memory - handled by memory interface
|
||||||
println!("DEBUG: [CPU] WPM - Write to program memory");
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Adm => {
|
Instruction::Adm => {
|
||||||
// Add from memory - add RAM data to accumulator
|
// Add from memory - add RAM data to accumulator
|
||||||
// This would read from RAM and add to accumulator
|
// This would read from RAM and add to accumulator
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] ADM - Add from RAM address 0x{:02X}",
|
|
||||||
self.address_latch
|
|
||||||
);
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Sbm => {
|
Instruction::Sbm => {
|
||||||
// Subtract from memory - subtract RAM data from accumulator
|
// Subtract from memory - subtract RAM data from accumulator
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] SBM - Subtract from RAM address 0x{:02X}",
|
|
||||||
self.address_latch
|
|
||||||
);
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Rdm => {
|
Instruction::Rdm => {
|
||||||
// Read memory - read RAM data to accumulator
|
// Read memory - read RAM data to accumulator
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] RDM - Read from RAM address 0x{:02X}",
|
|
||||||
self.address_latch
|
|
||||||
);
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::Rdr => {
|
Instruction::Rdr => {
|
||||||
// Read ROM port and register - handled by memory interface
|
// Read ROM port and register - handled by memory interface
|
||||||
println!("DEBUG: [CPU] RDR - Read from ROM port");
|
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1195,14 +1203,7 @@ impl Intel4004 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.instruction_phase == InstructionPhase::Execute {
|
if self.instruction_phase == InstructionPhase::Execute {
|
||||||
let old_pc = self.program_counter.value();
|
|
||||||
self.execute_instruction();
|
self.execute_instruction();
|
||||||
let new_pc = self.program_counter.value();
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"DEBUG: [TEST] Single Execute | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X}",
|
|
||||||
old_pc, new_pc, self.accumulator
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1219,12 +1220,6 @@ impl Intel4004 {
|
||||||
self.index_registers[i] = byte & 0x0F;
|
self.index_registers[i] = byte & 0x0F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
|
||||||
"DEBUG: [{}] Loaded test program: {:02X?}",
|
|
||||||
self.base.name(),
|
|
||||||
program
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1440,58 +1435,21 @@ impl Component for Intel4004 {
|
||||||
self.prev_phi2 = phi2;
|
self.prev_phi2 = phi2;
|
||||||
|
|
||||||
if phi1_rising {
|
if phi1_rising {
|
||||||
// Reduced debug output - only show occasional cycles
|
|
||||||
// Use atomic counter instead of static mutable for thread safety
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static CYCLE_COUNT: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let count = CYCLE_COUNT.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if count % 1000 == 0 {
|
|
||||||
let (sync, cm, _) = self.read_control_pins();
|
|
||||||
println!("DEBUG: [CPU] Φ1 Rising Edge - PC: 0x{:03X}, ACC: 0x{:X}, Phase: {:?}, SYNC: {}, CM: {} (cycle {})",
|
|
||||||
self.program_counter.value(), self.accumulator, self.instruction_phase, sync, cm, count);
|
|
||||||
}
|
|
||||||
self.handle_phi1_rising();
|
self.handle_phi1_rising();
|
||||||
}
|
}
|
||||||
|
|
||||||
if phi1_falling {
|
if phi1_falling {
|
||||||
// Φ1 Falling Edge: End of address phase
|
// Φ1 Falling Edge: End of address phase
|
||||||
// Reduced debug output - only show occasional cycles
|
|
||||||
// Use atomic counter instead of static mutable for thread safety
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static CYCLE_COUNT: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let count = CYCLE_COUNT.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if count % 1000 == 0 {
|
|
||||||
println!("DEBUG: [CPU] Φ1 Falling Edge (cycle {})", count);
|
|
||||||
}
|
|
||||||
self.handle_phi1_falling();
|
self.handle_phi1_falling();
|
||||||
}
|
}
|
||||||
|
|
||||||
if phi2_rising {
|
if phi2_rising {
|
||||||
// Φ2 Rising Edge: Data phase (peripherals drive bus) - handle data operations
|
// Φ2 Rising Edge: Data phase (peripherals drive bus) - handle data operations
|
||||||
// Reduced debug output - only show occasional cycles
|
|
||||||
// Use atomic counter instead of static mutable for thread safety
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static CYCLE_COUNT: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let count = CYCLE_COUNT.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if count % 1000 == 0 {
|
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] Φ2 Rising Edge - Memory State: {:?} (cycle {})",
|
|
||||||
self.memory_state, count
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.handle_phi2_rising();
|
self.handle_phi2_rising();
|
||||||
}
|
}
|
||||||
|
|
||||||
if phi2_falling {
|
if phi2_falling {
|
||||||
// Φ2 Falling Edge: End of data phase - tri-state bus and return to idle
|
// Φ2 Falling Edge: End of data phase - tri-state bus and return to idle
|
||||||
// Reduced debug output - only show occasional cycles
|
|
||||||
// Use atomic counter instead of static mutable for thread safety
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
static CYCLE_COUNT: AtomicU32 = AtomicU32::new(0);
|
|
||||||
let count = CYCLE_COUNT.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if count % 1000 == 0 {
|
|
||||||
println!("DEBUG: [CPU] Φ2 Falling Edge (cycle {})", count);
|
|
||||||
}
|
|
||||||
self.handle_phi2_falling();
|
self.handle_phi2_falling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1503,18 +1461,10 @@ impl Component for Intel4004 {
|
||||||
{
|
{
|
||||||
self.set_sync(true);
|
self.set_sync(true);
|
||||||
self.set_cm(true);
|
self.set_cm(true);
|
||||||
println!("DEBUG: [CPU] Setting signals - SYNC: HIGH, CM: HIGH");
|
|
||||||
} else {
|
} else {
|
||||||
self.set_sync(false);
|
self.set_sync(false);
|
||||||
println!("DEBUG: [CPU] Setting SYNC: LOW");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] Instruction phase: {:?}, PC: 0x{:03X}, State: {:?}",
|
|
||||||
self.instruction_phase,
|
|
||||||
self.program_counter.value(),
|
|
||||||
self.memory_state
|
|
||||||
);
|
|
||||||
match self.instruction_phase {
|
match self.instruction_phase {
|
||||||
InstructionPhase::Fetch => {
|
InstructionPhase::Fetch => {
|
||||||
// Check if we're waiting for an operand (two-instruction format)
|
// Check if we're waiting for an operand (two-instruction format)
|
||||||
|
|
@ -1531,14 +1481,10 @@ impl Component for Intel4004 {
|
||||||
Instruction::JunHigh(addr_high) => {
|
Instruction::JunHigh(addr_high) => {
|
||||||
let addr = ((addr_high as u16) << 4) | (operand as u16);
|
let addr = ((addr_high as u16) << 4) | (operand as u16);
|
||||||
self.current_op = Instruction::Jun(addr);
|
self.current_op = Instruction::Jun(addr);
|
||||||
println!("DEBUG: [CPU] Fetched JUN operand 0x{:02X} -> complete address 0x{:03X}",
|
|
||||||
operand, addr);
|
|
||||||
}
|
}
|
||||||
Instruction::JmsHigh(addr_high) => {
|
Instruction::JmsHigh(addr_high) => {
|
||||||
let addr = ((addr_high as u16) << 4) | (operand as u16);
|
let addr = ((addr_high as u16) << 4) | (operand as u16);
|
||||||
self.current_op = Instruction::Jms(addr);
|
self.current_op = Instruction::Jms(addr);
|
||||||
println!("DEBUG: [CPU] Fetched JMS operand 0x{:02X} -> complete address 0x{:03X}",
|
|
||||||
operand, addr);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -1549,13 +1495,8 @@ impl Component for Intel4004 {
|
||||||
} else {
|
} else {
|
||||||
// Normal instruction fetch
|
// Normal instruction fetch
|
||||||
let (sync, cm, _) = self.read_control_pins();
|
let (sync, cm, _) = self.read_control_pins();
|
||||||
println!(
|
|
||||||
"DEBUG: [CPU] Normal fetch - SYNC: {}, CM: {}, State: {:?}",
|
|
||||||
sync, cm, self.memory_state
|
|
||||||
);
|
|
||||||
if sync && cm {
|
if sync && cm {
|
||||||
// Memory access - fetch instruction
|
// Memory access - fetch instruction
|
||||||
println!("DEBUG: [CPU] Fetching instruction from memory - PC: 0x{:03X}, State: {:?}", self.program_counter.value(), self.memory_state);
|
|
||||||
if self.memory_state == MemoryState::DriveData {
|
if self.memory_state == MemoryState::DriveData {
|
||||||
let instruction = self.read_data_bus();
|
let instruction = self.read_data_bus();
|
||||||
self.current_instruction = instruction;
|
self.current_instruction = instruction;
|
||||||
|
|
@ -1567,17 +1508,12 @@ impl Component for Intel4004 {
|
||||||
// Two-instruction format - wait for operand
|
// Two-instruction format - wait for operand
|
||||||
self.current_op = decoded_op;
|
self.current_op = decoded_op;
|
||||||
// Don't advance PC yet - wait for operand
|
// Don't advance PC yet - wait for operand
|
||||||
println!("DEBUG: [CPU] Fetched two-instruction opcode 0x{:02X} from PC 0x{:03X}",
|
|
||||||
instruction, self.program_counter.value());
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Single instruction - execute immediately
|
// Single instruction - execute immediately
|
||||||
self.current_op = decoded_op;
|
self.current_op = decoded_op;
|
||||||
self.instruction_phase = InstructionPhase::Execute;
|
self.instruction_phase = InstructionPhase::Execute;
|
||||||
self.program_counter.inc();
|
self.program_counter.inc();
|
||||||
|
|
||||||
println!("DEBUG: [CPU] Fetched single instruction 0x{:02X} from PC 0x{:03X} | ACC: 0x{:X}",
|
|
||||||
instruction, self.program_counter.value(), self.accumulator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1594,19 +1530,7 @@ impl Component for Intel4004 {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Execute the current instruction
|
// Execute the current instruction
|
||||||
let old_pc = self.program_counter.value();
|
|
||||||
self.execute_instruction();
|
self.execute_instruction();
|
||||||
let new_pc = self.program_counter.value();
|
|
||||||
|
|
||||||
// Debug: Show instruction execution details (reduced frequency)
|
|
||||||
static mut EXEC_COUNT: u32 = 0;
|
|
||||||
unsafe {
|
|
||||||
EXEC_COUNT += 1;
|
|
||||||
if EXEC_COUNT % 10 == 0 {
|
|
||||||
println!("DEBUG: [CPU] Executed {:?} | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
|
|
||||||
self.current_op, old_pc, new_pc, self.accumulator, self.full_address_ready);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.instruction_phase = InstructionPhase::Fetch;
|
self.instruction_phase = InstructionPhase::Fetch;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -391,11 +391,12 @@ impl Intel4001 {
|
||||||
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
||||||
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
||||||
/// Focus: Address latching, reset, I/O operations
|
/// Focus: Address latching, reset, I/O operations
|
||||||
|
///
|
||||||
|
/// Intel 4004 Address Timing (CPU drives address during PHI1):
|
||||||
|
/// - A1 cycle: CPU drives PC high nibble on PHI1 rising
|
||||||
|
/// - A2 cycle: CPU drives PC low nibble on PHI1 rising
|
||||||
|
/// - ROM latches each nibble when it sees them on the bus during AddressPhase
|
||||||
fn handle_phi1_rising(&mut self) {
|
fn handle_phi1_rising(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Φ1 rising edge detected, current state: {:?}",
|
|
||||||
self.state_machine.get_state()
|
|
||||||
);
|
|
||||||
// Handle system reset first (highest priority)
|
// Handle system reset first (highest priority)
|
||||||
self.handle_reset("RESET");
|
self.handle_reset("RESET");
|
||||||
|
|
||||||
|
|
@ -407,17 +408,23 @@ impl Intel4001 {
|
||||||
let sync = self.read_sync_pin();
|
let sync = self.read_sync_pin();
|
||||||
let chip_select = self.read_cm_rom_pin();
|
let chip_select = self.read_cm_rom_pin();
|
||||||
let io_select = self.read_ci_pin();
|
let io_select = self.read_ci_pin();
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Φ1 rising - SYNC: {}, CM: {}, CI: {}",
|
|
||||||
sync, chip_select, io_select
|
|
||||||
);
|
|
||||||
|
|
||||||
if sync && chip_select && !io_select {
|
if sync && chip_select && !io_select {
|
||||||
// Start memory address phase on Φ1 rising edge (ROM access)
|
// Start memory address phase on Φ1 rising edge (ROM access)
|
||||||
// Only start a new memory operation if we're currently idle
|
// Only start a new memory operation if we're currently idle
|
||||||
if self.state_machine.get_state() == Intel400xUnifiedState::Idle {
|
if self.state_machine.get_state() == Intel400xUnifiedState::Idle {
|
||||||
self.process_chip_select();
|
self.process_chip_select();
|
||||||
println!("DEBUG: [ROM] Starting memory operation");
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Latch address nibbles during PHI1 when CPU is driving them
|
||||||
|
// The CPU drives address nibbles on PHI1 rising, so we must latch here
|
||||||
|
if self.state_machine.get_state() == Intel400xUnifiedState::AddressPhase {
|
||||||
|
let nibble = self.read_data_bus() & 0x0F;
|
||||||
|
|
||||||
|
if self.state_machine.handle_address_nibble(nibble) {
|
||||||
|
// Address is complete (both nibbles latched), transition to latency wait
|
||||||
|
self.process_address_complete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,10 +447,6 @@ impl Intel4001 {
|
||||||
/// Hardware: Φ2 high = Peripherals drive bus with data
|
/// Hardware: Φ2 high = Peripherals drive bus with data
|
||||||
/// Focus: Data driving operations
|
/// Focus: Data driving operations
|
||||||
fn handle_phi2_rising(&mut self) {
|
fn handle_phi2_rising(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Φ2 rising edge detected, current state: {:?}",
|
|
||||||
self.state_machine.get_state()
|
|
||||||
);
|
|
||||||
// Execute current state logic
|
// Execute current state logic
|
||||||
self.execute_state_machine();
|
self.execute_state_machine();
|
||||||
}
|
}
|
||||||
|
|
@ -453,10 +456,6 @@ impl Intel4001 {
|
||||||
/// Data is held on bus during Φ2 high, tri-stated when Φ2 falls
|
/// Data is held on bus during Φ2 high, tri-stated when Φ2 falls
|
||||||
/// Focus: Clean up and return to idle state
|
/// Focus: Clean up and return to idle state
|
||||||
fn handle_phi2_falling(&mut self) {
|
fn handle_phi2_falling(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Φ2 falling edge detected, current state: {:?}",
|
|
||||||
self.state_machine.get_state()
|
|
||||||
);
|
|
||||||
match self.state_machine.get_state() {
|
match self.state_machine.get_state() {
|
||||||
Intel400xUnifiedState::DriveData => {
|
Intel400xUnifiedState::DriveData => {
|
||||||
// End of data phase - tri-state bus and return to idle
|
// End of data phase - tri-state bus and return to idle
|
||||||
|
|
@ -629,10 +628,6 @@ impl Intel4001 {
|
||||||
/// Focus: Address latching and setup operations
|
/// Focus: Address latching and setup operations
|
||||||
#[allow(dead_code)] // This method is kept for compatibility but not currently used
|
#[allow(dead_code)] // This method is kept for compatibility but not currently used
|
||||||
fn handle_memory_address_operations(&mut self) {
|
fn handle_memory_address_operations(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: handle_memory_address_operations called, current state: {:?}",
|
|
||||||
self.state_machine.get_state()
|
|
||||||
);
|
|
||||||
match self.state_machine.get_state() {
|
match self.state_machine.get_state() {
|
||||||
Intel400xUnifiedState::Idle => {
|
Intel400xUnifiedState::Idle => {
|
||||||
// In idle state, ensure bus is tri-stated
|
// In idle state, ensure bus is tri-stated
|
||||||
|
|
@ -706,10 +701,6 @@ impl Intel4001 {
|
||||||
/// Handle address nibble latching during address phase
|
/// Handle address nibble latching during address phase
|
||||||
/// Hardware: CPU provides 8-bit address as two 4-bit nibbles
|
/// Hardware: CPU provides 8-bit address as two 4-bit nibbles
|
||||||
fn handle_address_latching(&mut self) {
|
fn handle_address_latching(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: handle_address_latching called, current state: {:?}",
|
|
||||||
self.state_machine.current_state
|
|
||||||
);
|
|
||||||
// Use common address latching functionality
|
// Use common address latching functionality
|
||||||
let nibble = self.read_data_bus() & 0x0F; // Only 4 bits are valid for address nibbles
|
let nibble = self.read_data_bus() & 0x0F; // Only 4 bits are valid for address nibbles
|
||||||
|
|
||||||
|
|
@ -723,15 +714,10 @@ impl Intel4001 {
|
||||||
/// Hardware: ROM needs 500ns to access data after address is latched
|
/// Hardware: ROM needs 500ns to access data after address is latched
|
||||||
#[allow(dead_code)] // This method is kept for compatibility with legacy code
|
#[allow(dead_code)] // This method is kept for compatibility with legacy code
|
||||||
fn handle_latency_wait(&mut self) {
|
fn handle_latency_wait(&mut self) {
|
||||||
println!(
|
|
||||||
"DEBUG: handle_latency_wait called, state: {:?}",
|
|
||||||
self.state_machine.current_state
|
|
||||||
);
|
|
||||||
// This is now handled by state machine
|
// This is now handled by state machine
|
||||||
if self.state_machine.is_latency_elapsed() {
|
if self.state_machine.is_latency_elapsed() {
|
||||||
// Latency elapsed, transition to data driving
|
// Latency elapsed, transition to data driving
|
||||||
// Data will be driven on next Φ2 rising edge
|
// Data will be driven on next Φ2 rising edge
|
||||||
println!("DEBUG: Latency elapsed, transitioning to DriveData state");
|
|
||||||
self.process_latency_elapsed();
|
self.process_latency_elapsed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -748,23 +734,14 @@ impl Intel4001 {
|
||||||
let address = self.state_machine.get_full_address().unwrap_or(0) as u16;
|
let address = self.state_machine.get_full_address().unwrap_or(0) as u16;
|
||||||
if (address as usize) < self.memory.len() {
|
if (address as usize) < self.memory.len() {
|
||||||
let data = self.memory[address as usize];
|
let data = self.memory[address as usize];
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Driving data 0x{:02X} from address 0x{:02X} onto bus",
|
|
||||||
data, address
|
|
||||||
);
|
|
||||||
self.write_data_bus(data);
|
self.write_data_bus(data);
|
||||||
// Note: Don't call return_to_idle() here - wait for Φ2 falling edge
|
// Note: Don't call return_to_idle() here - wait for Φ2 falling edge
|
||||||
} else {
|
} else {
|
||||||
// Invalid address, tri-state
|
// Invalid address, tri-state
|
||||||
println!(
|
|
||||||
"DEBUG: [ROM] Invalid address 0x{:02X}, tri-stating bus",
|
|
||||||
address
|
|
||||||
);
|
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Address not ready, tri-state
|
// Address not ready, tri-state
|
||||||
println!("DEBUG: [ROM] Address not ready, tri-stating bus");
|
|
||||||
self.tri_state_data_bus();
|
self.tri_state_data_bus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -849,9 +826,7 @@ impl Component for Intel4001 {
|
||||||
/// Main update cycle - handles clock edge detection and operation dispatch
|
/// Main update cycle - handles clock edge detection and operation dispatch
|
||||||
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
|
/// Hardware: Responds to Φ1 and Φ2 clock edges from CPU
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
println!("DEBUG: Update called");
|
|
||||||
if !self.is_running() {
|
if !self.is_running() {
|
||||||
println!("DEBUG: Component not running, returning");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Handle both rising and falling edges for proper two-phase operation
|
// Handle both rising and falling edges for proper two-phase operation
|
||||||
|
|
@ -869,7 +844,6 @@ impl Component for Intel4001 {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
|
|
||||||
if phi1_rising {
|
if phi1_rising {
|
||||||
println!("DEBUG: phi1_rising detected");
|
|
||||||
events.push(Intel400xEvent::ClockPhi1Rising);
|
events.push(Intel400xEvent::ClockPhi1Rising);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
47
src/pin.rs
47
src/pin.rs
|
|
@ -70,6 +70,7 @@ pub struct Pin {
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
settlement_time: Duration,
|
settlement_time: Duration,
|
||||||
connected_pins: Vec<Arc<Mutex<Pin>>>,
|
connected_pins: Vec<Arc<Mutex<Pin>>>,
|
||||||
|
is_propagating: bool,
|
||||||
}
|
}
|
||||||
impl Pin {
|
impl Pin {
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
|
|
@ -80,6 +81,7 @@ impl Pin {
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
settlement_time: Duration::from_nanos(10), // 10ns settlement time
|
settlement_time: Duration::from_nanos(10), // 10ns settlement time
|
||||||
connected_pins: Vec::new(),
|
connected_pins: Vec::new(),
|
||||||
|
is_propagating: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,27 +164,45 @@ impl Pin {
|
||||||
&self.connected_pins
|
&self.connected_pins
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn propagate(&self) {
|
pub fn propagate(&mut self) {
|
||||||
for connected_pin in &self.connected_pins {
|
if self.is_propagating {
|
||||||
if let Ok(mut pin) = connected_pin.lock() {
|
return;
|
||||||
// Copy our drivers to the connected pin (simulate electrical connection)
|
}
|
||||||
let mut new_drivers = self.drivers.clone();
|
|
||||||
|
|
||||||
// Merge with existing drivers on the connected pin
|
self.is_propagating = true;
|
||||||
for (driver, value) in &pin.drivers {
|
|
||||||
new_drivers.insert(driver.clone(), *value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pin.drivers = new_drivers;
|
// Use a scope guard to ensure the flag is reset even on panic
|
||||||
pin.last_update = Instant::now();
|
// This prevents the pin from becoming permanently unable to propagate
|
||||||
pin.recalculate_value();
|
struct PropagationGuard<'a> {
|
||||||
|
flag: &'a mut bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for PropagationGuard<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
*self.flag = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _guard = PropagationGuard {
|
||||||
|
flag: &mut self.is_propagating,
|
||||||
|
};
|
||||||
|
|
||||||
|
for connected_pin in &self.connected_pins {
|
||||||
|
if let Ok(mut pin) = connected_pin.lock() {
|
||||||
|
// Copy all our drivers with their strengths
|
||||||
|
for (driver_name, (value, strength)) in &self.drivers {
|
||||||
|
pin.set_driver_with_strength(Some(driver_name.clone()), *value, *strength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guard will be dropped here, resetting is_propagating to false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recalculate_value(&mut self) {
|
fn recalculate_value(&mut self) {
|
||||||
if self.drivers.is_empty() {
|
if self.drivers.is_empty() {
|
||||||
self.settled_value = PinValue::HighZ;
|
self.settled_value = PinValue::HighZ;
|
||||||
|
self.last_update = Instant::now();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,6 +215,7 @@ impl Pin {
|
||||||
}
|
}
|
||||||
if max_strength == DriveStrength::HighImpedance {
|
if max_strength == DriveStrength::HighImpedance {
|
||||||
self.settled_value = PinValue::HighZ;
|
self.settled_value = PinValue::HighZ;
|
||||||
|
self.last_update = Instant::now();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +236,8 @@ impl Pin {
|
||||||
self.settled_value = PinValue::HighZ;
|
self.settled_value = PinValue::HighZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_update = Instant::now();
|
||||||
|
|
||||||
// Propagate to connected pins
|
// Propagate to connected pins
|
||||||
self.propagate();
|
self.propagate();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -353,4 +353,4 @@ fn test_intel_4001_state_machine_reset() {
|
||||||
|
|
||||||
// Should have returned to Idle state
|
// Should have returned to Idle state
|
||||||
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
assert_eq!(rom.get_timing_state(), TimingState::Idle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -544,4 +544,4 @@ fn test_complete_state_machine_progression() {
|
||||||
println!("✅ Output Port Operation completed");
|
println!("✅ Output Port Operation completed");
|
||||||
|
|
||||||
println!("✅ Complete unified state machine progression test passed!");
|
println!("✅ Complete unified state machine progression test passed!");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,4 +250,4 @@ fn test_intel_4003_reset_functionality() {
|
||||||
remove_pin_driver(&sr, "RESET");
|
remove_pin_driver(&sr, "RESET");
|
||||||
|
|
||||||
println!("=== Intel 4003 Reset Functionality Test Complete ===");
|
println!("=== Intel 4003 Reset Functionality Test Complete ===");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue