Skip to content

Segment 31 "Other CPU" Mechanism - Complete Deep Dive

Every Detail: Bits, Fields, Structs, DMA, TAG Protocol, and Signal Flow


Table of Contents

  1. Complete Architecture Overview
  2. Segment 31 Capability Bits - Detailed
  3. 5MPM Message Buffer Structure - Every Field
  4. MICFU Codes - Complete Catalog
  5. TAG Register Protocol - Complete State Machine
  6. 3022/5015 Interface Registers - Every Bit
  7. DMA Operations and Memory Access
  8. ND-500 Trap Handler - Complete Flow
  9. ND-100 Interrupt Handler - Complete Flow
  10. SINTRAN III MON Call Mapping
  11. Signal Back Mechanism - Resume Flow
  12. Complete Example with Every Step
  13. Memory Access Mechanisms
  14. Process State Structures

Complete Architecture Overview

Full System Architecture with All Components

graph TB
    subgraph "ND-500 CPU Hardware"
        PC500[Program Counter<br/>32-bit]
        REG500[Registers<br/>R0-R15, A/Q/D/L/B]
        STATUS500[Status Register<br/>Flags: Z,C,M,K,etc]
        CAP500[Capability Registers<br/>32 Program + 32 Data<br/>16-bit each]
        TOS500[TOS Register<br/>Points to stack/heap]
    end

    subgraph "ND-500 Memory Management"
        SEG500[Segment Translation<br/>32 segments × 16MB each]
        PTABLE500[Page Tables<br/>4KB pages]
        CACHE500[TLB Cache]
    end

    subgraph "5MPM - Multiport Memory"
        direction TB
        MPM_PROC[Process Descriptors<br/>16 processes × 512 bytes]
        MPM_MSG[Message Buffers<br/>16 × 256 bytes]
        MPM_DATA[Shared Data Area<br/>Up to 2MB total]
        MPM_PAGE[Page Swap Area<br/>For paging]
    end

    subgraph "5015 Interface Card (ND-500 Side)"
        TAG_OUT[TAG-OUT Register<br/>16-bit, Write-only]
        STATUS_5015[Status Register<br/>5ALIVE, 5FAULT bits]
        DMA_5015[DMA Controller<br/>For 5MPM access]
        INT_5015[Interrupt Logic<br/>To ND-100]
    end

    subgraph "3022 Interface Card (ND-100 Side)"
        TAG_IN[TAG-IN Register<br/>16-bit, Read via IOX]
        STATUS_3022[Status Register<br/>Read via IOX offset 0]
        CONTROL_3022[Control Register<br/>Write via IOX offset 3]
        MAR[Memory Address Reg<br/>24-bit for 5MPM]
        DATA_3022[Data Register<br/>16-bit transfer]
        DMA_3022[DMA Controller<br/>For bulk transfers]
    end

    subgraph "ND-100 CPU Hardware"
        PC100[Program Counter<br/>15-bit]
        REG100[Registers<br/>A, D, T, P, L, X, B]
        PIE[Priority Interrupt Enable<br/>16 bits - levels 0-15]
        PID[Priority Interrupt Detect<br/>16 bits - current requests]
        PTABLE100[Page Tables<br/>4 tables × 64 entries]
    end

    subgraph "SINTRAN III Kernel"
        INT12[Level 12 Handler<br/>3022 interrupts]
        DISPATCH[MICFU Dispatcher<br/>Function routing]
        FILESYS[File System<br/>RFILE/WFILE handlers]
        DEVMGR[Device Manager<br/>DVIO handlers]
        SEGMGR[Segment Manager<br/>Paging, allocation]
    end

    PC500 --> CAP500
    CAP500 --> SEG500
    SEG500 --> PTABLE500

    PTABLE500 <-->|Memory access| MPM_DATA

    MPM_PROC --> MPM_MSG
    MPM_MSG --> MPM_DATA

    DMA_5015 <-->|Direct access| MPM_DATA
    DMA_3022 <-->|Direct access| MPM_DATA

    TAG_OUT -->|Hardware wire| TAG_IN
    INT_5015 -->|Interrupt line| PID

    STATUS_3022 -->|IOX read| REG100
    TAG_IN -->|IOX read| REG100
    REG100 -->|IOX write| CONTROL_3022
    REG100 -->|IOX write| TAG_OUT

    PID --> INT12
    INT12 --> DISPATCH
    DISPATCH --> FILESYS
    DISPATCH --> DEVMGR
    DISPATCH --> SEGMGR

Segment 31 Capability Bits - Detailed

Capability Register Format (16 bits)

graph LR
    subgraph "16-bit Capability Word"
        direction LR
        B15[Bit 15<br/>I<br/>Indirect]
        B14[Bit 14<br/>O<br/>Other CPU]
        B13[Bit 13<br/>A<br/>Absolute]
        B12[Bit 12<br/>W<br/>Writable]
        B11[Bit 11<br/>E<br/>Executable]
        B10[Bit 10<br/>R<br/>Readable]
        B9_0[Bits 9-0<br/>Reserved/Segment Base]
    end

    B15 --> IND{Indirect<br/>Translation?}
    B14 --> OTHER{Other CPU<br/>TRAP?}
    B13 --> ABS{Absolute<br/>Addressing?}
    B12 --> WR{Write<br/>Allowed?}
    B11 --> EX{Execute<br/>Allowed?}
    B10 --> RD{Read<br/>Allowed?}

Segment 31 Initialization During PLACE-DOMAIN

sequenceDiagram
    participant MON as ND-500 Monitor
    participant DESC as Process Descriptor<br/>(in 5MPM)
    participant CPU as ND-500 CPU

    Note over MON: PLACE-DOMAIN command

    MON->>DESC: Allocate descriptor<br/>Process N at offset N×512

    MON->>DESC: Write segments 0-30<br/>Normal capabilities

    Note over MON: Setup Segment 31 specially

    MON->>DESC: ProgramCap[31] = 0xC000
    Note right of DESC: Bits: 11000000_00000000<br/>I=1 (Indirect)<br/>O=1 (Other CPU)<br/>All other=0

    MON->>DESC: DataCap[31] = 0xC000
    Note right of DESC: Same value for data access

    MON->>DESC: Write entry point address
    MON->>DESC: Write stack pointer
    MON->>DESC: Initialize registers

    MON->>CPU: Load capabilities from descriptor
    CPU->>CPU: ProgramCapabilities[31] ← 0xC000
    CPU->>CPU: DataCapabilities[31] ← 0xC000

    Note over CPU: Process ready to run

Capability Bit Field Structure (C-style)

typedef struct {
    uint16_t reserved_segment_base : 10;  // Bits 0-9: Reserved or segment base
    uint16_t readable              : 1;   // Bit 10: R - Read permission
    uint16_t executable            : 1;   // Bit 11: E - Execute permission
    uint16_t writable              : 1;   // Bit 12: W - Write permission
    uint16_t absolute              : 1;   // Bit 13: A - Absolute addressing
    uint16_t other_cpu             : 1;   // Bit 14: O - Other CPU trap
    uint16_t indirect              : 1;   // Bit 15: I - Indirect translation
} SegmentCapability;

// For segment 31 during PLACE-DOMAIN:
SegmentCapability seg31 = {
    .reserved_segment_base = 0,
    .readable              = 0,
    .executable            = 0,
    .writable              = 0,
    .absolute              = 0,
    .other_cpu             = 1,  // ← KEY BIT!
    .indirect              = 1
};
// Result: 0xC000 = binary 1100000000000000

Detection Logic in CPU

flowchart TD
    START([CPU Executes Instruction]) --> FETCH[Fetch Instruction<br/>from PC]

    FETCH --> DECODE{Instruction<br/>Type?}

    DECODE -->|CALLG| CALLG_PROC[Process CALLG Instruction]
    DECODE -->|Other| NORMAL[Execute Normally]

    CALLG_PROC --> EXTRACT[Extract Target Address<br/>32-bit operand]

    EXTRACT --> SEGMENT[Extract Segment Number<br/>Top 5 bits: (addr >> 27) & 0x1F]

    SEGMENT --> CHECK31{Segment == 31<br/>0x1F?}

    CHECK31 -->|No| NORM_CALL[Normal CALLG<br/>Fetch from segment]
    CHECK31 -->|Yes| READ_CAP[Read ProgramCapabilities[31]]

    READ_CAP --> CAP_VAL[Capability Value]

    CAP_VAL --> CHECK_O{Bit 14<br/>O bit<br/>Set?}

    CHECK_O -->|0| NORM_CALL
    CHECK_O -->|1| TRAP[TRIGGER TRAP!<br/>Other CPU Trap]

    TRAP --> SAVE_STATE[Save CPU State]
    SAVE_STATE --> CALL_HANDLER[Call Trap Handler]
    CALL_HANDLER --> END([Wait for ND-100])

    NORM_CALL --> END2([Continue Execution])
    NORMAL --> END2

5MPM Message Buffer Structure - Every Field

Complete Memory Map

graph TB
    subgraph "5MPM Physical Layout"
        direction TB

        BASE["5MPM Base Address<br/>ND-500: 0x80000000<br/>ND-100: 0x00040000"]

        PROC_DESC["Process Descriptors<br/>Offset: 0x0000<br/>16 processes × 512 bytes<br/>= 8192 bytes (0x2000)"]

        MSG_BUF["Message Buffers<br/>Offset: 0x0400<br/>16 processes × 256 bytes<br/>= 4096 bytes (0x1000)"]

        SHARED["Shared Data Area<br/>Offset: 0x1400<br/>Varies by config<br/>Up to ~2MB"]

        PAGE_SWAP["Page Swap Area<br/>At high addresses<br/>For demand paging"]

        BASE --> PROC_DESC
        PROC_DESC --> MSG_BUF
        MSG_BUF --> SHARED
        SHARED --> PAGE_SWAP
    end

    subgraph "Address Calculation"
        PROC_NUM[Process Number: N<br/>0-15]

        DESC_ADDR["Descriptor Address:<br/>Base + (N × 512)"]
        MSG_ADDR["Message Buffer Address:<br/>Base + 0x400 + (N × 256)"]

        PROC_NUM --> DESC_ADDR
        PROC_NUM --> MSG_ADDR
    end

Message Buffer Structure - Every Byte

Each process has a 256-byte message buffer at: 5MPM_BASE + 0x400 + (ProcessNum × 0x100)

typedef struct {
    // === CPU STATE SAVE AREA (64 bytes) ===
    // Offset 0x00-0x3F
    uint32_t saved_pc;              // +0x00: Program Counter
    uint32_t saved_status;          // +0x04: Status register
    uint32_t saved_r0;              // +0x08: Register R0
    uint32_t saved_r1;              // +0x0C: Register R1
    uint32_t saved_r2;              // +0x10: Register R2
    uint32_t saved_r3;              // +0x14: Register R3
    uint32_t saved_r4;              // +0x18: Register R4
    uint32_t saved_r5;              // +0x1C: Register R5
    uint32_t saved_r6;              // +0x20: Register R6
    uint32_t saved_r7;              // +0x24: Register R7
    uint32_t saved_a;               // +0x28: A register (accumulator)
    uint32_t saved_q;               // +0x2C: Q register
    uint32_t saved_d;               // +0x30: D register
    uint32_t saved_l;               // +0x34: L register (link)
    uint32_t saved_b;               // +0x38: B register (base)
    uint32_t saved_tos;             // +0x3C: TOS register

    // === MONITOR CALL PARAMETERS (64 bytes) ===
    // Offset 0x40-0x7F
    uint16_t process_number;        // +0x40: Process number (0-15)
    uint16_t micfu_code;            // +0x42: Monitor function code (16-bit)
    uint16_t error_code;            // +0x44: Result error code
    uint16_t flags;                 // +0x46: Control flags

    // ITMQUEUE flag structure (part of flags):
    // Bit 0: ITMQUEUE (1=pending, 0=complete)
    // Bit 1: PRIORITY (1=high priority call)
    // Bit 2: RETRY (1=retry after error)
    // Bits 3-15: Reserved

    uint32_t param1;                // +0x48: Generic parameter 1
    uint32_t param2;                // +0x4C: Generic parameter 2
    uint32_t param3;                // +0x50: Generic parameter 3
    uint32_t param4;                // +0x54: Generic parameter 4
    uint32_t param5;                // +0x58: Generic parameter 5
    uint32_t param6;                // +0x5C: Generic parameter 6
    uint32_t param7;                // +0x60: Generic parameter 7
    uint32_t param8;                // +0x64: Generic parameter 8

    // Specific parameter overlays (union with above):
    struct {
        uint16_t device_number;     // +0x48: For DVIO - device number
        uint16_t reserved1;
        uint32_t buffer_address;    // +0x4C: Buffer address in ND-500 space
        uint32_t byte_count;        // +0x50: Number of bytes
        uint32_t control_word;      // +0x54: Device control word
    } dvio_params;

    struct {
        uint32_t file_number;       // +0x48: Open file number
        uint32_t block_number;      // +0x4C: Logical block number
        uint32_t buffer_address;    // +0x50: Buffer address
        uint32_t word_count;        // +0x54: Words to transfer
    } file_params;

    struct {
        uint32_t fault_address;     // +0x48: Virtual address that faulted
        uint32_t segment_number;    // +0x4C: Segment that faulted
        uint32_t page_number;       // +0x50: Page within segment
        uint32_t access_type;       // +0x54: Read/Write/Execute
    } page_fault_params;

    // === TIMING AND STATISTICS (32 bytes) ===
    // Offset 0x80-0x9F
    uint32_t call_count;            // +0x80: Number of monitor calls
    uint32_t total_wait_time;       // +0x84: Total wait time (ms)
    uint32_t last_call_time;        // +0x88: Timestamp of last call
    uint32_t max_wait_time;         // +0x8C: Maximum wait time seen
    uint32_t error_count;           // +0x90: Number of errors
    uint32_t retry_count;           // +0x94: Number of retries
    uint64_t reserved_stats;        // +0x98: Reserved for future stats

    // === RESERVED / SCRATCH AREA (96 bytes) ===
    // Offset 0xA0-0xFF
    uint8_t scratch[96];            // Scratch space for complex calls

} MessageBuffer;  // Total: 256 bytes (0x100)

Message Buffer Memory Layout Diagram

graph TB
    subgraph "Message Buffer for Process N"
        direction TB

        OFF00["0x00-0x3F (64 bytes)<br/>════════════════════<br/>CPU STATE SAVE<br/>PC, Status, R0-R7<br/>A, Q, D, L, B, TOS"]

        OFF40["0x40-0x47 (8 bytes)<br/>════════════════════<br/>HEADER<br/>Process#, MICFU<br/>Error, Flags"]

        OFF48["0x48-0x7F (56 bytes)<br/>════════════════════<br/>PARAMETERS<br/>8 × 32-bit params<br/>or specialized structs"]

        OFF80["0x80-0x9F (32 bytes)<br/>════════════════════<br/>STATISTICS<br/>Timing, counts<br/>Error tracking"]

        OFFA0["0xA0-0xFF (96 bytes)<br/>════════════════════<br/>SCRATCH AREA<br/>For complex operations"]

        OFF00 --> OFF40
        OFF40 --> OFF48
        OFF48 --> OFF80
        OFF80 --> OFFA0
    end

Detailed ITMQUEUE Flag Bits

graph LR
    subgraph "Flags Word at Offset +0x46 (16 bits)"
        B0[Bit 0<br/>ITMQUEUE<br/>1=Pending<br/>0=Complete]
        B1[Bit 1<br/>PRIORITY<br/>1=High<br/>0=Normal]
        B2[Bit 2<br/>RETRY<br/>1=Retry<br/>0=First]
        B3[Bit 3<br/>INTERRUPT<br/>1=Async<br/>0=Sync]
        B4_15[Bits 4-15<br/>RESERVED<br/>Must be 0]
    end

    B0 --> CHECK0{Checked by<br/>ND-100}
    B1 --> PRIORITY_Q[Priority Queue<br/>Selection]
    B2 --> RETRY_LOGIC[Retry Handler]
    B3 --> INT_MODE[Interrupt vs<br/>Polling Mode]

MICFU Codes - Complete Catalog

MICFU Code Space (16-bit)

graph TB
    MICFU[MICFU Code Space<br/>16-bit = 65,536 codes]

    MICFU --> RANGE0[0x0000-0x000F<br/>Device I/O]
    MICFU --> RANGE1[0x0010-0x002F<br/>File Operations]
    MICFU --> RANGE2[0x0030-0x004F<br/>Memory Management]
    MICFU --> RANGE3[0x0050-0x006F<br/>Process Control]
    MICFU --> RANGE4[0x0070-0x008F<br/>System Calls]
    MICFU --> RANGE5[0x0090-0x00FF<br/>User Extensions]
    MICFU --> RANGE6[0x0100-0x01FF<br/>Network/XMSG]
    MICFU --> RANGE7[0x0200+<br/>Reserved/Future]

    RANGE0 --> DVIO_IN[0x0000: DVIO IN]
    RANGE0 --> DVIO_OUT[0x0001: DVIO OUT]
    RANGE0 --> DVIO_CTRL[0x0002: DVIO CONTROL]
    RANGE0 --> DVIO_STAT[0x0003: DVIO STATUS]

    RANGE1 --> RFILE[0x0010: RFILE<br/>Read file block]
    RANGE1 --> WFILE[0x0011: WFILE<br/>Write file block]
    RANGE1 --> OPEN[0x0012: OPEN<br/>Open file]
    RANGE1 --> CLOSE[0x0013: CLOSE<br/>Close file]
    RANGE1 --> DELETE[0x0014: DELETE<br/>Delete file]
    RANGE1 --> RENAME[0x0015: RENAME<br/>Rename file]
    RANGE1 --> FSTAT[0x0016: FSTAT<br/>File status]

    RANGE2 --> PGFAULT[0x0030: PAGE FAULT<br/>Load page]
    RANGE2 --> ALLOC_SEG[0x0031: ALLOCATE SEGMENT]
    RANGE2 --> FREE_SEG[0x0032: FREE SEGMENT]
    RANGE2 --> ATTACH[0x0040: ATTACH SEGMENT]
    RANGE2 --> DETACH[0x0041: DETACH SEGMENT]

Complete MICFU Code Table

MICFU Name MON Equivalent Parameters Description
Device I/O (0x00-0x0F)
0x0000 DVIO_IN MON 4 (INBT) device, buffer, count Input from device
0x0001 DVIO_OUT MON 5 (OUTBT) device, buffer, count Output to device
0x0002 DVIO_CONTROL MON 6 (IOCTL) device, control_word Device control
0x0003 DVIO_STATUS - device Get device status
0x0004 DVIO_RESERVE MON 32 (RSDEV) device Reserve device
0x0005 DVIO_RELEASE MON 33 (RLDEV) device Release device
File Operations (0x10-0x2F)
0x0010 RFILE MON 14 (RFILE) file#, block#, buffer, words Read file block
0x0011 WFILE MON 15 (WFILE) file#, block#, buffer, words Write file block
0x0012 OPEN MON 16 (OPEN) filename, mode Open file
0x0013 CLOSE MON 17 (CLOSE) file# Close file
0x0014 DELETE MON 20 (DELETE) filename Delete file
0x0015 RENAME MON 21 (RENAME) old_name, new_name Rename file
0x0016 FSTAT - file#, buffer Get file status
0x0017 CREATE MON 46 (CREATE) filename, size, type Create file
0x0018 LOCK - file#, block# Lock file record
0x0019 UNLOCK - file#, block# Unlock record
Program Loading (0x20-0x2F)
0x0020 LOAD MON 19 (LOAD) filename, address Load program
0x0021 DUMP MON 18 (DUMP) filename, address, size Dump program
0x0022 EXEC - filename, params Execute program
Memory Management (0x30-0x4F)
0x0030 PAGE_FAULT - address, seg#, page# Handle page fault
0x0031 ALLOC_SEGMENT - size Allocate segment
0x0032 FREE_SEGMENT - segment# Free segment
0x0033 ALLOC_PAGES - count Allocate pages
0x0034 FREE_PAGES - address, count Free pages
0x0040 ATTACH_SEGMENT MON 61 segment#, address Attach to ND-100 seg
0x0041 DETACH_SEGMENT MON 61 segment# Detach segment
Process Control (0x50-0x6F)
0x0050 CREATE_PROCESS - domain, priority Create process
0x0051 DELETE_PROCESS - process# Delete process
0x0052 SUSPEND_PROCESS - process# Suspend process
0x0053 RESUME_PROCESS - process# Resume process
0x0054 SET_PRIORITY - process#, priority Change priority
System Calls (0x70-0x8F)
0x0070 GETIME MON 50 (GETIME) buffer Get system time
0x0071 SETIME MON 51 (SETIME) time Set system time
0x0072 ALARM - time, signal Set alarm
0x0073 WAIT MON 8 (WAIT) event# Wait for event
0x0074 SIGNAL MON 9 (SIGNAL) event# Signal event
Network/XMSG (0x0100-0x01FF)
0x0100 XMSEND - dest, port, msg, len Send XMSG message
0x0101 XMRECV - port, buffer, len Receive XMSG
0x0102 XMOPEN - port Open XMSG port
0x0103 XMCLOSE - port Close XMSG port

MICFU Parameter Encoding

Different MICFU codes use different parameter layouts in the message buffer:

// Example 1: DVIO_OUT (0x0001)
struct MICFU_0001_Params {
    uint16_t device_number;      // +0x48: Which device (1=terminal, etc)
    uint16_t reserved;
    uint32_t buffer_address;     // +0x4C: ND-500 address of data
    uint32_t byte_count;         // +0x50: How many bytes
    uint32_t flags;              // +0x54: Control flags
    // Flags bits:
    // Bit 0: ECHO (for terminal input)
    // Bit 1: NOWAIT (non-blocking)
    // Bit 2: BINARY (vs text mode)
};

// Example 2: RFILE (0x0010)
struct MICFU_0010_Params {
    uint32_t file_number;        // +0x48: Open file handle
    uint32_t block_number;       // +0x4C: Logical block (512 bytes)
    uint32_t buffer_address;     // +0x50: Where to read data
    uint32_t word_count;         // +0x54: 16-bit words to read
    uint32_t options;            // +0x58: Read options
};

// Example 3: PAGE_FAULT (0x0030)
struct MICFU_0030_Params {
    uint32_t fault_address;      // +0x48: Address that caused fault
    uint32_t segment_number;     // +0x4C: Which segment (0-31)
    uint32_t page_number;        // +0x50: Page within segment
    uint32_t access_type;        // +0x54: 0=Read, 1=Write, 2=Execute
    uint32_t process_state;      // +0x58: Full CPU state pointer
};

TAG Register Protocol - Complete State Machine

TAG Register Communication

The 3022/5015 interface uses two 16-bit TAG registers for inter-CPU signaling:

TAG-OUT: Written by ND-500 (via trap handler), read by ND-100 (via IOX) TAG-IN: Written by ND-100 (via IOX), read by ND-500 (via trap handler)

stateDiagram-v2
    [*] --> IDLE: System Boot

    state "IDLE (Both TAGs = 0x0000)" as IDLE
    state "ND-500 Requesting" as REQ_500
    state "ND-100 Processing" as PROC_100
    state "ND-100 Responding" as RESP_100
    state "ND-500 Resuming" as RESUME_500

    IDLE --> REQ_500: ND-500 trap writes<br/>TAG-OUT = 0x0001

    REQ_500 --> PROC_100: ND-100 reads TAG-IN<br/>Sees 0x0001<br/>Triggers INT12

    note right of PROC_100
        ND-100 reads message buffer
        Dispatches to MICFU handler
        Executes operation
        Writes result to buffer
    end note

    PROC_100 --> RESP_100: ND-100 writes<br/>TAG-OUT = 0x0002<br/>(OPERATION_COMPLETE)

    RESP_100 --> RESUME_500: ND-500 trap handler<br/>reads TAG-IN<br/>Sees 0x0002

    note right of RESUME_500
        Restore CPU state
        Set R0 = error code
        Advance PC past CALLG
        Clear IsWaiting flag
    end note

    RESUME_500 --> IDLE: Both CPUs write<br/>TAGs back to 0x0000

    state "Error States" as ERROR
    PROC_100 --> ERROR: Timeout or<br/>error condition
    ERROR --> IDLE: Write TAG = 0x0004<br/>(ERROR_OCCURRED)

Complete TAG Value Table

TAG Value Name Direction Set By Read By Meaning
0x0000 IDLE Both Both Both No communication, system idle
0x0001 MON_CALL_REQUEST 500→100 ND-500 trap ND-100 INT12 ND-500 needs monitor call
0x0002 OPERATION_COMPLETE 100→500 ND-100 handler ND-500 trap ND-100 finished successfully
0x0003 PAGE_FAULT_REQUEST 500→100 ND-500 trap ND-100 swapper Page fault needs handling
0x0004 ERROR_OCCURRED 100→500 ND-100 handler ND-500 trap Error during operation
0x0005 RETRY_REQUEST 500→100 ND-500 trap ND-100 INT12 Retry after error
0x0006 INTERRUPT_REQUEST 500→100 ND-500 trap ND-100 INT12 Async interrupt
0x0007 ACK_INTERRUPT 100→500 ND-100 handler ND-500 trap Interrupt acknowledged
0x0010 DMA_REQUEST 500→100 DMA logic ND-100 DMA Request DMA transfer
0x0011 DMA_COMPLETE 100→500 ND-100 DMA ND-500 DMA DMA finished
0x0020 RESET_REQUEST Either Either Either Request system reset
0x00FF DIAGNOSTIC Either Test code Test code Diagnostic/test mode

TAG Protocol Timing Diagram

sequenceDiagram
    participant CPU500 as ND-500 CPU
    participant TRAP500 as ND-500 Trap Handler
    participant TAG_OUT as TAG-OUT Register
    participant TAG_IN as TAG-IN Register
    participant INT100 as ND-100 Interrupt
    participant HAND100 as ND-100 Handler

    Note over CPU500: Executing CALLG #0x1F000000

    CPU500->>TRAP500: Trigger Other CPU trap
    activate TRAP500

    TRAP500->>TAG_OUT: Write 0x0001<br/>(MON_CALL_REQUEST)
    Note over TAG_OUT: Hardware updates TAG-IN<br/>on ND-100 side

    TRAP500->>CPU500: Set IsWaiting = true
    Note over CPU500: ND-500 SUSPENDED
    deactivate TRAP500

    Note over TAG_IN: ND-100 polling TAG-IN
    TAG_IN-->>INT100: Value changed to 0x0001
    INT100->>INT100: Set PID bit 12<br/>(interrupt level 12)

    activate HAND100
    HAND100->>TAG_IN: Read via IOX (device 100₈, offset 1)
    TAG_IN-->>HAND100: Returns 0x0001

    Note over HAND100: Dispatch to MICFU handler<br/>Process operation<br/>Write result

    HAND100->>TAG_OUT: Write 0x0002<br/>(OPERATION_COMPLETE)<br/>via IOX offset 2
    deactivate HAND100

    Note over TAG_OUT: Hardware updates TAG-IN<br/>on ND-500 side

    TAG_OUT-->>TRAP500: Wake up trap handler
    activate TRAP500
    TRAP500->>TAG_IN: Read value
    TAG_IN-->>TRAP500: Returns 0x0002

    TRAP500->>CPU500: Restore state<br/>Clear IsWaiting
    Note over CPU500: ND-500 RESUMES
    deactivate TRAP500

    TRAP500->>TAG_OUT: Write 0x0000 (IDLE)
    HAND100->>TAG_OUT: Write 0x0000 (IDLE)

    Note over TAG_OUT,TAG_IN: Both TAGs now 0x0000<br/>Ready for next call

3022/5015 Interface Registers - Every Bit

Complete IOX Register Map (ND-100 Side)

The 3022 interface is accessed via IOX instructions with device number 100₈ (0x40 hex, 64 decimal).

// IOX register offsets for device 100₈
#define IOX_3022_DEVICE     0x40  // 100 octal

// Register offsets
#define IOX_STATUS          0     // Read-only: Status register
#define IOX_TAG_IN          1     // Read-only: TAG from ND-500
#define IOX_TAG_OUT         2     // Write-only: TAG to ND-500
#define IOX_CONTROL         3     // Write-only: Control register
#define IOX_MAR_LOW         4     // R/W: Memory Address Low
#define IOX_MAR_HIGH        5     // R/W: Memory Address High
#define IOX_DATA            6     // R/W: Data transfer
#define IOX_DMA_COUNT       7     // Write-only: DMA word count
#define IOX_INTERRUPT_MASK  8     // R/W: Interrupt enable mask

Status Register (Offset 0) - Every Bit

graph LR
    subgraph "Status Register - 16 bits (Read via IOX offset 0)"
        B15[Bit 15<br/>5ALIVE<br/>1=ND-500 Running]
        B14[Bit 14<br/>5FAULT<br/>1=ND-500 Fault]
        B13[Bit 13<br/>5PFAIL<br/>1=Power Fail]
        B12[Bit 12<br/>5DMAER<br/>1=DMA Error]
        B11[Bit 11<br/>TAGVAL<br/>1=TAG Valid]
        B10[Bit 10<br/>INTPEN<br/>1=Interrupt Pending]
        B9[Bit 9<br/>DMAACT<br/>1=DMA Active]
        B8[Bit 8<br/>Reserved]
        B7_0[Bits 7-0<br/>PROC_NUM<br/>Process that<br/>triggered interrupt]
    end

C Structure:

typedef union {
    uint16_t raw;
    struct {
        uint16_t process_number  : 8;  // Bits 0-7
        uint16_t reserved        : 1;  // Bit 8
        uint16_t dma_active      : 1;  // Bit 9
        uint16_t interrupt_pending : 1; // Bit 10
        uint16_t tag_valid       : 1;  // Bit 11
        uint16_t dma_error       : 1;  // Bit 12
        uint16_t power_fail      : 1;  // Bit 13
        uint16_t nd500_fault     : 1;  // Bit 14
        uint16_t nd500_alive     : 1;  // Bit 15
    } bits;
} Status3022Register;

Control Register (Offset 3) - Every Bit

graph LR
    subgraph "Control Register - 16 bits (Write via IOX offset 3)"
        B15[Bit 15<br/>RESET5<br/>1=Reset ND-500]
        B14[Bit 14<br/>INTEN<br/>1=Enable Interrupts]
        B13[Bit 13<br/>DMAEN<br/>1=Enable DMA]
        B12[Bit 12<br/>CLRINT<br/>1=Clear Interrupt]
        B11_8[Bits 11-8<br/>Reserved]
        B7_0[Bits 7-0<br/>PROC_MASK<br/>Process<br/>interrupt mask]
    end

C Structure:

typedef union {
    uint16_t raw;
    struct {
        uint16_t process_mask    : 8;  // Bits 0-7: Which processes can interrupt
        uint16_t reserved        : 4;  // Bits 8-11
        uint16_t clear_interrupt : 1;  // Bit 12: Write 1 to clear
        uint16_t dma_enable      : 1;  // Bit 13
        uint16_t interrupt_enable : 1; // Bit 14
        uint16_t reset_nd500     : 1;  // Bit 15: Write 1 to reset ND-500
    } bits;
} Control3022Register;

Memory Address Register (MAR) - 24-bit

The MAR is split across two 16-bit IOX registers:

// Building 24-bit 5MPM address
uint32_t mar_low = IOX_Read(IOX_3022_DEVICE, IOX_MAR_LOW);   // Bits 0-15
uint32_t mar_high = IOX_Read(IOX_3022_DEVICE, IOX_MAR_HIGH); // Bits 16-23

uint32_t mpm_address = (mar_high << 16) | mar_low;
// Result: 24-bit address, max 16MB (0x00000000 - 0x00FFFFFF)

Complete IOX Access Examples

; ND-100 assembly to read TAG-IN
        LDA     100         ; Device number 100₈
        STA     T           ; T register = device number
        LDA     1           ; Offset 1 (TAG-IN)
        IOXT                ; IOX with T, read to A
        ; A register now contains TAG-IN value

; ND-100 assembly to write TAG-OUT
        LDA     100         ; Device number
        STA     T
        LDA     2           ; TAG value to write
        STA     D           ; Put in D register
        LDA     2           ; Offset 2 (TAG-OUT)
        IOXT                ; IOX with T, write from D

; Read 5MPM memory via MAR/DATA
        LDA     100
        STA     T
        LDA     <low_addr>  ; Low 16 bits of 5MPM address
        IOXT    4           ; Write to MAR_LOW (offset 4)
        LDA     <high_addr> ; High 8 bits
        IOXT    5           ; Write to MAR_HIGH (offset 5)
        IOXT    6           ; Read from DATA (offset 6)
        ; A now contains 16-bit word from 5MPM

DMA Operations and Memory Access

DMA Controller Architecture

Both the 3022 and 5015 interfaces have DMA controllers for efficient bulk data transfer to/from 5MPM.

graph TB
    subgraph "ND-100 Side (3022)"
        CPU100[ND-100 CPU]
        DMA100[DMA Controller<br/>3022 Card]
        IOX100[IOX Bus]
        MEM100[ND-100 Memory]
    end

    subgraph "5MPM"
        MPM[Multiport Memory<br/>Dual-ported RAM]
    end

    subgraph "ND-500 Side (5015)"
        DMA500[DMA Controller<br/>5015 Card]
        CPU500[ND-500 CPU]
        MEM500[ND-500 Memory]
    end

    CPU100 -->|Setup DMA| DMA100
    DMA100 <-->|Burst transfer| MPM
    MPM <-->|Burst transfer| DMA500
    DMA500 -->|Interrupt| CPU500

    CPU100 -.Programmed I/O.-> IOX100
    IOX100 -.Single word.-> MPM

DMA Transfer Modes

Mode 1: ND-100 → 5MPM (Write)

// Setup DMA transfer from ND-100 memory to 5MPM
void Setup_DMA_Write(uint16_t nd100_address, uint32_t mpm_address,
                     uint16_t word_count)
{
    // Write 5MPM destination address to MAR
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_LOW, mpm_address & 0xFFFF);
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_HIGH, (mpm_address >> 16) & 0xFF);

    // Write ND-100 source address to internal DMA register
    // (implementation-specific, may use memory-mapped registers)
    DMA_SRC_ADDR = nd100_address;

    // Write word count
    IOX_Write(IOX_3022_DEVICE, IOX_DMA_COUNT, word_count);

    // Start DMA by setting DMA_ENABLE bit in control register
    uint16_t control = IOX_Read(IOX_3022_DEVICE, IOX_CONTROL);
    control |= (1 << 13);  // Set DMAEN bit
    IOX_Write(IOX_3022_DEVICE, IOX_CONTROL, control);

    // DMA proceeds automatically
    // When complete, DMAACT bit in status clears
    // Interrupt generated if enabled
}

Mode 2: 5MPM → ND-100 (Read)

void Setup_DMA_Read(uint32_t mpm_address, uint16_t nd100_address,
                    uint16_t word_count)
{
    // Write 5MPM source address to MAR
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_LOW, mpm_address & 0xFFFF);
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_HIGH, (mpm_address >> 16) & 0xFF);

    // Write ND-100 destination
    DMA_DST_ADDR = nd100_address;

    // Write count and start (direction bit = 0 for read)
    IOX_Write(IOX_3022_DEVICE, IOX_DMA_COUNT, word_count);

    uint16_t control = IOX_Read(IOX_3022_DEVICE, IOX_CONTROL);
    control |= (1 << 13);  // DMAEN
    control &= ~(1 << 14); // Direction = Read
    IOX_Write(IOX_3022_DEVICE, IOX_CONTROL, control);
}

DMA State Machine

stateDiagram-v2
    [*] --> IDLE: DMA Idle

    IDLE --> SETUP: CPU writes<br/>MAR, COUNT, CONTROL

    SETUP --> REQUESTING: Assert DMA REQ<br/>to memory arbiter

    REQUESTING --> GRANTED: Arbiter grants<br/>DMA access

    state GRANTED {
        [*] --> TRANSFER
        TRANSFER --> TRANSFER: Transfer each word<br/>Increment MAR<br/>Decrement COUNT
        TRANSFER --> [*]: COUNT = 0
    }

    GRANTED --> COMPLETE: All words transferred

    COMPLETE --> INT: Generate interrupt<br/>if INTEN set

    INT --> IDLE: Clear DMAACT bit

    GRANTED --> ERROR: Bus error or<br/>timeout
    ERROR --> IDLE: Set DMAER bit<br/>Generate interrupt

Programmed I/O vs DMA Performance

Aspect Programmed I/O DMA
CPU Involvement High - CPU does every transfer Low - CPU only sets up
Speed ~50K words/sec ~500K words/sec
CPU Overhead 100% during transfer ~5% (setup + completion)
Use Case Small transfers (<100 words) Large transfers (>100 words)
Typical Use TAG reading, single parameters Page swapping, large buffers

Decision Logic:

void Transfer_To_5MPM(uint32_t mpm_addr, void* data, uint16_t word_count)
{
    if (word_count < DMA_THRESHOLD)  // Typically 64-100 words
    {
        // Use programmed I/O for small transfers
        for (int i = 0; i < word_count; i++)
        {
            IOX_Write(IOX_3022_DEVICE, IOX_MAR_LOW, (mpm_addr + i*2) & 0xFFFF);
            IOX_Write(IOX_3022_DEVICE, IOX_MAR_HIGH, ((mpm_addr + i*2) >> 16) & 0xFF);
            IOX_Write(IOX_3022_DEVICE, IOX_DATA, ((uint16_t*)data)[i]);
        }
    }
    else
    {
        // Use DMA for large transfers
        Setup_DMA_Write(Local_Copy_Of_Data, mpm_addr, word_count);
        Wait_For_DMA_Complete();
    }
}

ND-500 Trap Handler - Complete Flow

Trap Handler Entry

flowchart TD
    START([CALLG #0x1F000000]) --> DETECT[CPU Detects Segment 31<br/>with O bit set]

    DETECT --> SAVE_PC[Save Current PC<br/>PC_TRAP = PC]

    SAVE_PC --> SAVE_STATUS[Save Status Register<br/>STATUS_TRAP = STATUS]

    SAVE_STATUS --> GET_PROC[Get Current Process Number<br/>from TOS or internal register]

    GET_PROC --> CALC_MSG[Calculate Message Buffer Address<br/>MSG_ADDR = 0x80000400 + (PROC_NUM × 0x100)]

    CALC_MSG --> SAVE_STATE[Save Complete CPU State]

    SAVE_STATE --> READ_MICFU[Read MICFU Code<br/>from message buffer<br/>(already written by library)]

    READ_MICFU --> VALIDATE{MICFU<br/>Valid?}

    VALIDATE -->|No| ERROR_TRAP[Raise Invalid MICFU Error]
    VALIDATE -->|Yes| SET_FLAGS[Set ITMQUEUE Flag<br/>flags |= 0x0001]

    SET_FLAGS --> WRITE_TAG[Write TAG-OUT Register<br/>TAG = 0x0001<br/>(MON_CALL_REQUEST)]

    WRITE_TAG --> TRIGGER_INT[Hardware triggers<br/>ND-100 Interrupt Level 12]

    TRIGGER_INT --> SET_WAIT[Set CPU.IsWaiting = true<br/>Process blocked]

    SET_WAIT --> SUSPEND([ND-500 Process SUSPENDED<br/>Execution halts])

Complete Trap Handler Code Flow

void HandleOtherCPUTrap(ND500CPU* cpu, uint32_t target_address)
{
    // 1. IDENTIFY PROCESS
    uint8_t process_num = cpu->CurrentProcess;  // 0-15

    printf("[ND500-TRAP] Process %d triggered Other CPU trap\n", process_num);
    printf("[ND500-TRAP] PC = 0x%08X, Target = 0x%08X\n",
           cpu->PC, target_address);

    // 2. CALCULATE MESSAGE BUFFER ADDRESS
    // ND-500 sees 5MPM at 0x80000000
    // Message buffers start at offset 0x400
    // Each process has 256 bytes (0x100)
    uint32_t mpm_base = 0x80000000;
    uint32_t msg_offset = 0x400 + (process_num * 0x100);
    uint32_t msg_addr = mpm_base + msg_offset;

    printf("[ND500-TRAP] Message buffer at 0x%08X\n", msg_addr);

    // 3. SAVE COMPLETE CPU STATE TO MESSAGE BUFFER
    // Offset +0x00: PC (32-bit)
    WriteDoubleWord(msg_addr + 0x00, cpu->PC);

    // Offset +0x04: Status register (32-bit)
    WriteDoubleWord(msg_addr + 0x04, cpu->STATUS.raw);

    // Offset +0x08-0x3C: Registers R0-R7, A, Q, D, L, B, TOS
    WriteDoubleWord(msg_addr + 0x08, cpu->R[0]);
    WriteDoubleWord(msg_addr + 0x0C, cpu->R[1]);
    WriteDoubleWord(msg_addr + 0x10, cpu->R[2]);
    WriteDoubleWord(msg_addr + 0x14, cpu->R[3]);
    WriteDoubleWord(msg_addr + 0x18, cpu->R[4]);
    WriteDoubleWord(msg_addr + 0x1C, cpu->R[5]);
    WriteDoubleWord(msg_addr + 0x20, cpu->R[6]);
    WriteDoubleWord(msg_addr + 0x24, cpu->R[7]);
    WriteDoubleWord(msg_addr + 0x28, cpu->A);
    WriteDoubleWord(msg_addr + 0x2C, cpu->Q);
    WriteDoubleWord(msg_addr + 0x30, cpu->D);
    WriteDoubleWord(msg_addr + 0x34, cpu->L);
    WriteDoubleWord(msg_addr + 0x38, cpu->B);
    WriteDoubleWord(msg_addr + 0x3C, cpu->TOS);

    // 4. WRITE HEADER INFORMATION
    // Offset +0x40: Process number (16-bit)
    WriteWord(msg_addr + 0x40, process_num);

    // 5. READ MICFU CODE (already written by ND-500 library before CALLG)
    // Offset +0x42: MICFU code (16-bit)
    uint16_t micfu = ReadWord(msg_addr + 0x42);

    printf("[ND500-TRAP] MICFU = 0x%04X\n", micfu);

    // 6. VALIDATE MICFU
    if (micfu == 0 || micfu == 0xFFFF)
    {
        printf("[ND500-TRAP] ERROR: Invalid MICFU code!\n");
        // Could set error and resume immediately, or trap to debugger
        return;
    }

    // 7. SET ITMQUEUE FLAG (bit 0 of flags word)
    // Offset +0x46: Flags (16-bit)
    uint16_t flags = ReadWord(msg_addr + 0x46);
    flags |= 0x0001;  // Set ITMQUEUE bit
    WriteWord(msg_addr + 0x46, flags);

    printf("[ND500-TRAP] ITMQUEUE flag set\n");

    // 8. SIGNAL ND-100 VIA TAG REGISTER
    // TAG-OUT is at a hardware-mapped location in 5015 interface
    // Writing to it triggers interrupt on ND-100 side
    Interface5015_WriteTag(TAG_MON_CALL_REQUEST, process_num);

    printf("[ND500-TRAP] TAG-OUT written: 0x0001 (MON_CALL_REQUEST)\n");
    printf("[ND500-TRAP] ND-100 interrupt level 12 triggered\n");

    // 9. BLOCK THIS PROCESS
    cpu->IsWaiting = true;
    cpu->WaitingForProcess = process_num;

    printf("[ND500-TRAP] Process %d now WAITING for ND-100\n", process_num);

    // 10. INCREMENT STATISTICS
    uint32_t call_count = ReadDoubleWord(msg_addr + 0x80);
    WriteDoubleWord(msg_addr + 0x80, call_count + 1);

    uint32_t timestamp = GetSystemTimestamp();
    WriteDoubleWord(msg_addr + 0x88, timestamp);

    // CPU execution loop will now skip this process until IsWaiting = false
}

Trap Handler State Save Structure

graph TB
    subgraph "CPU State at Trap"
        PC[PC = 0x05001218<br/>Points to CALLG instruction]
        R0[R0 = 0x00000001<br/>Device parameter]
        R1[R1 = 0x80001000<br/>Buffer address]
        STATUS[STATUS = 0x00000004<br/>Flags, modes]
    end

    subgraph "Message Buffer After Save"
        direction TB
        SAVE_PC[+0x00: PC<br/>0x05001218]
        SAVE_STATUS[+0x04: STATUS<br/>0x00000004]
        SAVE_R0[+0x08: R0<br/>0x00000001]
        SAVE_R1[+0x0C: R1<br/>0x80001000]
        DOTS[...]
        FLAGS[+0x46: Flags<br/>0x0001 (ITMQUEUE)]
    end

    PC --> SAVE_PC
    STATUS --> SAVE_STATUS
    R0 --> SAVE_R0
    R1 --> SAVE_R1

ND-100 Interrupt Handler - Complete Flow

Interrupt Level 12 Handler Entry

flowchart TD
    START([Interrupt Level 12 Triggered]) --> CHECK_PID[Check PID Register<br/>Bit 12 set?]

    CHECK_PID -->|No| SPURIOUS[Spurious Interrupt<br/>Clear and return]
    CHECK_PID -->|Yes| SAVE_CONTEXT[Save ND-100 Context<br/>Registers A, D, T, X, etc.]

    SAVE_CONTEXT --> READ_TAG[Read TAG-IN via IOX<br/>Device 100₈, Offset 1]

    READ_TAG --> DECODE_TAG{TAG<br/>Value?}

    DECODE_TAG -->|0x0001| MON_CALL[Monitor Call Request]
    DECODE_TAG -->|0x0003| PAGE_FAULT[Page Fault Request]
    DECODE_TAG -->|Other| UNKNOWN[Unknown TAG<br/>Log error]

    MON_CALL --> READ_STATUS[Read Status Register<br/>Get process number]

    READ_STATUS --> CALC_MSG[Calculate Message Buffer<br/>ND-100 sees 5MPM at 0x00040000<br/>MSG = 0x00040400 + (PROC × 0x100)]

    CALC_MSG --> READ_MICFU[Read MICFU from Message Buffer<br/>Offset +0x42]

    READ_MICFU --> DISPATCH{Dispatch<br/>MICFU}

    DISPATCH -->|0x0001| DVIO_OUT[Handle DVIO OUT]
    DISPATCH -->|0x0010| RFILE[Handle RFILE]
    DISPATCH -->|0x0030| PG_FAULT[Handle Page Fault]
    DISPATCH -->|Other| GENERIC[Generic Handler]

    DVIO_OUT --> WRITE_RESULT[Write Result to<br/>Message Buffer +0x44]
    RFILE --> WRITE_RESULT
    PG_FAULT --> WRITE_RESULT
    GENERIC --> WRITE_RESULT

    WRITE_RESULT --> CLEAR_FLAG[Clear ITMQUEUE Flag<br/>+0x46 &= ~0x0001]

    CLEAR_FLAG --> SIGNAL_COMPLETE[Write TAG-OUT = 0x0002<br/>(OPERATION_COMPLETE)]

    SIGNAL_COMPLETE --> WAKE_500[Trigger ND-500 Resume<br/>via interface]

    WAKE_500 --> RESTORE_CONTEXT[Restore ND-100 Context]

    RESTORE_CONTEXT --> CLEAR_INT[Clear PID bit 12<br/>Acknowledge interrupt]

    CLEAR_INT --> RETURN([Return from Interrupt])

Complete Interrupt Handler Code

void ND100_InterruptLevel12_Handler(void)
{
    // Called when ND-100 CPU detects interrupt on level 12

    // 1. SAVE ND-100 CPU CONTEXT
    // (This is typically done automatically by ND-100 hardware
    //  when entering interrupt level, but may need to save additional regs)
    uint16_t saved_a = CPU100.A;
    uint16_t saved_d = CPU100.D;
    uint16_t saved_t = CPU100.T;

    printf("[ND100-INT12] Interrupt Level 12 triggered\n");

    // 2. READ TAG-IN REGISTER
    uint16_t tag = IOX_Read(IOX_3022_DEVICE, IOX_TAG_IN);

    printf("[ND100-INT12] TAG-IN = 0x%04X\n", tag);

    // 3. DECODE TAG VALUE
    switch (tag)
    {
        case TAG_MON_CALL_REQUEST:  // 0x0001
            Handle_Monitor_Call();
            break;

        case TAG_PAGE_FAULT_REQUEST: // 0x0003
            Handle_Page_Fault();
            break;

        case TAG_INTERRUPT_REQUEST: // 0x0006
            Handle_Async_Interrupt();
            break;

        default:
            printf("[ND100-INT12] ERROR: Unknown TAG 0x%04X\n", tag);
            // Write error TAG back
            IOX_Write(IOX_3022_DEVICE, IOX_TAG_OUT, TAG_ERROR_OCCURRED);
            break;
    }

    // 4. RESTORE ND-100 CONTEXT
    CPU100.A = saved_a;
    CPU100.D = saved_d;
    CPU100.T = saved_t;

    // 5. CLEAR INTERRUPT
    // Write to control register to acknowledge
    uint16_t control = IOX_Read(IOX_3022_DEVICE, IOX_CONTROL);
    control |= (1 << 12);  // Set CLRINT bit
    IOX_Write(IOX_3022_DEVICE, IOX_CONTROL, control);

    printf("[ND100-INT12] Interrupt handled, returning\n");

    // Return from interrupt (hardware restores PC, switches back to level 1)
}

void Handle_Monitor_Call(void)
{
    // 1. READ STATUS TO GET PROCESS NUMBER
    uint16_t status = IOX_Read(IOX_3022_DEVICE, IOX_STATUS);
    uint8_t process_num = status & 0xFF;  // Bits 0-7

    printf("[ND100-INT12] Monitor call from process %d\n", process_num);

    // 2. CALCULATE MESSAGE BUFFER ADDRESS
    // ND-100 sees 5MPM starting at 0x00040000
    uint32_t mpm_base_nd100 = 0x00040000;
    uint32_t msg_offset = 0x400 + (process_num * 0x100);
    uint32_t msg_addr = mpm_base_nd100 + msg_offset;

    // 3. READ MICFU CODE
    uint16_t micfu = Read5MPM_Word(msg_addr + 0x42);

    printf("[ND100-INT12] MICFU = 0x%04X\n", micfu);

    // 4. DISPATCH TO HANDLER
    uint16_t error_code = 0;

    switch (micfu)
    {
        case MICFU_DVIO_OUT:  // 0x0001
            error_code = Handle_DVIO_OUT(msg_addr);
            break;

        case MICFU_DVIO_IN:   // 0x0000
            error_code = Handle_DVIO_IN(msg_addr);
            break;

        case MICFU_RFILE:     // 0x0010
            error_code = Handle_RFILE(msg_addr);
            break;

        case MICFU_WFILE:     // 0x0011
            error_code = Handle_WFILE(msg_addr);
            break;

        case MICFU_PAGE_FAULT: // 0x0030
            error_code = Handle_Page_Fault_Load(msg_addr);
            break;

        default:
            printf("[ND100-INT12] Unknown MICFU 0x%04X\n", micfu);
            error_code = 0xFFFF;  // Unknown function error
            break;
    }

    // 5. WRITE RESULT TO MESSAGE BUFFER
    Write5MPM_Word(msg_addr + 0x44, error_code);

    printf("[ND100-INT12] Result written: error = 0x%04X\n", error_code);

    // 6. CLEAR ITMQUEUE FLAG
    uint16_t flags = Read5MPM_Word(msg_addr + 0x46);
    flags &= ~0x0001;  // Clear bit 0
    Write5MPM_Word(msg_addr + 0x46, flags);

    // 7. SIGNAL COMPLETION TO ND-500
    IOX_Write(IOX_3022_DEVICE, IOX_TAG_OUT, TAG_OPERATION_COMPLETE);

    printf("[ND100-INT12] TAG-OUT written: 0x0002 (OPERATION_COMPLETE)\n");

    // 8. TRIGGER ND-500 RESUME
    // Writing TAG-OUT automatically triggers hardware signal to ND-500 side
    // ND-500 trap handler will wake up the process
}

MICFU Handler Example: DVIO_OUT

uint16_t Handle_DVIO_OUT(uint32_t msg_addr)
{
    printf("[DVIO-OUT] Processing device output request\n");

    // Read parameters from message buffer
    // Offset +0x48: Device number (16-bit)
    uint16_t device = Read5MPM_Word(msg_addr + 0x48);

    // Offset +0x4C: Buffer address in ND-500 space (32-bit)
    uint32_t buffer_addr_500 = Read5MPM_DoubleWord(msg_addr + 0x4C);

    // Offset +0x50: Byte count (32-bit)
    uint32_t byte_count = Read5MPM_DoubleWord(msg_addr + 0x50);

    printf("[DVIO-OUT] Device=%d, Buffer=0x%08X, Bytes=%d\n",
           device, buffer_addr_500, byte_count);

    // Translate ND-500 address to 5MPM address
    // ND-500 addresses in 5MPM range start at 0x80000000
    // ND-100 sees same memory starting at 0x00040000
    // Translation: ND100_addr = ND500_addr - 0x80000000 + 0x00040000

    uint32_t buffer_addr_100;
    if (buffer_addr_500 >= 0x80000000 && buffer_addr_500 < 0x80200000)
    {
        // Address is in 5MPM
        buffer_addr_100 = buffer_addr_500 - 0x80000000 + 0x00040000;
    }
    else
    {
        printf("[DVIO-OUT] ERROR: Buffer not in 5MPM! Addr=0x%08X\n",
               buffer_addr_500);
        return 0x0001;  // Error: invalid address
    }

    // Read data from 5MPM into local buffer
    uint8_t* data = malloc(byte_count);
    for (uint32_t i = 0; i < byte_count; i++)
    {
        data[i] = Read5MPM_Byte(buffer_addr_100 + i);
    }

    // Call SINTRAN III device manager
    uint16_t sintran_error = SINTRAN_DeviceWrite(device, data, byte_count);

    free(data);

    if (sintran_error == 0)
    {
        printf("[DVIO-OUT] Success: %d bytes written to device %d\n",
               byte_count, device);
        return 0x0000;  // Success
    }
    else
    {
        printf("[DVIO-OUT] Error: SINTRAN error code 0x%04X\n", sintran_error);
        return sintran_error;
    }
}

SINTRAN III MON Call Mapping

How SINTRAN III Handles Device I/O

SINTRAN III has a device manager that abstracts all I/O devices. The ND-100 interrupt handler calls SINTRAN routines.

flowchart TD
    INT12[ND-100 INT12 Handler] --> DVIO_HAND[DVIO Handler<br/>in interrupt code]

    DVIO_HAND --> SINTRAN[Call SINTRAN III<br/>Device Manager]

    SINTRAN --> LOOKUP[Lookup Device Number<br/>in Device Table]

    LOOKUP --> GET_DRIVER[Get Device Driver<br/>Address]

    GET_DRIVER --> CALL_DRIVER[Call Driver Routine<br/>e.g., TERMINAL_WRITE]

    CALL_DRIVER --> DRIVER_CODE[Driver Code Executes]

    subgraph "Driver Code (Example: Terminal)"
        DRIVER_CODE --> CHECK_READY[Check Device Ready]
        CHECK_READY --> SEND_BYTES[Send Bytes via IOX]
        SEND_BYTES --> WAIT_DONE[Wait for Completion]
        WAIT_DONE --> RETURN_STATUS[Return Status Code]
    end

    RETURN_STATUS --> SINTRAN_RET[SINTRAN Returns<br/>to DVIO Handler]

    SINTRAN_RET --> WRITE_ERROR[Write Error Code<br/>to Message Buffer]

    WRITE_ERROR --> SIGNAL[Signal ND-500<br/>via TAG]

SINTRAN Device Table Structure

typedef struct {
    uint16_t device_number;      // Logical device number (1-255)
    uint16_t device_type;        // Type: terminal, disk, tape, etc.
    uint16_t driver_address;     // Address of driver routine
    uint16_t status;             // Current status
    uint16_t reserved_flags;     // Flags, busy bit, etc.
    uint32_t driver_data;        // Driver-specific data
} DeviceTableEntry;

// SINTRAN maintains array of these
DeviceTableEntry SINTRAN_DeviceTable[256];

// Initialization (during SINTRAN boot)
void SINTRAN_InitializeDevices(void)
{
    // Device 1: Console terminal
    SINTRAN_DeviceTable[1].device_number = 1;
    SINTRAN_DeviceTable[1].device_type = TYPE_TERMINAL;
    SINTRAN_DeviceTable[1].driver_address = ADDRESS_OF(TerminalDriver);
    SINTRAN_DeviceTable[1].status = STATUS_READY;

    // Device 10: System disk
    SINTRAN_DeviceTable[10].device_number = 10;
    SINTRAN_DeviceTable[10].device_type = TYPE_DISK;
    SINTRAN_DeviceTable[10].driver_address = ADDRESS_OF(DiskDriver);
    SINTRAN_DeviceTable[10].status = STATUS_READY;

    // ... initialize other devices ...
}

SINTRAN Device Write Routine

uint16_t SINTRAN_DeviceWrite(uint16_t device_num, uint8_t* data, uint32_t byte_count)
{
    // 1. VALIDATE DEVICE NUMBER
    if (device_num == 0 || device_num > 255)
    {
        return ERROR_INVALID_DEVICE;
    }

    // 2. LOOKUP DEVICE IN TABLE
    DeviceTableEntry* device = &SINTRAN_DeviceTable[device_num];

    if (device->driver_address == 0)
    {
        return ERROR_DEVICE_NOT_PRESENT;
    }

    // 3. CHECK DEVICE READY
    if (device->status & STATUS_BUSY)
    {
        return ERROR_DEVICE_BUSY;
    }

    // 4. MARK DEVICE BUSY
    device->status |= STATUS_BUSY;

    // 5. CALL DRIVER
    // Driver routine has signature: uint16_t (*driver)(uint8_t*, uint32_t)
    typedef uint16_t (*DriverFunc)(uint8_t*, uint32_t);
    DriverFunc driver_func = (DriverFunc)(device->driver_address);

    uint16_t error = driver_func(data, byte_count);

    // 6. MARK DEVICE READY
    device->status &= ~STATUS_BUSY;

    return error;
}

Mapping MICFU to MON Calls

Some MICFU codes map directly to existing SINTRAN MON calls:

MICFU SINTRAN MON Call Notes
0x0001 (DVIO_OUT) MON 5 (OUTBT) Parameters restructured
0x0010 (RFILE) MON 14 (RFILE) Direct mapping
0x0011 (WFILE) MON 15 (WFILE) Direct mapping
0x0012 (OPEN) MON 16 (OPEN) Direct mapping
0x0030 (PAGE_FAULT) Special swapper No MON equivalent

Implementation approach:

uint16_t Handle_RFILE(uint32_t msg_addr)
{
    // Read parameters
    uint32_t file_num = Read5MPM_DoubleWord(msg_addr + 0x48);
    uint32_t block_num = Read5MPM_DoubleWord(msg_addr + 0x4C);
    uint32_t buffer_addr = Read5MPM_DoubleWord(msg_addr + 0x50);
    uint32_t word_count = Read5MPM_DoubleWord(msg_addr + 0x54);

    // Translate to ND-100 address
    uint32_t buffer_nd100 = Translate_500_to_100(buffer_addr);

    // Call SINTRAN's internal RFILE routine
    // (This is the SAME routine that MON 14 calls)
    uint16_t error = SINTRAN_Internal_RFILE(
        (uint16_t)file_num,
        (uint16_t)block_num,
        (uint16_t)buffer_nd100,
        (uint16_t)word_count
    );

    return error;
}

Signal Back Mechanism - Resume Flow

ND-500 Resume Process

sequenceDiagram
    participant HAND100 as ND-100 Handler
    participant TAG as TAG Registers
    participant IF5015 as 5015 Interface
    participant TRAP500 as ND-500 Trap Handler
    participant CPU500 as ND-500 CPU
    participant PROC as ND-500 Process

    Note over HAND100: Operation complete<br/>Result written to buffer

    HAND100->>TAG: Write TAG-OUT = 0x0002<br/>(OPERATION_COMPLETE)

    Note over TAG: Hardware signal propagates

    TAG->>IF5015: TAG change detected

    IF5015->>TRAP500: Trigger resume handler
    activate TRAP500

    TRAP500->>TAG: Read TAG-IN value
    TAG-->>TRAP500: Returns 0x0002

    TRAP500->>TRAP500: Locate waiting process<br/>from process number

    Note over TRAP500: Message buffer address<br/>0x80000400 + (PROC × 0x100)

    TRAP500->>TRAP500: Read message buffer<br/>+0x44: Error code<br/>+0x46: Flags (check ITMQUEUE=0)

    TRAP500->>CPU500: Restore saved state:<br/>PC, STATUS, R0-R7, etc.

    Note over TRAP500: Critical: Restore PC + 4<br/>to skip past CALLG instruction

    TRAP500->>CPU500: PC = saved_PC + 4
    TRAP500->>CPU500: R0 = error_code<br/>(Result of operation)
    TRAP500->>CPU500: IsWaiting = false

    deactivate TRAP500

    Note over CPU500: Process now READY to run

    CPU500->>PROC: Resume execution

    Note over PROC: Next instruction after CALLG<br/>R0 contains result

Complete Resume Handler Code

void ResumeAfterMonitorCall(uint8_t process_num)
{
    printf("[ND500-RESUME] Resuming process %d\n", process_num);

    // 1. VERIFY TAG VALUE
    uint16_t tag = Interface5015_ReadTag();
    if (tag != TAG_OPERATION_COMPLETE)
    {
        printf("[ND500-RESUME] WARNING: Unexpected TAG 0x%04X\n", tag);
        if (tag == TAG_ERROR_OCCURRED)
        {
            printf("[ND500-RESUME] ND-100 reported error\n");
            // Proceed anyway to restore state
        }
    }

    // 2. CALCULATE MESSAGE BUFFER
    uint32_t msg_addr = 0x80000400 + (process_num * 0x100);

    // 3. READ RESULT
    uint16_t error_code = ReadWord(msg_addr + 0x44);

    printf("[ND500-RESUME] Error code: 0x%04X\n", error_code);

    // 4. VERIFY ITMQUEUE FLAG CLEARED
    uint16_t flags = ReadWord(msg_addr + 0x46);
    if (flags & 0x0001)
    {
        printf("[ND500-RESUME] WARNING: ITMQUEUE still set!\n");
        // Force clear it
        flags &= ~0x0001;
        WriteWord(msg_addr + 0x46, flags);
    }

    // 5. RESTORE CPU STATE
    ND500CPU* cpu = &CPU500;  // Get CPU instance

    cpu->PC = ReadDoubleWord(msg_addr + 0x00);
    cpu->STATUS.raw = ReadDoubleWord(msg_addr + 0x04);
    cpu->R[0] = ReadDoubleWord(msg_addr + 0x08);
    cpu->R[1] = ReadDoubleWord(msg_addr + 0x0C);
    cpu->R[2] = ReadDoubleWord(msg_addr + 0x10);
    cpu->R[3] = ReadDoubleWord(msg_addr + 0x14);
    cpu->R[4] = ReadDoubleWord(msg_addr + 0x18);
    cpu->R[5] = ReadDoubleWord(msg_addr + 0x1C);
    cpu->R[6] = ReadDoubleWord(msg_addr + 0x20);
    cpu->R[7] = ReadDoubleWord(msg_addr + 0x24);
    cpu->A = ReadDoubleWord(msg_addr + 0x28);
    cpu->Q = ReadDoubleWord(msg_addr + 0x2C);
    cpu->D = ReadDoubleWord(msg_addr + 0x30);
    cpu->L = ReadDoubleWord(msg_addr + 0x34);
    cpu->B = ReadDoubleWord(msg_addr + 0x38);
    cpu->TOS = ReadDoubleWord(msg_addr + 0x3C);

    printf("[ND500-RESUME] State restored from PC=0x%08X\n", cpu->PC);

    // 6. CRITICAL: ADVANCE PC PAST CALLG INSTRUCTION
    // CALLG is 4 bytes (32-bit instruction)
    cpu->PC += 4;

    printf("[ND500-RESUME] PC advanced to 0x%08X (skip CALLG)\n", cpu->PC);

    // 7. PUT ERROR CODE IN R0
    // This is the return value from the monitor call
    cpu->R[0] = (uint32_t)error_code;

    // 8. CLEAR WAITING FLAG
    cpu->IsWaiting = false;
    cpu->WaitingForProcess = 0xFF;  // Invalid process number

    printf("[ND500-RESUME] Process %d now READY\n", process_num);

    // 9. UPDATE STATISTICS
    uint32_t call_count = ReadDoubleWord(msg_addr + 0x80);
    uint32_t last_time = ReadDoubleWord(msg_addr + 0x88);
    uint32_t now = GetSystemTimestamp();
    uint32_t wait_time = now - last_time;

    uint32_t total_wait = ReadDoubleWord(msg_addr + 0x84);
    WriteDoubleWord(msg_addr + 0x84, total_wait + wait_time);

    uint32_t max_wait = ReadDoubleWord(msg_addr + 0x8C);
    if (wait_time > max_wait)
    {
        WriteDoubleWord(msg_addr + 0x8C, wait_time);
    }

    printf("[ND500-RESUME] Wait time: %d ms (total: %d ms, max: %d ms)\n",
           wait_time, total_wait + wait_time,
           (wait_time > max_wait) ? wait_time : max_wait);

    // 10. CLEAR TAG REGISTERS (return to IDLE)
    Interface5015_WriteTag(TAG_IDLE, 0);

    // CPU scheduler will now resume this process in next cycle
}

Why PC Must Be Advanced

CRITICAL DETAIL:

The saved PC points to the CALLG instruction itself. If we restored PC without advancing it, the CPU would execute CALLG again, creating an infinite loop!

; ND-500 code at PC = 0x05001218
0x05001218: CALLG   #0x1F000000    ; ← Saved PC points here
0x0500121C: RET                     ; ← Must resume HERE (PC + 4)

; When trap occurs:
; - Trap handler saves PC = 0x05001218
; - Trap handler blocks process
; - ND-100 processes request
; - Resume handler restores state
; - Resume handler sets PC = 0x05001218 + 4 = 0x0500121C
; - Execution continues at RET instruction

Instruction size: - CALLG with 32-bit operand = 6 bytes in some encodings - BUT in this case, it's typically a 4-byte instruction in ND-500 - Always check actual instruction encoding!


Complete Example with Every Step

Full DVIO Terminal Write Example

Let's trace a complete monitor call from start to finish with EVERY detail:

User Program (FORTRAN):

      PROGRAM TEST
      CHARACTER*5 MSG
      INTEGER IERR

      MSG = 'HELLO'
      CALL DVIO(1, MSG, 5, IERR)

      IF (IERR .EQ. 0) THEN
          WRITE(*,*) 'Success!'
      END IF
      END

Compiled ND-500 Assembly (DVIO library):

; User code calls DVIO
; Parameters: R0=device(1), A/Q=buffer address, R2=count(5)
; ...user setup code...
CALLG   DVIO_LIBRARY        ; Call DVIO library routine

DVIO_LIBRARY:
    ; Get process number (from TOS or system register)
    LDWS    W1, PROCESS_NUM_REG
    ; W1 = 2 (assume process 2)

    ; Calculate message buffer address
    ; 0x80000000 (5MPM base) + 0x400 (msg offset) + (2 × 0x100)
    LDWSA   W2, 0x80000600  ; Message buffer for process 2

    ; Write MICFU code
    LDWS    R3, #0x0001     ; MICFU = DVIO_OUT
    STWS    [W2+0x42], R3   ; Offset +0x42 = MICFU field

    ; Write device number
    STWS    [W2+0x48], R0   ; Offset +0x48 = device (param1)

    ; Write buffer address (A/Q registers)
    STAQ    [W2+0x4C]       ; Offset +0x4C = buffer addr (param2)

    ; Write byte count
    LDWS    R3, R2
    STWS    [W2+0x50], R3   ; Offset +0x50 = byte count (param3)

    ; Set ITMQUEUE flag
    LDWS    R3, #0x0001
    STWS    [W2+0x46], R3   ; Offset +0x46 = flags, ITMQUEUE=1

    ; **CRITICAL: Trigger monitor call via segment 31**
    CALLG   #0x1F000000     ; ← THIS TRIGGERS THE TRAP!

    ; When this returns, R0 = error code
    RET

Step-by-Step Execution

sequenceDiagram
    autonumber

    participant USER as User Program
    participant LIB as DVIO Library
    participant CPU500 as ND-500 CPU
    participant TRAP as Trap Handler
    participant MPM as 5MPM Memory
    participant TAG as TAG Registers
    participant INT as ND-100 INT12
    participant SINT as SINTRAN III
    participant DEV as Terminal Device

    USER->>LIB: Call DVIO(1, "HELLO", 5)
    activate LIB

    Note over LIB: R0=1, A/Q=buffer, R2=5

    LIB->>MPM: Write MICFU=0x0001 to [0x80000642]
    LIB->>MPM: Write device=1 to [0x80000648]
    LIB->>MPM: Write buffer addr to [0x8000064C]
    LIB->>MPM: Write count=5 to [0x80000650]
    LIB->>MPM: Write ITMQUEUE=1 to [0x80000646]

    LIB->>CPU500: Execute CALLG #0x1F000000

    CPU500->>CPU500: Decode: segment = 0x1F (31)
    CPU500->>CPU500: Read ProgramCap[31] = 0xC000
    CPU500->>CPU500: Check bit 14 (O bit) = 1

    Note over CPU500: O BIT SET - TRIGGER TRAP!

    CPU500->>TRAP: Call HandleOtherCPUTrap()
    activate TRAP

    TRAP->>MPM: Save PC=0x05001218 to [0x80000600]
    TRAP->>MPM: Save STATUS to [0x80000604]
    TRAP->>MPM: Save R0=1 to [0x80000608]
    TRAP->>MPM: Save R1 to [0x8000060C]
    TRAP->>MPM: Save R2=5 to [0x80000610]
    TRAP->>MPM: (save all other registers...)

    TRAP->>TAG: Write TAG-OUT = 0x0001

    TRAP->>CPU500: IsWaiting = true

    Note over CPU500: PROCESS 2 SUSPENDED

    deactivate TRAP

    TAG-->>INT: Hardware interrupt signal
    INT->>INT: Set PID bit 12

    Note over INT: ND-100 enters level 12

    activate INT

    INT->>TAG: Read TAG-IN via IOX(100₈, 1)
    TAG-->>INT: Returns 0x0001

    INT->>TAG: Read STATUS via IOX(100₈, 0)
    TAG-->>INT: Returns 0x0002 (process 2 in bits 0-7)

    Note over INT: Message buffer for process 2<br/>ND-100 addr: 0x00040600

    INT->>MPM: Read MICFU from [0x00040642]
    MPM-->>INT: Returns 0x0001 (DVIO_OUT)

    INT->>MPM: Read device from [0x00040648]
    MPM-->>INT: Returns 0x0001

    INT->>MPM: Read buffer addr from [0x0004064C]
    MPM-->>INT: Returns 0x80001000

    Note over INT: Translate address:<br/>0x80001000 → 0x00041000

    INT->>MPM: Read count from [0x00040650]
    MPM-->>INT: Returns 0x00000005

    INT->>MPM: Read data bytes from [0x00041000]
    MPM-->>INT: Returns: 'H','E','L','L','O'

    INT->>SINT: Call SINTRAN_DeviceWrite(1, data, 5)
    activate SINT

    SINT->>SINT: Lookup device 1 in table
    SINT->>SINT: Get driver address
    SINT->>SINT: Call TerminalDriver

    SINT->>DEV: Write 'HELLO' to terminal
    activate DEV
    DEV-->>SINT: Success
    deactivate DEV

    SINT-->>INT: Return error=0
    deactivate SINT

    INT->>MPM: Write error=0 to [0x00040644]
    INT->>MPM: Clear ITMQUEUE: flags=0 to [0x00040646]

    INT->>TAG: Write TAG-OUT = 0x0002 via IOX(100₈, 2)

    deactivate INT

    TAG-->>TRAP: Signal resume
    activate TRAP

    TRAP->>TAG: Read TAG-IN
    TAG-->>TRAP: Returns 0x0002

    TRAP->>MPM: Read error from [0x80000644]
    MPM-->>TRAP: Returns 0x0000

    TRAP->>MPM: Read PC from [0x80000600]
    MPM-->>TRAP: Returns 0x05001218

    TRAP->>CPU500: Restore all registers
    TRAP->>CPU500: PC = 0x05001218 + 4 = 0x0500121C
    TRAP->>CPU500: R0 = 0 (error code)
    TRAP->>CPU500: IsWaiting = false

    deactivate TRAP

    Note over CPU500: PROCESS 2 READY

    CPU500->>LIB: Resume at RET instruction
    deactivate LIB

    LIB->>USER: Return (R0=0)

    Note over USER: IERR = 0<br/>Success!

    Note over DEV: Terminal displays:<br/>HELLO

Memory State at Each Phase

Phase 1: Library Writes Parameters

5MPM Address (ND-500 view: 0x80000600, ND-100 view: 0x00040600)

Offset  | Field          | Value        | Description
--------|----------------|--------------|---------------------------
+0x00   | saved_pc       | (unwritten)  | Will be filled by trap
+0x42   | micfu          | 0x0001       | ← DVIO_OUT code
+0x44   | error_code     | (unwritten)  | Will be filled by ND-100
+0x46   | flags          | 0x0001       | ← ITMQUEUE bit set
+0x48   | device         | 0x0001       | ← Terminal device
+0x4C   | buffer_addr    | 0x80001000   | ← Buffer in 5MPM
+0x50   | byte_count     | 0x00000005   | ← 5 bytes

Phase 2: After Trap Handler Saves State

Offset  | Field          | Value        | Description
--------|----------------|--------------|---------------------------
+0x00   | saved_pc       | 0x05001218   | ← Saved by trap
+0x04   | saved_status   | 0x00000004   | ← Saved by trap
+0x08   | saved_r0       | 0x00000001   | ← Saved by trap
+0x10   | saved_r2       | 0x00000005   | ← Saved by trap
+0x42   | micfu          | 0x0001       | (unchanged)
+0x44   | error_code     | (unwritten)  |
+0x46   | flags          | 0x0001       | (unchanged)
+0x48   | device         | 0x0001       | (unchanged)
+0x4C   | buffer_addr    | 0x80001000   | (unchanged)
+0x50   | byte_count     | 0x00000005   | (unchanged)

Phase 3: After ND-100 Processes

Offset  | Field          | Value        | Description
--------|----------------|--------------|---------------------------
+0x00   | saved_pc       | 0x05001218   | (unchanged)
+0x04   | saved_status   | 0x00000004   | (unchanged)
+0x08   | saved_r0       | 0x00000001   | (unchanged)
+0x42   | micfu          | 0x0001       | (unchanged)
+0x44   | error_code     | 0x0000       | ← Written by ND-100 (success!)
+0x46   | flags          | 0x0000       | ← ITMQUEUE cleared by ND-100
+0x48   | device         | 0x0001       | (unchanged)
+0x4C   | buffer_addr    | 0x80001000   | (unchanged)
+0x50   | byte_count     | 0x00000005   | (unchanged)

Memory Access Mechanisms

Who Can Access 5MPM and How

graph TB
    subgraph "5MPM Access Methods"
        direction TB

        ACCESS[5MPM Memory<br/>Dual-ported RAM]

        subgraph "ND-500 Access"
            CPU500[ND-500 CPU<br/>Direct memory access<br/>via address 0x80000000]
            DMA500[5015 DMA<br/>Burst transfers<br/>Hardware controller]
        end

        subgraph "ND-100 Access"
            IOX100[IOX Programmed I/O<br/>MAR + DATA registers<br/>One word at a time]
            DMA100[3022 DMA<br/>Burst transfers<br/>Hardware controller]
        end

        CPU500 -->|Memory instructions| ACCESS
        DMA500 -->|DMA cycle steal| ACCESS
        IOX100 -->|Via interface| ACCESS
        DMA100 -->|DMA cycle steal| ACCESS
    end

Access Method Comparison

Method Speed CPU Overhead Use Case
ND-500 CPU direct Fastest (1 cycle) None (normal memory) Normal ND-500 program access
ND-500 DMA Very fast Low (setup only) Large transfers, page loading
ND-100 IOX Slow (~10 cycles/word) High (CPU in loop) Small transfers, TAG, parameters
ND-100 DMA Fast Low (setup only) Page swapping, large file buffers

ND-100 IOX Access Pattern

Reading a word from 5MPM:

uint16_t Read5MPM_Word(uint32_t mpm_address)
{
    // Split address into high and low
    uint16_t addr_low = mpm_address & 0xFFFF;
    uint16_t addr_high = (mpm_address >> 16) & 0xFF;

    // Write address to MAR
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_LOW, addr_low);
    IOX_Write(IOX_3022_DEVICE, IOX_MAR_HIGH, addr_high);

    // Read data
    uint16_t data = IOX_Read(IOX_3022_DEVICE, IOX_DATA);

    return data;
}

ND-100 assembly equivalent:

; Read word from 5MPM address in D register
READ_5MPM:
        LDA     100         ; Device 100₈ (3022)
        STA     T           ; T = device number

        ; Write MAR_LOW
        LDA     D           ; Low 16 bits of address
        STA     X           ; Save in X
        LDA     4           ; Offset 4 (MAR_LOW)
        IOXT                ; IOX write

        ; Write MAR_HIGH
        LDA     D+1         ; High 16 bits of address
        STA     X
        LDA     5           ; Offset 5 (MAR_HIGH)
        IOXT                ; IOX write

        ; Read DATA
        LDA     6           ; Offset 6 (DATA)
        IOXT                ; IOX read, result in A

        RET                 ; A = word from 5MPM

Process State Structures

ND-500 Process Descriptor

Each ND-500 process has a 512-byte descriptor in 5MPM at offset processNum × 512:

typedef struct {
    // === SEGMENT CAPABILITIES (128 bytes) ===
    // Offset 0x000-0x07F
    uint16_t program_capabilities[32];  // +0x000: Each segment's prog cap
    uint16_t data_capabilities[32];     // +0x040: Each segment's data cap
    uint16_t reserved_caps[32];         // +0x080: Reserved

    // === SEGMENT BASE ADDRESSES (128 bytes) ===
    // Offset 0x080-0x0FF
    uint32_t segment_base[32];          // +0x080: Physical base for each seg

    // === PROCESS CONTROL (64 bytes) ===
    // Offset 0x100-0x13F
    uint8_t  process_number;            // +0x100: Process number (0-15)
    uint8_t  process_state;             // +0x101: State (READY/WAITING/etc)
    uint16_t process_priority;          // +0x102: Execution priority
    uint32_t entry_point;               // +0x104: Initial PC value
    uint32_t stack_pointer;             // +0x108: Initial stack pointer
    uint32_t heap_pointer;              // +0x10C: TOS register initial value

    // Process state flags
    uint16_t state_flags;               // +0x110: Flags (bit field)
    // Bit 0: ACTIVE (1=process exists)
    // Bit 1: WAITING (1=blocked)
    // Bit 2: SUSPENDED (1=manually suspended)
    // Bit 3: INTERRUPT_ENABLED (1=can be interrupted)

    uint32_t cpu_time_used;             // +0x112: CPU time in milliseconds
    uint32_t wait_time;                 // +0x116: Time spent waiting
    uint32_t page_faults;               // +0x11A: Number of page faults

    char domain_name[16];               // +0x11E: Name of loaded domain

    // === PAGE TABLE (256 bytes) ===
    // Offset 0x140-0x1FF
    uint32_t page_table[64];            // +0x140: Virtual→Physical mapping
    // Each entry: bits 31-12 = physical page, bits 11-0 = flags

} ProcessDescriptor;  // Total: 512 bytes (0x200)

Process State Values

enum ProcessState {
    STATE_FREE       = 0,  // Process slot not in use
    STATE_READY      = 1,  // Ready to run
    STATE_RUNNING    = 2,  // Currently executing
    STATE_WAITING    = 3,  // Waiting for monitor call
    STATE_SUSPENDED  = 4,  // Manually suspended
    STATE_PAGE_WAIT  = 5,  // Waiting for page load
    STATE_TERMINATED = 6   // Terminated, cleanup pending
};

Process Scheduling

stateDiagram-v2
    [*] --> FREE: System boot

    FREE --> READY: PLACE-DOMAIN command

    READY --> RUNNING: Scheduler selects<br/>highest priority

    RUNNING --> READY: Time slice expired<br/>or higher priority ready

    RUNNING --> WAITING: Monitor call triggered<br/>(CALLG segment 31)

    RUNNING --> PAGE_WAIT: Page fault occurred

    WAITING --> READY: ND-100 completes<br/>monitor call

    PAGE_WAIT --> READY: Page loaded<br/>from disk

    RUNNING --> SUSPENDED: Manual suspend<br/>(operator command)

    SUSPENDED --> READY: Resume command

    RUNNING --> TERMINATED: Program exit

    TERMINATED --> FREE: Cleanup complete

    note right of WAITING
        IsWaiting = true
        Process blocked
        Not scheduled for execution
    end note

    note right of PAGE_WAIT
        Page fault handler active
        ND-100 loads page from disk
        Then signals completion
    end note

This completes the exhaustive deep-dive into the Segment 31 mechanism! Every bit, field, struct, DMA operation, TAG signal, and process flow has been documented with detailed Mermaid diagrams and code examples.

Would you like me to expand on any particular aspect even further, or create additional diagrams for specific scenarios?