NDBusHDLC.cs Critical Issues Analysis and Fix Guide¶
Executive Summary¶
The NDBusHDLC.cs emulator has fundamental DMA implementation flaws that prevent proper SINTRAN operation:
- Incorrect DMA bit auto-clear behavior - Only clears bit 4, not bits 8-15
- Missing TransmissionFinished generation - Success not properly signaled
- Wrong DMA completion bit patterns - BlockEnd/FrameEnd/ListEnd misused
- Inconsistent interrupt generation - Multiple code paths with different logic
- Invalid register read clearing - Clears DMAModuleRequest too early
Critical Bugs Found¶
Bug 1: Wrong Auto-Clear Implementation¶
Current Code (Lines 1838-1851):
case Register.ReadReceiverTransferStatus: //IOX+10
// Clear DMA flag pr doc before reading flags
regs.ReceiverStatus &= ~(ReceiverStatusBits.DMAModuleRequest); // BUG: Clears before read!
value = (ushort)regs.ReceiverStatus;
// Clear bits 8-15 (DMA)
regs.ReceiverStatus = regs.ReceiverStatus & ReceiverStatusBits.DMA_CLEAR_BITS;
Issues: - DMAModuleRequest cleared BEFORE read - SINTRAN never sees it! - DMA_CLEAR_BITS = 0x00FF - Only keeps low bits, correct behavior - But documentation says bit 4 clears "at beginning of read" - means during the read operation, not before
Bug 2: Missing TransmissionFinished Logic¶
Current Code Shows:
SetTXDMAFlag(TransmitterStatusBits.TransmissionFinished| TransmitterStatusBits.ListEnd);
But SINTRAN expects: - TransmissionFinished (bit 11) = 0x0800 for successful transmission - Auto-clear after register read so SINTRAN sees it once - Current code sets it but may not clear it properly
Bug 3: Wrong DMA Status Bit Patterns¶
Current Implementation Issues:
-
BlockEnd/FrameEnd always set together:
SetRXDMAFlag(ReceiverStatusBits.FrameEnd | ReceiverStatusBits.BlockEnd); SetTXDMAFlag(TransmitterStatusBits.FrameEnd| TransmitterStatusBits.BlockEnd); -
ListEmpty set incorrectly:
SetRXDMAFlag(ReceiverStatusBits.ListEmpty); // Set whenever next buffer found
According to SINTRAN analysis: - BlockEnd should indicate "more blocks available for processing" - FrameEnd should only be set when complete HDLC frame received/sent - ListEmpty should stop the receiver - only set when actually out of buffers
Bug 4: Comment Errors Show Misunderstanding¶
Incorrect Comments in Code:
/// Block End Status bit from DMA module.
/// Used by RX module only ???
/// Frame End Status bit from DMA module
/// Used by RX module only ???
/// List End Status bit from DMA module.
/// Used by RX module only ???
Reality from Analysis: - ALL three bits used by BOTH RX and TX modules - Different semantic meanings for each direction - Critical for proper SINTRAN DMA flow control
Correct Implementation Requirements¶
Fix 1: Proper Auto-Clear Behavior¶
case Register.ReadReceiverTransferStatus: //IOX+10
{
// Return current status INCLUDING DMAModuleRequest
value = (ushort)regs.ReceiverStatus;
// Clear DMAModuleRequest at START of read (per documentation)
regs.ReceiverStatus &= ~(ReceiverStatusBits.DMAModuleRequest);
// Auto-clear DMA bits 8-15 AFTER read
regs.ReceiverStatus &= ReceiverStatusBits.DMA_CLEAR_BITS; // Keep only 0-7
break;
}
case Register.ReadTransmitterTransferStatus: //IOX+12
{
// Return current status INCLUDING DMAModuleRequest
value = (ushort)regs.TransmitterStatus;
// Clear DMAModuleRequest at START of read
regs.TransmitterStatus &= ~(TransmitterStatusBits.DMAModuleRequest);
// Auto-clear DMA bits 8-15 AFTER read (add DMA_CLEAR_BITS to TransmitterStatusBits)
regs.TransmitterStatus &= (TransmitterStatusBits)0x00FF; // Keep only 0-7
break;
}
Fix 2: Correct DMA Status Generation¶
Receiver DMA Status¶
public void OnPacketReceived(byte[] frameData, bool isCompleteFrame, bool moreBuffersAvailable)
{
ReceiverStatusBits status = ReceiverStatusBits.DataAvailable;
// Always set when buffer filled
status |= ReceiverStatusBits.BlockEnd;
// Only set when complete HDLC frame received
if (isCompleteFrame)
{
status |= ReceiverStatusBits.FrameEnd;
}
// Only set when no more buffers - this STOPS receiver!
if (!moreBuffersAvailable)
{
status |= ReceiverStatusBits.ListEmpty; // CRITICAL: Stops processing
}
SetRXDMAFlag(status);
}
Transmitter DMA Status¶
public void OnTransmissionComplete(bool blockComplete, bool frameComplete, bool listComplete)
{
TransmitterStatusBits status = 0;
if (blockComplete)
{
status |= TransmitterStatusBits.BlockEnd;
}
if (frameComplete)
{
status |= TransmitterStatusBits.FrameEnd;
}
if (listComplete)
{
status |= TransmitterStatusBits.ListEnd;
status |= TransmitterStatusBits.TransmissionFinished; // Success signal
}
SetTXDMAFlag(status);
}
Fix 3: Enhanced DMA Flag Methods¶
private void SetRXDMAFlag(ReceiverStatusBits flags)
{
// Set DMA module request to trigger interrupt
regs.ReceiverStatus |= ReceiverStatusBits.DMAModuleRequest;
// Set the specific DMA status bits
regs.ReceiverStatus |= flags;
// Generate interrupt if enabled
if (regs.ReceiverEnable.HasFlag(ReceiverEnableBits.DMAModuleIE))
{
SetInterruptBit(13); // Receiver interrupt level
}
}
private void SetTXDMAFlag(TransmitterStatusBits flags)
{
// Set DMA module request to trigger interrupt
regs.TransmitterStatus |= TransmitterStatusBits.DMAModuleRequest;
// Set the specific DMA status bits
regs.TransmitterStatus |= flags;
// Generate interrupt if enabled
if (regs.TransmitterEnable.HasFlag(TransmitterEnableBits.DMAModuleIE))
{
SetInterruptBit(12); // Transmitter interrupt level
}
}
Fix 4: Add Missing TransmitterStatusBits.DMA_CLEAR_BITS¶
// Add to TransmitterStatusBits enum
public enum TransmitterStatusBits : ushort
{
// ... existing bits ...
/// <summary>
/// Used for clearing DMA info after read
/// </summary>
DMA_CLEAR_BITS = 0x00FF
}
SINTRAN Enable Bit Recognition¶
Fix 5: Recognize SINTRAN WTTC Patterns¶
case Register.WriteTransmitterTransferControlRegister: // IOX +13
{
var newValue = (TransmitterEnableBits)value;
// Recognize SINTRAN standard patterns
switch (value)
{
case 105: // Standard DMA transmission (octal 151)
EnableStandardDMATransmission();
break;
case 107: // DMA with underrun detection (octal 153)
EnableStandardDMATransmission();
EnableUnderrunDetection(true);
break;
case 134: // X.21 DMA mode (octal 206)
EnableX21DMATransmission();
break;
case 0: // Disable transmission
DisableTransmission();
break;
}
regs.TransmitterEnable = newValue;
break;
}
Fix 6: Recognize SINTRAN WRTC Patterns¶
case Register.WriteReceiverTransferControl: // IOX + 11
{
var newValue = (ReceiverEnableBits)value;
// Recognize SINTRAN standard patterns
switch (value)
{
case 100: // Device clear (octal 144)
ClearReceiver();
SetDTR(true);
break;
case 304: // Receiver + modem status (octal 456)
case 305: // Full interrupt mode (octal 457)
EnableReceiverWithModemStatus();
break;
case 140: // Maintenance mode (octal 214)
SetMaintenanceMode(true);
SetDTR(true);
break;
}
regs.ReceiverEnable = newValue;
break;
}
Critical Behavioral Changes Required¶
1. ListEmpty Behavior¶
Current: Set whenever moving to next buffer Correct: Only set when actually out of receive buffers (stops receiver)
2. BlockEnd Semantics¶
Current: Always set with FrameEnd Correct: Set when buffer complete, indicates "more processing available"
3. TransmissionFinished Usage¶
Current: Sometimes set, unclear when Correct: Set on successful transmission completion, auto-clears on read
4. Auto-Clear Timing¶
Current: Clears DMAModuleRequest before returning value Correct: Return value with DMAModuleRequest, then clear it and high bits
Testing Validation¶
Test DMA Bit Auto-Clear¶
public void TestDMABitClearing()
{
// Set up DMA status
regs.TransmitterStatus = TransmitterStatusBits.TransmissionFinished |
TransmitterStatusBits.DMAModuleRequest;
// First read should return full status
ushort firstRead = IOOperation(Register.ReadTransmitterTransferStatus);
Assert.AreEqual(0x0810, firstRead); // Should include both bits
// Second read should show cleared high bits
ushort secondRead = IOOperation(Register.ReadTransmitterTransferStatus);
Assert.AreEqual(0x0000, secondRead); // Should be cleared
}
Root Cause Summary - CORRECTED WITH SOURCE CODE ANALYSIS¶
Actual SINTRAN Counter Analysis (from source code):¶
HDLC Driver Counters Found:
- TELL (lines 104057, 104547) = Total interrupt count (TX + RX)
- STPCNT (line 104477) = Receiver stops due to buffer shortage ("LACK OF BUFFER, INC COUNTER")
- T9 (line 104442) = Dummy/unexpected interrupts when device inactive
- DUIN (line 104040) = Transmission interrupts when device inactive (Retry counter)
Critical Finding: NO RXBad counter found in HDLC driver source code
Updated Analysis Based on Log Evidence:¶
Your retransmissions were caused by: 1. ListEmpty bug - Receiver stopping prematurely, preventing acknowledgments 2. Missing acknowledgments - Remote system retransmits due to no ACK received 3. Fixed by removing ListEmpty - Retries reduced from 6 → 1 (83% improvement)
RXBad counter source: - NOT incremented at HDLC driver level (confirmed by source analysis) - Incremented at higher protocol layer (X.25/LAPB level) - Caused by duplicate frame reception - Protocol detects duplicates and counts as "bad" - Empirical evidence: 3 duplicate frames in log = 3 RXBad count
Current Status: System working correctly with normal network error rates (3/1/3 = good performance)
The fix: ListEmpty removal was the critical fix. Remaining issues are normal X.25 protocol behavior.