ND-500 Process I/O and User Interaction¶
Complete Guide to stdin/stdout, Terminal I/O, and User Interaction for ND-500 Processes
Version: 1.0
Last Updated: October 16, 2025
Purpose: Explain how ND-500 processes perform I/O, interact with terminals, and communicate with users
Table of Contents¶
- Overview - No Direct I/O
- The Proxy Model
- Terminal I/O Flow
- DVIO and DVINST Operations
- Complete Examples
- C# Implementation
1. Overview - No Direct I/O¶
1.1 Critical Concept: ND-500 Has NO Direct Terminal Access¶
IMPORTANT: ND-500 processes CANNOT directly access: - Terminals - Keyboards - Disks - Printers - Network interfaces - Any physical devices
What ND-500 Does NOT Have:
┌─────────────────────────────────────┐
│ ND-500 CPU │
│ │
│ ✗ No terminal drivers │
│ ✗ No disk drivers │
│ ✗ No device access │
│ ✗ No stdin/stdout (UNIX concept) │
│ ✗ No file handles │
│ │
│ Only has: │
│ ✓ Compute power │
│ ✓ Private memory │
│ ✓ Access to 5MPM (multiport memory) │
│ ✓ Message passing to ND-100 │
└─────────────────────────────────────┘
1.2 Why No Direct I/O?¶
Design Philosophy: 1. ND-500 is a compute engine - optimized for processing, not I/O 2. ND-100 owns all devices - centralized device management 3. Simpler ND-500 software - no device drivers needed 4. Resource sharing - ND-100 manages device allocation 5. Security - ND-500 can't bypass ND-100 protection
2. The Proxy Model¶
2.1 ND-100 as I/O Proxy¶
All I/O operations go through the ND-100:
flowchart TB
%% ============================================
%% USER TERMINAL
%% ============================================
subgraph USER [User at Terminal]
KEYBOARD[Keyboard]
SCREEN[Screen]
TERM[Terminal connected to ND100]
KEYBOARD --> TERM
SCREEN <--> TERM
end
%% ============================================
%% ND100 SYSTEM
%% ============================================
subgraph ND100 [ND100 System]
TERMDRV[Terminal Driver owns hardware]
KERNEL[SINTRAN Kernel IO Proxy]
DATAFIELD[Terminal Datafield Buffer]
end
%% ============================================
%% MULTIPORT MEMORY
%% ============================================
subgraph MPM [5MPM Multiport Memory]
MSGBUF[Message Buffer]
DATABUF[Data Buffer Characters]
end
%% ============================================
%% ND500 SYSTEM
%% ============================================
subgraph ND500 [ND500 System]
PROC[ND500 Process wants terminal IO]
end
%% ============================================
%% FLOW CONNECTIONS
%% ============================================
TERM --> TERMDRV
TERMDRV --> DATAFIELD
PROC -->|"1 DVIO DVINST message"| MSGBUF
MSGBUF -->|"2 Kernel reads"| KERNEL
KERNEL -->|"3 Request IO"| DATAFIELD
DATAFIELD -->|"4 Get data"| TERMDRV
TERMDRV -->|"5 Copy characters"| DATABUF
DATABUF -->|"6 Result ready"| PROC
%% ============================================
%% STYLE DEFINITIONS
%% ============================================
classDef blue fill:#2196F3,stroke:#1565C0,stroke-width:2px,color:#fff
classDef green fill:#009688,stroke:#00695C,stroke-width:2px,color:#fff
class TERM blue
class MSGBUF,DATABUF green
2.2 No stdin/stdout Concept¶
UNIX/Linux has:
- stdin (file descriptor 0) - standard input
- stdout (file descriptor 1) - standard output
- stderr (file descriptor 2) - standard error
ND-500 has: - Nothing like this! - Instead: Message-based I/O requests - Each I/O operation is a complete message transaction
Comparison:
| UNIX Program | ND-500 Process |
|---|---|
read(0, buffer, 100) |
Send DVINST message with 100-byte request |
write(1, buffer, 50) |
Send DVIO message with 50 bytes of data |
fprintf(stderr, "error") |
Send DVIO message to terminal with error text |
| Blocking I/O call | Process blocked until ND-100 sends response |
| Non-blocking I/O | Not directly supported |
3. Terminal I/O Flow¶
3.1 User Interaction Architecture¶
┌──────────────────────────────────────────────────────────────────┐
│ User Types on Terminal │
└────────────┬─────────────────────────────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────────────────────┐
│ ND-100 Terminal Driver (Level 12) │
│ - Receives characters from hardware │
│ - Stores in terminal datafield buffer │
│ - Can echo characters back to screen │
└────────────┬─────────────────────────────────────────────────────┘
│
↓ (when ND-500 process requests input)
┌──────────────────────────────────────────────────────────────────┐
│ ND-100 Kernel (Monitor Level) │
│ - Receives DVINST message from ND-500 │
│ - Reads ND-500's message buffer in 5MPM │
│ - Extracts: buffer address, byte count, terminal device │
└────────────┬─────────────────────────────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────────────────────┐
│ XIBMOVE Routine (RP-P2-N500.NPL lines 430-533) │
│ - Copies characters from terminal buffer to 5MPM data buffer │
│ - One character at a time using IOTRANS │
│ - Checks for "break" conditions (max bytes, special chars) │
│ - Sets up MMU windows for access │
└────────────┬─────────────────────────────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────────────────────┐
│ 5MPM Data Buffer │
│ - Characters now in multiport memory │
│ - ND-500 can read them │
└────────────┬─────────────────────────────────────────────────────┘
│
↓
┌──────────────────────────────────────────────────────────────────┐
│ ND-500 Process Resumed │
│ - Level 12 interrupt to ND-100 │
│ - ND-100 updates message buffer with byte count │
│ - ND-100 sends completion interrupt to ND-500 │
│ - ND-500 process reads data from 5MPM buffer │
│ - Process continues execution │
└──────────────────────────────────────────────────────────────────┘
3.2 Complete Terminal Input Sequence¶
sequenceDiagram
autonumber
participant U as User<br/>(Terminal)
participant TD as Terminal Driver<br/>(ND-100)
participant K as SINTRAN Kernel
participant MPM as 5MPM
participant P as ND-500 Process
note over P: ND-500 Process Needs Input
P->>MPM: Prepare DVINST message<br/>(func=2, device, bufaddr, count)
P->>MPM: Set 5ITMQUEUE flag
P->>K: Interrupt (LTAG5)
P->>P: Block waiting for response
note over K,TD: ND-100 Processes Request
K->>MPM: Read DVINST message
K->>TD: Check terminal datafield
alt Terminal buffer has data
TD-->>K: Characters available
else Terminal buffer empty
K->>U: Wait for user input
U->>TD: User types characters
TD->>TD: Store in terminal buffer
TD-->>K: Data ready
end
note over K,MPM: Copy Data to 5MPM
loop For each character (XIBMOVE)
K->>TD: Read one character (IOTRANS)
TD-->>K: Character value
K->>MPM: Write to data buffer<br/>at ND-500's address
K->>K: Increment byte counter
alt Max bytes reached OR special char
K->>K: Break condition - stop
end
end
K->>MPM: Update message:<br/>ByteCount=actual<br/>ErrorCode=0
K->>MPM: Clear 5ITMQUEUE
note over K,P: Resume ND-500 Process
K->>P: Interrupt (Level 12 trigger)
P->>MPM: Read message
P->>MPM: Read data buffer
P->>P: Process input<br/>Continue execution
3.3 Terminal Output Sequence¶
Similar but reversed:
- ND-500 Process has data to output
- Writes data to 5MPM buffer
- Sends DVIO message (function=1)
- ND-100 Kernel reads message
- Copies data from 5MPM to terminal driver buffer
- Terminal Driver sends characters to screen
- User sees output on terminal
- ND-100 sends completion interrupt to ND-500
- ND-500 Process continues
4. DVIO and DVINST Operations¶
4.1 DVINST - Direct Input¶
Purpose: Read data from a device (typically terminal input)
From ND-500 Process perspective:
// Pseudo-code for ND-500 process
void read_from_terminal(char* buffer, int max_bytes)
{
// 1. Prepare message in my dedicated message buffer (in 5MPM)
MessageBuffer* msg = get_my_message_buffer();
msg->FunctionCode = 2; // DVINST
msg->ToDatafield = terminal_datafield_addr; // Which terminal
msg->ByteCount = max_bytes; // How many bytes wanted
msg->ND500LogicalAddr = (uint32_t)buffer; // Where to put data (in 5MPM!)
msg->MicrocodeFunction = 3RMED; // Read memory
// 2. Set ready flag
msg->MessageFlags |= 5ITMQUEUE;
// 3. Trigger ND-100 (hardware interrupt)
trigger_nd100_interrupt();
// 4. Block waiting for response
// (ND-500 microcode handles this)
wait_for_completion();
// 5. When resumed, check result
int bytes_read = msg->ByteCount; // Actual count
int error = msg->ErrorCode;
// 6. Data is now in buffer (in 5MPM)
// Can read it directly
}
From ND-100 Kernel perspective (MP-P2-N500.NPL lines 1817-1839):
NINSTR: T:=5MBBANK; X:=N5MESSAGE
*AAX DMAXB; LDDTX; AAX -DMAXB % AD=MAX. BYTECOUNT
XNINSTR:
IF A><0 OR D>>4000 THEN % Check byte count (max 4000₈ bytes)
X:=N5MESSAGE; A:=EC174
CALL EMONICO % Error - too many bytes
CALL XACTRDY
GO NXTMSG
FI
A:=B=:BREG
CALL 5GTDF; GO NORMMC % Get terminal datafield
IF A.TYPRING BIT 5BAD THEN % Bad device?
X:=N5MESSAGE; GO NORMMC
FI
X=:B; N5MESSAGE=:CCMESS % Save message address
CALL SET12WINDOW % Set up MMU windows
% Call XIBMOVE to copy characters...
4.2 DVIO - Direct Output¶
Purpose: Write data to a device (typically terminal output)
From ND-500 Process perspective:
// Pseudo-code for ND-500 process
void write_to_terminal(const char* data, int byte_count)
{
// 1. Data must already be in 5MPM buffer
// (ND-500 can only reference 5MPM memory for I/O)
// 2. Prepare message
MessageBuffer* msg = get_my_message_buffer();
msg->FunctionCode = 1; // DVIO
msg->ToDatafield = terminal_datafield_addr;
msg->ByteCount = byte_count;
msg->ND500LogicalAddr = (uint32_t)data; // Source in 5MPM
msg->MicrocodeFunction = 3WMED; // Write memory
// 3. Set ready flag and trigger
msg->MessageFlags |= 5ITMQUEUE;
trigger_nd100_interrupt();
// 4. Block waiting
wait_for_completion();
// 5. Check result
int bytes_written = msg->ByteCount;
int error = msg->ErrorCode;
}
From ND-100 Kernel perspective (MP-P2-N500.NPL lines 1688-1707):
DVIO:
NOUTSTR:
CALL 5GTDF; GO NORMMC % Get terminal datafield
A:=D; X:=N5MESSAGE; T:=5MBBANK; *AAX TODF; STATX % Store datafield addr
*AAX DNOBY-TODF; LDDTX; AAX -DNOBY % Get byte count
IF A><0 OR D>>4000 THEN % Check max 4000₈ bytes
A:=EC174; CALL EMONICO % Error
CALL XACTRDY
GO NXTMSG
ELSE IF D=0 THEN % Zero bytes?
CALL OSTRS; GO NXTMSG % Just restart
FI; FI
IF MIFLAG NBIT WSMC THEN % Not in com-buffer?
T:=5MBBANK; 3RMED; *STATX XMICF % Microcode func = read data memory
A:=D; *AAX NRBYT; STATX % Store byte count
*AAX 5DITN-NRBYT; STZTX
*AAX OSTRA-5DITN; LDDTX; AAX N500A-OSTRA; STDTX % ND-500 source address
*AAX ABUFA-N500A; LDDTX; AAX N100A-ABUFA; STDTX % ND-100 dest address (CNVWADR'd)
"STTDRIV"; *AAX SPFLA-N100A; STATX; AAX -SPFLA % Restart in driver
4.3 Key Constraints¶
Buffer Location: - ALL I/O buffers MUST be in 5MPM (multiport memory) - ND-500 cannot use its private memory for I/O - Addresses passed in messages must be 5MPM addresses (bit 31 set from ND-500 view)
Size Limits: - Maximum 4000₈ bytes (2048 decimal) per operation - This is due to communication buffer size limits - Larger transfers require multiple operations
Device Access: - ND-500 must know the terminal datafield address - Usually passed during process initialization - Can only access devices ND-100 allows
5. Complete Examples¶
5.1 Example: ND-500 Process Reading User Input¶
Scenario: ND-500 process running database query, needs SQL command from user
sequenceDiagram
participant U as User
participant T as Terminal
participant K as SINTRAN
participant M as 5MPM
participant DB as ND-500<br/>Database Process
DB->>DB: Need SQL query
DB->>M: Write DVINST message:<br/>func=2, term_df, buf, 256 bytes
DB->>M: Set 5ITMQUEUE
DB->>K: Interrupt
DB->>DB: Block waiting
K->>M: Read message
K->>T: Check terminal buffer
T-->>K: Empty
T->>U: Display prompt (previous DVIO)
U->>T: Types "SELECT * FROM customers"
U->>T: Press ENTER
T->>T: Store in terminal buffer
T->>K: Signal data available
loop Character by character
K->>T: IOTRANS (read 1 char)
T-->>K: Character
K->>M: Write to 5MPM buffer
end
K->>M: Update message:<br/>ByteCount=27<br/>ErrorCode=0
K->>DB: Interrupt
DB->>M: Read message
DB->>M: Read "SELECT * FROM customers"
DB->>DB: Parse SQL
DB->>DB: Execute query
DB->>M: Write result to 5MPM buffer
DB->>M: Write DVIO message (output)
DB->>K: Interrupt
K->>M: Read output data
K->>T: Write to terminal
T->>U: Display results
5.2 Example Code Flow¶
ND-500 Side (pseudo-code):
// ND-500 database process
void process_user_query()
{
char query_buffer[256]; // Must be in 5MPM!
char result_buffer[2048]; // Must be in 5MPM!
// Display prompt
write_to_terminal("SQL> ", 5);
// Read user input
int bytes_read = read_from_terminal(query_buffer, 256);
if (bytes_read <= 0) {
write_to_terminal("ERROR: No input\n", 16);
return;
}
// Process SQL
QueryResult* result = execute_sql(query_buffer, bytes_read);
// Format output
int output_len = format_results(result, result_buffer, 2048);
// Write results
write_to_terminal(result_buffer, output_len);
// Done - loop for next query
}
ND-100 Side (from NPL code):
% XIBMOVE - Copy characters from terminal to ND-500 buffer
% Called from monitor level when ND-500 process is waiting for input
XIBMOVE: A:=L=:IBMLREG; X=:IBMPRD
% - SET UP TERMINAL WINDOW
AD:=DTDFPHPAGE=:IMBDTDFPHPAGE % Terminal datafield physical page
D=:X; T:=A SHZ -6 % Physical address
A:=:D/\1777+"WND41*2000"=:B % Logical address in window
A:=142000; AD=:IBM41PITENTRY % Save PIT entry
% - SET UP WINDOW FOR ND-500 MESSAGE
A:=IBMIN5MSG=:D:=5MBBANK; A=:T; D=:X
AD SHZ -12 % Physical page of message
A:=142000; AD=:IBM5PITENTRY % Save PIT entry
A:=IBMIN5MSG/\1777+"WNDN5*2000"=:IBMWNDMESS
% - SET UP WINDOW FOR ND-500 DATA BUFFER
X:=IBMIN5MSG; *AAX ABUFA; LDDTX % Get buffer address from message
D=:T
AD SHZ -12; A:=142000; AD=:IBMM6PITENTRY % Physical page
A:=T/\1777+"WNDBF*2000"=:IBMLGBUADDR % Logical address
IIBM: *ION; IOF
% - MOVE CHARACTER
AD:=IBM41PITENTRY; T:=0; X:="WND41+WND41+174000"; *STDTX % Set terminal PIT
AD:=IBM5PITENTRY; X:="WNDN5+WNDN5+174000"; *STDTX % Set message PIT
AD:=IBMM6PITENTRY; X:="WNDBF+WNDBF+174000"; *STDTX % Set buffer PIT
CALL IOTRANS; GO FAR TMWT % Read character
X:=IBMWNDMESS.5FYLLE; T:=IBMLGBUADDR; *SBYT % Store in 5MPM buffer
A:=X+1=:IBMWNDMESS.5FYLLE % Increment counter
% - Check for break condition
IF bytes_read >= max_bytes THEN GO N5RST % Done
GO IIBM % Next character
N5RST: % Restart ND-500 process
% Update message with final byte count
% Send interrupt to ND-500