Device Driver Framework and Task Relationships¶
Version: 1.0
Date: 2025-10-17
Status: Complete
Author: AI Analysis of SINTRAN III Source Code
Table of Contents¶
- Overview
- Device Datafield Structure
- Driver Architecture
- Interrupt Handling
- I/O Queueing and Task Relationships
- Driver Initialization
- Driver Types
- Device State Machine
- C# Emulator Implementation
- Device Traversal and Inspection
1. Overview¶
1.1 Device Driver Model¶
SINTRAN III uses a interrupt-driven, queue-based device driver architecture where:
- Each device has a datafield (Device Control Block) in shared memory
- Drivers run at dedicated interrupt levels (10-12)
- Tasks waiting for I/O are linked in waiting queues
- I/O completion triggers interrupt → driver → scheduler
1.2 Key Design Principles¶
┌────────────────────────────────────────┐
│ 1. Separation of Concerns │
│ - User programs: Make I/O requests │
│ - Monitor: Queue management │
│ - Drivers: Hardware interaction │
│ - Interrupts: Asynchronous events │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 2. Non-blocking I/O │
│ - Tasks never busy-wait │
│ - Moved to waiting queue │
│ - Scheduler runs other tasks │
│ - I/O complete → task ready │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 3. Priority-based Interrupt Handling │
│ - Clock (L13) > Storage (L11) │
│ - Nested interrupts possible │
│ - Fast interrupt handlers │
└────────────────────────────────────────┘
1.3 System Architecture¶
sequenceDiagram
participant Task as User Task
participant Mon as Monitor (Level 3)
participant Drv as Driver (Level 10-12)
participant HW as Hardware Device
Note over Task: Running on Level 1
Task->>Mon: Monitor Call (I/O request)
Mon->>Mon: Validate parameters
Mon->>Mon: Add task to device waiting queue
Mon->>Mon: TOWQU(task, device)
Mon->>Drv: Activate driver (if not running)
Drv->>HW: Initiate I/O operation
Note over Drv: Driver returns, task blocked
Mon->>Mon: Select next ready task
Mon->>Task: Context switch to other task
Note over HW: Time passes...<br/>I/O completes
HW->>Drv: Interrupt!
Note over Drv: Driver runs on Level 10-12
Drv->>Drv: Handle I/O completion
Drv->>Mon: FWQU(task, device)
Drv->>Mon: RTENTRY(task)
Note over Mon: Task now ready
Mon->>Task: Context switch (if high priority)
Note over Task: I/O data available
2. Device Datafield Structure¶
2.1 Standard Datafield Layout¶
Every device has a datafield (minimum 16 words) in shared memory:
% Standard datafield structure (first 16 words)
DISP 0
% === Standard section (all devices) ===
INTEGER RESLINK % 0: Link in reservation queue
INTEGER RTRES % 1: Owner RT-program (or 0/-1 if free)
INTEGER BWLINK % 2: Head of waiting queue
% Location 3 reserved
INTEGER TYPRING % 4: Device type and ring
INTEGER MLINK % 5: Monitor queue link
INTEGER MFUNC % 6: Monitor function address
% Location 7 reserved
% === Device-specific section (varies) ===
INTEGER HDEV % 8: Hardware device number
INTEGER HST % 9: Hardware status
INTEGER BUFFER % 10: Buffer pointer
INTEGER COUNT % 11: Transfer count
INTEGER STATUS % 12: Device status
INTEGER FLAGS % 13: Control flags
INTEGER RESERVED1 % 14
INTEGER RESERVED2 % 15
% Additional words follow (device-specific)
PSID
2.2 Key Fields¶
| Offset | Field | Purpose |
|---|---|---|
| 0 | RESLINK | Link to next reserved device (for resource tracking) |
| 1 | RTRES | Points to RT-description of owning task (0 = free) |
| 2 | BWLINK | Head of waiting queue (tasks waiting for this device) |
| 4 | TYPRING | Device type and ring level encoded |
| 5 | MLINK | Link in monitor queue (for scheduled actions) |
| 6 | MFUNC | Monitor function to call |
| 8+ | (Device) | Device-specific data (varies by driver) |
2.3 RTRES Field Usage¶
Purpose: Track which task owns the device
% Device free?
IF X.RTRES = 0 THEN
% Device available
FI
% Allocate device to task
RTDESC =: X.RTRES
% Release device
0 =: X.RTRES
Values: - 0: Device is free - -1 (0xFFFF): Device reserved by system - Positive: Address of RT-description owning device
2.4 BWLINK Field Usage¶
Purpose: Head of waiting queue (linked via WLINK in RT-descriptions)
Device Datafield (BWLINK):
↓
RT-Desc Task A (WLINK):
↓
RT-Desc Task B (WLINK):
↓
RT-Desc Task C (WLINK):
↓
NULL (-1)
Operations: - TOWQU: Add task to head of waiting queue - FWQU: Remove task from waiting queue
3. Driver Architecture¶
3.1 Interrupt Level Assignment¶
| Level | Purpose | Examples |
|---|---|---|
| Level 10 | Output devices | Line printers, plotters, terminal output |
| Level 11 | Mass storage | Disk controllers, tape drives |
| Level 12 | Input devices | Terminal input, card readers |
| Level 13 | Clock | System timer, scheduling |
| Level 14 | Internal interrupts | Page faults, monitor calls |
Priority:
Level 13 (Clock) > Level 12 (Input) > Level 11 (Storage) > Level 10 (Output)
3.2 Driver Entry Points¶
Every driver implements:
% Main driver entry point (called on interrupt)
SUBR DRIVER_NAME
DRIVER_NAME:
% Save timer
TTMR =: TMR
RETURN:
% Wait for interrupt
CALL ID<level> % e.g., ID11 for level 11
ENTRY:
% Interrupt occurred, hardware ident in A-register
% Look up datafield from ident table
T =: ITB<level>; A - 1; *LDDTX
A =: B % B = datafield address
% Process interrupt
CALL HANDLE_IO
% Return to RETURN (wait for next interrupt)
GO RETURN
Key points:
- Driver runs in infinite loop
- ID
3.3 Ident Code Lookup¶
Ident tables map hardware ident codes to datafield addresses:
┌────────────────────────────────┐
│ ITB10 (Level 10 - Output) │
│ [0] → Datafield addr for ID 1 │
│ [1] → Datafield addr for ID 2 │
│ [2] → Datafield addr for ID 3 │
│ ... │
└────────────────────────────────┘
┌────────────────────────────────┐
│ ITB11 (Level 11 - Storage) │
│ [0] → Datafield addr for ID 1 │
│ [1] → Datafield addr for ID 2 │
│ ... │
└────────────────────────────────┘
┌────────────────────────────────┐
│ ITB12 (Level 12 - Input) │
│ [0] → Datafield addr for ID 1 │
│ [1] → Datafield addr for ID 2 │
│ ... │
└────────────────────────────────┘
Usage:
% Hardware interrupts with ident code in A
T =: ITB11 % Ident table for level 11
A - 1 % Convert to 0-based index
*LDDTX % Load datafield address from table
A =: B % B now points to device datafield
3.4 Datafield Initialization¶
During boot (see Chapter 01 - Boot Sequence):
% For each device
DEVICEID =: T
ITB<level> + T - 1 % Calculate ident table entry address
DATAFIELD_ADDR =: * % Store datafield address
4. Interrupt Handling¶
4.1 Terminal Input Driver (Level 12)¶
From MP-P2-TERM-DRIV.NPL:
% STTIN: Terminal input driver
% Entry: Interrupt from terminal with ident code in A
STTIN:
TTMR =: TMR % Save timer
RETURN:
CALL ID12 % Wait for interrupt (Level 12)
TYENT:
% Interrupt occurred, A = ident code
ISTATE =: IISTATE % Save interrupt state
CALL SET12WINDOW % Set up memory window
% Check for special mode (BRKMODE)
IF BRKMODE = 12 GO BFYICOMDRIVER
% Get output datafield (two-way device)
IF DFOPP >< 0 THEN
X =: 1777; X/\A; A =: B/\176000 + X
FI
A =: 12DFOPP
A.BITFLAG BZERO 5CLOU =: X.BITFLAG
NXCHR:
X =: 12DFOPP
CALL TIAPD; GO RETURN % Read character from hardware
% Character read successfully, A = character
IF T =: "ROUSPEC" >< 0 THEN
D =: 0; CALL ROUSPEC; GO NXCHR; GO OKCHAR
FI
A =: LAST % Save character
OKCHAR:
% Clear break/echo flag
BRECHOFL/\177770 =: BRECHOFL
% Test for XON/XOFF
A =: LAST/\377; CALL XONCHECK
% Handle echo
IF DFLAG BIT 5ECHO THEN
LAST/\377
CALL TECHO % Test for echo
CALL TBREAK % Test for break
X =: 12DFOPP
ELSE
% No echo mode
IF IISTATE = -1 THEN
*ION
LAST/\377; CALL CXRBPUT; GO BFULL
FI
FI
*ION
% Echo character if needed
IF BRECHOFL BIT 5ECHO THEN CALL ECHSUBR FI
% Ignore character?
IF BRECHOFL < 0 THEN
7 =: LAST; CALL ECHSUBR; GO RETU
FI
ADDCH:
% Add character to buffer
LAST/\377; CALL CXRBPUT
% Check if buffer full
IF CFREE < 20 AND BRECHOFL NBIT 5BREAK GO BFULL
% Check for break condition
IF BRECHOFL BIT 5BREAK THEN
% Turn off echo in driver
DFLAG BZERO 5ECHO
% Determine echo strategy
IF X =: FYLLE = HENTE THEN
A BONE 5ALEC % Whole buffer echoed by driver
ELSE
A BZERO 5ALEC % Part echoed by IOTRANS
FI
A =: DFLAG; X =: RSISTE
ELSE
IF DFLAG BIT 5XOFF GO BFULL
GO RETU
FI
BFULL:
% Buffer full or break, restart task
CALL TSTBACK % Restart waiting program
% (Additional ND-500 message handling omitted)
GO FAR NXCHR
RETU:
IF "ROUSPEC" >< 0 THEN
D =: 1; X =: 12DFOPP; CALL ROUSPEC; GO FAR OKCHAR
FI
GO FAR NXCHR
Key operations:
- ID12: Wait for terminal interrupt
- TIAPD: Read character from hardware
- CXRBPUT: Put character in buffer
- TSTBACK: Wake up waiting task
4.2 Disk Driver (Level 11)¶
From IP-P2-DISK-START.NPL:
% CTRDISK: Disk transfer driver
% Entry: Called from monitor (MTRANS) via Level 11
% B = Device datafield, X = ABSTR parameter list
CTRDISK:
A =: L =: "CTRLR"
% Save parameter pointer
X =: PARDF
% Set owner
X.RTRES =: STPRW % Owner RT-program
% Fetch parameters
T =: X.ABFUN; AD =: X.MEMAD =: MEMAD
IF A = 0 AND D = 0 THEN CALL ERRFATAL FI % Memory addr 0 illegal
% Build control word (CTRG)
A/\3; A SH 14 + T =: CTRG
A SHZ -6/\7 SH 3 \/ CBLDA
% Get unit type
T =: M2UNTYP; *EXR SA
IF K THEN
% Phoenix disk
CTRG BONE 16 =: CTRG; 0 =: CARG
ELSE
CTRG/\7000 SHZ -11 =: CARG
FI
% Extract disk address
IF CTRG/\77 >= 60 AND A <= 66 THEN
IF A = T THEN CTRG/\177701 =: CTRG
ELSE CTRG/\177703 =: CTRG
FI
X.ABPA2 =: CADRG % Double disk address
ELSE IF A = 43 OR A = 44 THEN
X.ABPA2 =: CADRG % Double disk address
ELSE
X.ABP21 =: CDRG % Single disk address
FI FI
% Number of sectors
X.ABP31 =: CXRG
% Save initial control word
CTRG =: TRGINI
% ... (Additional parameter processing) ...
% Wait for interrupt if needed
IF TMR >< 0 THEN CALL ID11 FI
% ... (Disk logging, actual I/O initiation) ...
Key operations:
- Parameter fetching: Memory address, disk address, sector count
- Control word building: Function code, unit, disk address
- ID11: Wait for disk interrupt (if not already active)
- Hardware I/O initiation: Start DMA transfer
4.3 I/O Completion Path¶
sequenceDiagram
participant HW as Hardware
participant Drv as Driver (L10-12)
participant Mon as Monitor (L3)
participant Sched as Scheduler
participant Task as Waiting Task
Note over HW: I/O completes
HW->>Drv: Interrupt (ident code)
Note over Drv: Driver wakes up<br/>from ID<level>
Drv->>Drv: Look up datafield<br/>from ident table
Drv->>Drv: Read hardware status
Drv->>Drv: Store result in datafield
Drv->>Mon: Call FWQU(task, device)
Note over Mon: Remove task from<br/>waiting queue
Drv->>Mon: Call RTENTRY(task)
Note over Mon: Add task to<br/>execution queue
Mon->>Sched: Check preemption
alt Task has high priority
Sched->>Task: Context switch
Note over Task: Task resumes,<br/>I/O data available
else Task has low priority
Note over Sched: Task ready,<br/>will run later
end
Drv->>Drv: Loop back to ID<level>
Note over Drv: Wait for next interrupt
5. I/O Queueing and Task Relationships¶
5.1 Queue Relationships¶
┌─────────────────────────────────────────────────┐
│ RT-Description (Task) │
│ - TLINK: Time queue link │
│ - ELINK: Execution queue link │
│ - WLINK: Waiting queue link (points to next) │
│ - BRESLINK: Reservation link │
│ - RTRES: Reserved devices (head of chain) │
└─────────────────────────────────────────────────┘
│
│ RTRES (owner)
▼
┌─────────────────────────────────────────────────┐
│ Device Datafield │
│ - RTRES: Points back to owning task │
│ - BWLINK: Waiting queue head │
│ - RESLINK: Next reserved device │
└─────────────────────────────────────────────────┘
│
│ BWLINK (waiting queue)
▼
┌─────────────────────────────────────────────────┐
│ RT-Description (Waiting Task 1) │
│ - WLINK: Points to next waiting task │
└─────────────────────────────────────────────────┘
│
│ WLINK
▼
┌─────────────────────────────────────────────────┐
│ RT-Description (Waiting Task 2) │
│ - WLINK: -1 (end of queue) │
└─────────────────────────────────────────────────┘
5.2 TOWQU - Add to Waiting Queue¶
From Chapter 02 and 17:
% TOWQU: Add task to device waiting queue
% Entry: X = RT-description, B = Device datafield
TOWQU:
*IOF % Disable interrupts
% Set WAIT flag in task status
X.STATUS BONE 5WAIT =: X.STATUS
% Add to head of device waiting queue
A =: B.BWLINK % Current head
X =: B.BWLINK % X becomes new head
A =: X.WLINK % Link to previous head
*ION % Re-enable interrupts
EXIT
Result:
Before:
Device.BWLINK → Task A → Task B → NULL
After TOWQU(Task C, Device):
Device.BWLINK → Task C → Task A → Task B → NULL
5.3 FWQU - Remove from Waiting Queue¶
% FWQU: Remove task from device waiting queue
% Entry: X = RT-description, B = Device datafield
FWQU:
*IOF
% Clear WAIT flag
X.STATUS BZERO 5WAIT =: X.STATUS
% Search and unlink
PREVLINK =: -1
A =: B.BWLINK
DO WHILE A >< -1
IF A = X THEN % Found task
IF PREVLINK = -1 THEN
% Remove from head
X.WLINK =: B.BWLINK
ELSE
% Remove from middle/end
X.WLINK =: PREVLINK.WLINK
FI
GO DONE
FI
PREVLINK =: A
A.WLINK =: A
OD
DONE:
*ION
EXIT
5.4 Task State Transitions¶
stateDiagram-v2
[*] --> Running: Task executing
Running --> IORequest: I/O Monitor Call
IORequest --> Waiting: TOWQU(task, device)<br/>FREXQU(task)
note left of Waiting
Task blocked on device
In device's BWLINK queue
STATUS has 5WAIT bit set
end note
Waiting --> Ready: FWQU(task, device)<br/>RTENTRY(task)
note right of Ready
I/O completed
Task in execution queue
STATUS 5WAIT bit cleared
end note
Ready --> Running: Scheduler selects
Running --> [*]: Task exits
classDef runState fill:#4CAF50,stroke:#388E3C,stroke-width:2px,color:#fff
classDef waitState fill:#F44336,stroke:#D32F2F,stroke-width:2px,color:#fff
classDef readyState fill:#2196F3,stroke:#1976D2,stroke-width:2px,color:#fff
classDef reqState fill:#FFA726,stroke:#F57C00,stroke-width:2px,color:#fff
class Running runState
class Waiting waitState
class Ready readyState
class IORequest reqState
5.5 Device Reservation¶
Purpose: Exclusive access to device (e.g., tape drive)
% Reserve device
SUBR BRESERVE
% Entry: X = RT-description, B = Device datafield
BRESERVE:
*IOF
% Check if device free
IF B.RTRES >< 0 THEN
% Device busy, add to waiting queue
CALL TOWQU
A =: -1 % Return error
EXIT
FI
% Allocate device
X =: B.RTRES % Device now owned by task
% Add device to task's reservation chain
A =: X.BRESLINK % Current head
B =: X.BRESLINK % Device becomes new head
A =: B.RESLINK % Link to previous head
*ION
A =: 0 % Return success
EXIT
Reservation chain:
Task RT-Description:
BRESLINK → Device A → Device B → NULL
Task owns devices A and B
Release:
% Release device
SUBR BRELEASE
% Entry: X = RT-description, B = Device datafield
BRELEASE:
*IOF
% Clear owner
0 =: B.RTRES
% Remove from task's reservation chain
% ... (chain manipulation) ...
% Wake up next waiting task (if any)
IF B.BWLINK >< -1 THEN
A =: B.BWLINK % Head of waiting queue
CALL FWQU % Remove from waiting
CALL RTENTRY % Add to execution queue
FI
*ION
EXIT
6. Driver Initialization¶
6.1 Boot-Time Registration¶
From Chapter 01 (Boot Sequence):
% During boot, for each device:
% 1. Allocate datafield memory
CALL GETMEM(DATAFIELD_SIZE)
DATAFIELD_ADDR =: A
% 2. Initialize standard fields
0 =: A.RESLINK % No reservation
0 =: A.RTRES % Device free
-1 =: A.BWLINK % Empty waiting queue
DEVICE_TYPE =: A.TYPRING
0 =: A.MLINK % No monitor queue link
0 =: A.MFUNC % No monitor function
% 3. Initialize device-specific fields
HARDWARE_IDENT =: A.HDEV
0 =: A.STATUS
% ... etc ...
% 4. Register in ident table
T =: ITB<level>
HARDWARE_IDENT - 1
*STDTX DATAFIELD_ADDR % Store datafield address
% 5. Start driver (if not already running)
% Driver runs in infinite loop on its level
6.2 Driver Activation¶
Drivers are started during boot:
% Activate Level 11 (disk driver)
A =: DRIVER_ENTRY_POINT
*IRW LEVEL DP % Set P register for level
*MST PIE % Enable interrupts for level
Driver then runs forever:
DRIVER:
TTMR =: TMR
LOOP:
CALL ID<level> % Wait for interrupt
% Process interrupt
% ... driver logic ...
GO LOOP % Back to wait
7. Driver Types¶
7.1 Disk Drivers¶
Purpose: Mass storage I/O (read/write sectors)
Files:
- IP-P2-DISK-START.NPL: Main disk driver
- IP-P2-SCSI-DISK.NPL: SCSI disk driver
- MP-P2-DISK-START.NPL: Monitor-level disk routines
Key operations: - CTRDISK: Main disk transfer routine - STRNS: Start disk transfer from monitor - MTRANS: Monitor-level transfer initiation
Datafield extensions:
% Disk datafield (16+ words)
DISP 0
% Standard fields (0-15)
% ... (as above) ...
% Disk-specific fields
INTEGER DISKADDR1 % 16: Disk address (high)
INTEGER DISKADDR2 % 17: Disk address (low)
INTEGER SECTORCOUNT % 18: Number of sectors
INTEGER CYLINDER % 19: Current cylinder
INTEGER HEAD % 20: Current head
INTEGER SECTOR % 21: Current sector
INTEGER ERRORCOUNT % 22: Error retry count
PSID
7.2 Terminal Drivers¶
Purpose: Character I/O to/from terminals
Files:
- MP-P2-TERM-DRIV.NPL: Terminal input/output driver
- MP-P2-TAD.NPL: TAD (Terminal Access Device) driver
Key operations: - STTIN: Terminal input (Level 12) - TECHO: Echo character - CXRBPUT: Put character in buffer
Datafield extensions:
% Terminal datafield
DISP 0
% Standard fields (0-15)
% ... (as above) ...
% Terminal-specific fields
INTEGER DFOPP % 16: Opposite datafield (input/output pair)
INTEGER DFLAG % 17: Driver flags (echo, break, etc.)
INTEGER BITFLAG % 18: Bit flags
INTEGER HENTE % 19: Buffer read pointer
INTEGER FYLLE % 20: Buffer write pointer
INTEGER CFREE % 21: Free space in buffer
INTEGER LAST % 22: Last character read
INTEGER BRECHOFL % 23: Break/echo flags
INTEGER SCREEN % 24: Screen control
INTEGER TINFO % 25: Terminal info
PSID
7.3 Communication Drivers¶
Purpose: Network and serial communication
Files:
- MP-P2-HDLC-DRIV.NPL: HDLC driver
- MP-P2-X21-DRIV.NPL: X.21 driver
- MP-P2-PIOC-DRIV.NPL: PIOC driver
Key operations: - Frame assembly/disassembly - CRC calculation - Protocol state machines
7.4 ND-500 Communication Driver¶
Purpose: Coordinate I/O between ND-100 and ND-500
Files:
- MP-P2-N500.NPL: ND-500 message handling
- XC-P2-1.NPL: ND-500 coordination
Key operations: - Message passing - Shared memory coordination - Interrupt synchronization
8. Device State Machine¶
8.1 Device States¶
stateDiagram-v2
[*] --> Idle: Boot/Init
Idle --> Busy: I/O Request
note left of Busy
RTRES = owner task
Hardware active
DMA in progress
end note
Busy --> Completing: I/O Done (interrupt)
note right of Completing
Read hardware status
Update datafield
Wake task
end note
Completing --> Idle: RTRES = 0
Completing --> Busy: Next request queued
Busy --> Error: Hardware fault
Error --> Idle: Error handled
Idle --> Reserved: Task reserves device
Reserved --> Busy: I/O Request
Reserved --> Idle: Task releases device
classDef idleState fill:#9E9E9E,stroke:#616161,stroke-width:2px,color:#fff
classDef busyState fill:#4CAF50,stroke:#388E3C,stroke-width:2px,color:#fff
classDef completeState fill:#2196F3,stroke:#1976D2,stroke-width:2px,color:#fff
classDef errorState fill:#F44336,stroke:#D32F2F,stroke-width:2px,color:#fff
classDef reservedState fill:#FFA726,stroke:#F57C00,stroke-width:2px,color:#fff
class Idle idleState
class Busy busyState
class Completing completeState
class Error errorState
class Reserved reservedState
8.2 Typical I/O Lifecycle¶
┌──────────────────────────────────────────────┐
│ 1. Task makes I/O request │
│ - Monitor call (e.g., READ-SECTOR) │
│ - Parameters: device, buffer, count │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ 2. Monitor validates and queues │
│ - Check parameters │
│ - TOWQU(task, device) if device busy │
│ - Activate driver if device free │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ 3. Driver initiates hardware I/O │
│ - Set device registers │
│ - Start DMA │
│ - Return to ID<level> (wait interrupt) │
└──────────────────┬───────────────────────────┘
│
│ (Time passes...)
│
┌──────────────────▼───────────────────────────┐
│ 4. Hardware completes, interrupts │
│ - Driver wakes from ID<level> │
│ - Read hardware status │
│ - Update datafield │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ 5. Driver wakes task │
│ - FWQU(task, device) │
│ - RTENTRY(task) │
│ - Check for next queued request │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ 6. Scheduler runs task │
│ - Task resumes from monitor call │
│ - I/O data available in buffer │
└──────────────────────────────────────────────┘
9. C# Emulator Implementation¶
9.1 Device Datafield Class¶
namespace RetroCore.Emulated.SINTRAN.Drivers
{
/// <summary>
/// Represents a device datafield (Device Control Block)
/// </summary>
public class DeviceDatafield
{
// Base address in memory
public ushort Address { get; set; }
// Standard fields (offsets from Address)
public const int RESLINK_OFFSET = 0;
public const int RTRES_OFFSET = 1;
public const int BWLINK_OFFSET = 2;
public const int TYPRING_OFFSET = 4;
public const int MLINK_OFFSET = 5;
public const int MFUNC_OFFSET = 6;
public const int HDEV_OFFSET = 8;
private readonly SINTRANKernel _kernel;
public DeviceDatafield(SINTRANKernel kernel, ushort address)
{
_kernel = kernel;
Address = address;
}
// Standard field accessors
public ushort RESLINK
{
get => _kernel.Memory.Read(Address + RESLINK_OFFSET);
set => _kernel.Memory.Write(Address + RESLINK_OFFSET, value);
}
public ushort RTRES
{
get => _kernel.Memory.Read(Address + RTRES_OFFSET);
set => _kernel.Memory.Write(Address + RTRES_OFFSET, value);
}
public ushort BWLINK
{
get => _kernel.Memory.Read(Address + BWLINK_OFFSET);
set => _kernel.Memory.Write(Address + BWLINK_OFFSET, value);
}
public ushort TYPRING
{
get => _kernel.Memory.Read(Address + TYPRING_OFFSET);
set => _kernel.Memory.Write(Address + TYPRING_OFFSET, value);
}
public ushort HDEV
{
get => _kernel.Memory.Read(Address + HDEV_OFFSET);
set => _kernel.Memory.Write(Address + HDEV_OFFSET, value);
}
// Helper properties
public bool IsFree => RTRES == 0;
public bool IsReserved => RTRES == 0xFFFF;
public bool IsOwned => RTRES != 0 && RTRES != 0xFFFF;
public ushort OwnerTask => IsOwned ? RTRES : (ushort)0;
public bool HasWaitingTasks => BWLINK != 0xFFFF;
/// <summary>
/// Get list of tasks waiting for this device
/// </summary>
public List<ushort> GetWaitingTasks()
{
var tasks = new List<ushort>();
ushort current = BWLINK;
while (current != 0xFFFF)
{
tasks.Add(current);
current = _kernel.Memory.ReadField(current, "WLINK");
}
return tasks;
}
/// <summary>
/// Get device type name
/// </summary>
public string GetDeviceType()
{
byte type = (byte)(TYPRING & 0xFF);
return type switch
{
0x01 => "Terminal Input",
0x02 => "Terminal Output",
0x10 => "Disk",
0x11 => "Tape",
0x20 => "Line Printer",
0x30 => "Card Reader",
0x40 => "HDLC",
0x41 => "X.21",
_ => $"Unknown (0x{type:X2})"
};
}
}
}
9.2 Device Manager¶
namespace RetroCore.Emulated.SINTRAN.Drivers
{
/// <summary>
/// Manages all devices in the system
/// </summary>
public class DeviceManager
{
private readonly SINTRANKernel _kernel;
// Ident tables
private ushort _itb10Address; // Level 10 (Output)
private ushort _itb11Address; // Level 11 (Storage)
private ushort _itb12Address; // Level 12 (Input)
private int _itb10Size;
private int _itb11Size;
private int _itb12Size;
public DeviceManager(SINTRANKernel kernel)
{
_kernel = kernel;
}
/// <summary>
/// Initialize device manager with ident table addresses
/// </summary>
public void Initialize(
ushort itb10, int size10,
ushort itb11, int size11,
ushort itb12, int size12)
{
_itb10Address = itb10;
_itb11Address = itb11;
_itb12Address = itb12;
_itb10Size = size10;
_itb11Size = size11;
_itb12Size = size12;
}
/// <summary>
/// Get datafield address from ident code and level
/// </summary>
public ushort GetDatafieldFromIdent(int level, byte identCode)
{
ushort tableAddress = level switch
{
10 => _itb10Address,
11 => _itb11Address,
12 => _itb12Address,
_ => throw new ArgumentException($"Invalid level: {level}")
};
if (identCode == 0)
throw new ArgumentException("Ident code must be >= 1");
// Ident table is 0-based, ident codes are 1-based
ushort entryAddress = (ushort)(tableAddress + identCode - 1);
return _kernel.Memory.Read(entryAddress);
}
/// <summary>
/// Get all devices at a specific level
/// </summary>
public List<DeviceDatafield> GetDevicesAtLevel(int level)
{
var devices = new List<DeviceDatafield>();
(ushort tableAddr, int size) = level switch
{
10 => (_itb10Address, _itb10Size),
11 => (_itb11Address, _itb11Size),
12 => (_itb12Address, _itb12Size),
_ => throw new ArgumentException($"Invalid level: {level}")
};
for (int i = 0; i < size; i++)
{
ushort datafieldAddr = _kernel.Memory.Read((ushort)(tableAddr + i));
if (datafieldAddr != 0)
{
devices.Add(new DeviceDatafield(_kernel, datafieldAddr));
}
}
return devices;
}
/// <summary>
/// Get all devices in the system
/// </summary>
public List<DeviceDatafield> GetAllDevices()
{
var devices = new List<DeviceDatafield>();
devices.AddRange(GetDevicesAtLevel(10));
devices.AddRange(GetDevicesAtLevel(11));
devices.AddRange(GetDevicesAtLevel(12));
return devices;
}
/// <summary>
/// Get device by hardware ident
/// </summary>
public DeviceDatafield GetDevice(int level, byte identCode)
{
ushort addr = GetDatafieldFromIdent(level, identCode);
return new DeviceDatafield(_kernel, addr);
}
}
}
10. Device Traversal and Inspection¶
10.1 Device Status Inspector¶
namespace RetroCore.Emulated.SINTRAN.Drivers
{
/// <summary>
/// Provides detailed inspection of device status
/// </summary>
public class DeviceInspector
{
private readonly DeviceManager _deviceManager;
private readonly SINTRANKernel _kernel;
public DeviceInspector(DeviceManager deviceManager, SINTRANKernel kernel)
{
_deviceManager = deviceManager;
_kernel = kernel;
}
/// <summary>
/// Get comprehensive device status report
/// </summary>
public DeviceStatusReport GetDeviceStatus(DeviceDatafield device)
{
var report = new DeviceStatusReport
{
Address = device.Address,
DeviceType = device.GetDeviceType(),
HardwareIdent = device.HDEV,
IsFree = device.IsFree,
IsReserved = device.IsReserved,
IsOwned = device.IsOwned
};
if (device.IsOwned)
{
ushort owner = device.OwnerTask;
report.OwnerTask = owner;
report.OwnerTaskName = GetTaskName(owner);
report.OwnerTaskPriority = GetTaskPriority(owner);
}
if (device.HasWaitingTasks)
{
var waitingTasks = device.GetWaitingTasks();
report.WaitingTaskCount = waitingTasks.Count;
report.WaitingTasks = waitingTasks
.Select(t => new WaitingTaskInfo
{
TaskAddress = t,
TaskName = GetTaskName(t),
Priority = GetTaskPriority(t)
})
.ToList();
}
return report;
}
/// <summary>
/// Get system-wide device statistics
/// </summary>
public DeviceStatistics GetSystemStatistics()
{
var allDevices = _deviceManager.GetAllDevices();
return new DeviceStatistics
{
TotalDevices = allDevices.Count,
FreeDevices = allDevices.Count(d => d.IsFree),
ReservedDevices = allDevices.Count(d => d.IsReserved),
OwnedDevices = allDevices.Count(d => d.IsOwned),
DevicesWithWaitingTasks = allDevices.Count(d => d.HasWaitingTasks),
TotalWaitingTasks = allDevices.Sum(d => d.GetWaitingTasks().Count),
OutputDevices = _deviceManager.GetDevicesAtLevel(10).Count,
StorageDevices = _deviceManager.GetDevicesAtLevel(11).Count,
InputDevices = _deviceManager.GetDevicesAtLevel(12).Count
};
}
/// <summary>
/// Print device report to console
/// </summary>
public void PrintDeviceReport(DeviceDatafield device)
{
var report = GetDeviceStatus(device);
Console.WriteLine($"Device @ 0x{device.Address:X4}");
Console.WriteLine($" Type: {report.DeviceType}");
Console.WriteLine($" Hardware Ident: {report.HardwareIdent}");
Console.WriteLine($" Status: {GetStatusString(report)}");
if (report.IsOwned)
{
Console.WriteLine($" Owner: {report.OwnerTaskName} (0x{report.OwnerTask:X4}), Priority {report.OwnerTaskPriority}");
}
if (report.WaitingTaskCount > 0)
{
Console.WriteLine($" Waiting Tasks: {report.WaitingTaskCount}");
foreach (var task in report.WaitingTasks)
{
Console.WriteLine($" - {task.TaskName} (0x{task.TaskAddress:X4}), Priority {task.Priority}");
}
}
}
/// <summary>
/// Print system-wide device report
/// </summary>
public void PrintSystemReport()
{
Console.WriteLine("=== SINTRAN Device System Report ===\n");
var stats = GetSystemStatistics();
Console.WriteLine($"Total Devices: {stats.TotalDevices}");
Console.WriteLine($" - Output (Level 10): {stats.OutputDevices}");
Console.WriteLine($" - Storage (Level 11): {stats.StorageDevices}");
Console.WriteLine($" - Input (Level 12): {stats.InputDevices}");
Console.WriteLine();
Console.WriteLine($"Device Status:");
Console.WriteLine($" - Free: {stats.FreeDevices}");
Console.WriteLine($" - Reserved: {stats.ReservedDevices}");
Console.WriteLine($" - Owned: {stats.OwnedDevices}");
Console.WriteLine();
Console.WriteLine($"Waiting Tasks:");
Console.WriteLine($" - Devices with waiting tasks: {stats.DevicesWithWaitingTasks}");
Console.WriteLine($" - Total waiting tasks: {stats.TotalWaitingTasks}");
Console.WriteLine();
// Print each device
Console.WriteLine("=== Device Details ===\n");
foreach (int level in new[] { 10, 11, 12 })
{
Console.WriteLine($"--- Level {level} Devices ---");
var devices = _deviceManager.GetDevicesAtLevel(level);
foreach (var device in devices)
{
PrintDeviceReport(device);
Console.WriteLine();
}
}
}
private string GetTaskName(ushort rtDesc)
{
// Try to read task name from RT-description
// (Implementation depends on where task names are stored)
return $"Task_{rtDesc:X4}";
}
private byte GetTaskPriority(ushort rtDesc)
{
ushort actpri = _kernel.Memory.ReadField(rtDesc, "ACTPRI");
return (byte)(actpri & 0xFF);
}
private string GetStatusString(DeviceStatusReport report)
{
if (report.IsFree) return "Free";
if (report.IsReserved) return "System Reserved";
if (report.IsOwned) return "Owned";
return "Unknown";
}
}
// Supporting classes
public class DeviceStatusReport
{
public ushort Address { get; set; }
public string DeviceType { get; set; }
public ushort HardwareIdent { get; set; }
public bool IsFree { get; set; }
public bool IsReserved { get; set; }
public bool IsOwned { get; set; }
public ushort OwnerTask { get; set; }
public string OwnerTaskName { get; set; }
public byte OwnerTaskPriority { get; set; }
public int WaitingTaskCount { get; set; }
public List<WaitingTaskInfo> WaitingTasks { get; set; } = new();
}
public class WaitingTaskInfo
{
public ushort TaskAddress { get; set; }
public string TaskName { get; set; }
public byte Priority { get; set; }
}
public class DeviceStatistics
{
public int TotalDevices { get; set; }
public int FreeDevices { get; set; }
public int ReservedDevices { get; set; }
public int OwnedDevices { get; set; }
public int DevicesWithWaitingTasks { get; set; }
public int TotalWaitingTasks { get; set; }
public int OutputDevices { get; set; }
public int StorageDevices { get; set; }
public int InputDevices { get; set; }
}
}
10.2 Usage Example¶
// Initialize device manager
var deviceManager = new DeviceManager(kernel);
deviceManager.Initialize(
itb10: 0x1000, size10: 16,
itb11: 0x1010, size11: 16,
itb12: 0x1020, size12: 16
);
// Create inspector
var inspector = new DeviceInspector(deviceManager, kernel);
// Print system report
inspector.PrintSystemReport();
// Inspect specific device (e.g., disk controller on Level 11, ident 3)
var diskDevice = deviceManager.GetDevice(11, 3);
inspector.PrintDeviceReport(diskDevice);
// Get statistics
var stats = inspector.GetSystemStatistics();
Console.WriteLine($"Total waiting tasks: {stats.TotalWaitingTasks}");
Example output:
=== SINTRAN Device System Report ===
Total Devices: 18
- Output (Level 10): 6
- Storage (Level 11): 8
- Input (Level 12): 4
Device Status:
- Free: 12
- Reserved: 0
- Owned: 6
Waiting Tasks:
- Devices with waiting tasks: 3
- Total waiting tasks: 7
=== Device Details ===
--- Level 11 Devices ---
Device @ 0x2500
Type: Disk
Hardware Ident: 3
Status: Owned
Owner: RTDISK (0x3200), Priority 150
Waiting Tasks: 2
- RTBATCH (0x3300), Priority 100
- RTUSER1 (0x3400), Priority 80
Device @ 0x2520
Type: Disk
Hardware Ident: 4
Status: Free
Appendix A: Quick Reference¶
Device Datafield Standard Fields¶
| Offset | Field | Purpose |
|---|---|---|
| 0 | RESLINK | Reservation link |
| 1 | RTRES | Owner task (0=free, -1=system) |
| 2 | BWLINK | Waiting queue head |
| 4 | TYPRING | Device type |
| 5 | MLINK | Monitor queue link |
| 6 | MFUNC | Monitor function |
| 8+ | (device) | Device-specific |
Key Driver Functions¶
| Function | Purpose | Level |
|---|---|---|
| ID10 | Wait for Level 10 interrupt | 10 |
| ID11 | Wait for Level 11 interrupt | 11 |
| ID12 | Wait for Level 12 interrupt | 12 |
| TOWQU | Add task to waiting queue | Monitor |
| FWQU | Remove from waiting queue | Monitor |
| RTENTRY | Add task to execution queue | Monitor |
Device Type Codes¶
| Code | Type |
|---|---|
| 0x01 | Terminal Input |
| 0x02 | Terminal Output |
| 0x10 | Disk |
| 0x11 | Tape |
| 0x20 | Line Printer |
| 0x30 | Card Reader |
| 0x40 | HDLC |
| 0x41 | X.21 |
Appendix B: Related Documentation¶
- Chapter 02: Queue Structures (waiting queue operations)
- Chapter 15: Disk I/O Subsystem (detailed disk driver)
- Chapter 17: Scheduler and Priorities (task state transitions)
- Chapter 13: INT 14 Handler (monitor call entry)
End of Document