MPM5 Memory Detection and Identification¶
How SINTRAN Identifies Which Memory is 5MPM for ND-500 Shared Memory Communication
Version: 1.0
Date: 2025-01-XX
Status: Complete
Source: Analysis of SINTRAN III source code (PH-P2-OPPSTART.NPL)
Table of Contents¶
- Overview
- Detection Process
- MEMARRAY Structure
- MPM5 Identification Algorithm
- ADRZERO Calculation
- Concrete Identification Method
- Emulator Implementation Guide
1. Overview¶
1.1 The Problem¶
When SINTRAN boots with an ND-500 coprocessor, it must identify which physical memory pages are MPM5 (5MPM) - the shared multiport memory used for ND-100/ND-500 communication.
Key Questions: - How does SINTRAN know which memory is MPM5? - How is ADRZERO (5MPM base address) determined? - What is the concrete algorithm for identifying MPM5 pages?
1.2 The Answer¶
SINTRAN uses a two-stage process:
- Initial Assignment: All detected memory is initially marked as MPM5 (
KMPM5 = 4₈) - Refinement: Other memory types (MPM3, MPM4, Local, PIOC) are detected and overwrite MPM5 markings
- Final Identification: Remaining pages still marked as MPM5 are the actual 5MPM memory
ADRZERO is set to: The first page found that is marked as MPM3, MPM4, or MPM5 in MEMARRAY.
2. Detection Process¶
2.1 Boot Sequence¶
% From PH-P2-OPPSTART.NPL boot sequence
% Step 1: Physical memory scan (builds TMMAP bitmap)
CURRPAGE:=1000₈
DO WHILE CURRPAGE << ENDPAGE
CALL PHYSPTEST % Test if page exists
IF page_exists THEN
SETBIT TMMAP(page_number)
FI
CURRPAGE+100=:CURRPAGE
OD
% Step 2: Initial MPM5 assignment (lines 2396-2406)
RETU: FOR X:=0 TO 17 DO % ALL FOUND MEMORY IS INITIALLY SET TO MPM5 MEMORY
IF TMMAP(X)><0 THEN
X=:CSAVX; A=:XA:=X SH 12=:CURRPAGE
FOR X:=-20 DO
IF XA BIT "0" THEN % MEMORY BANK EXISTS
T:=KMPM5; A:=CURRPAGE; CALL SMEMTYPE % Mark as MPM5
FI; XA SHZ -1=:XA
CURRPAGE+100=:CURRPAGE
OD; X:=CSAVX
FI
OD
% Step 3: Controller-level detection (refines memory types)
% - BUS EXPANDER test (IOX 100000)
% - MPM3 controller test (IOX 750)
% - ECCR test (IOX 100115) → marks as Local (KMECCR)
% - BUSC scan (IOX 100200+) → marks as MPM4 (KMPM4)
% - PIOC configuration → marks as PIOC (KMPIOC)
% Step 4: Page-level mapping
% - MPM3MAP: Tests pages with IOX 751 → marks as MPM3 (KMPM3)
% - MPM4MAP: Tests pages with ECCR → marks as Local (KMECCR)
% Step 5: MPM5 refinement (lines 2510-2519)
FMPM5: X:=MEMARRAY; A:=X+200=:D
DO WHILE X<<D
T:=MBMEMARRAY; *LDATX
IF A SHZ -10=KMPM5 GO SMPM5 % Check upper byte
T:=MBMEMARRAY; *LDATX
IF A/\377=KMPM5 GO SMPM5 % Check lower byte
X+1
OD; GO MEMFINE
SMPM5: MEMTYPE BONE BMPM5=:MEMTYPE
GO MEMFINE
% Step 6: Find first MPM page for ADRZERO (lines 2492-2498)
FN5MEM:
A:=0; CALL FPMPMPAGE; A:=-1; A=:FPIMPM % Find first MPM page
IF X:=PN500D><0 AND X.ADRZERO=-1 THEN
CURRPAGE; CALL FPMPMPAGE; A=:CURRPAGE
IF PN500D.ADRZERO=-1 THEN CURRPAGE=:X.ADRZERO FI % Set ADRZERO
FI
2.2 Memory Type Detection Order¶
| Step | Process | Memory Types Identified | Code Reference |
|---|---|---|---|
| 1 | Initial Assignment | All memory → MPM5 | Lines 2396-2406 |
| 2 | BUS EXPANDER Test | MPM4 indicator | Line 2410 |
| 3 | MPM3 Controller Test | MPM3 controller present | Line 2413 |
| 4 | ECCR Test | Local/OnCpu memory | Line 2415 |
| 5 | BUSC Scan | MPM4 memory ranges | Lines 2418-2433 |
| 6 | MPM3MAP | MPM3 pages | Lines 3857-3860 |
| 7 | MPM4MAP | Local pages | Lines 3851-3855 |
| 8 | PIOC Configuration | PIOC memory | Lines 2450-2461 |
| 9 | MPM5 Refinement | Remaining MPM5 pages | Lines 2510-2519 |
Key Insight: MPM5 is the default memory type. Other types overwrite it during detection.
3. MEMARRAY Structure¶
3.1 Purpose¶
MEMARRAY stores the memory type code for each physical page in the ND-100's 24-bit address space.
3.2 Format¶
Structure: Array of 16-bit words
Indexing: One entry per 128 pages (100₈ pages)
Encoding: Packed format - two pages per word
Bit Layout:
MEMARRAY Entry (16-bit word):
┌─────────────────────────────────────┐
│ Bits 15-8: Memory type code │
│ Bits 7-0: Memory type code │
└─────────────────────────────────────┘
Index Calculation:
MEMARRAY_index = (page_number >> 7) + MEMARRAY
Byte Selection (from FPMPMPAGE line 2483):
- Bit 6 of page number = 1: Extract lower byte (A/\377) - bits 7-0
- Bit 6 of page number = 0: Extract upper byte (A SHZ -10) - bits 15-8
Storage (from SMEMTYPE lines 3884-3887):
- Bit 6 of page number = 1: Store in upper byte (A/\177400\/TR) - bits 15-8
- Bit 6 of page number = 0: Store in lower byte (A/\377; T:=TR SH 10; A\/T) - bits 7-0
Note: The encoding is determined by bit 6 of the page number: - Pages 64-127 (bit 6=1): Stored in upper byte, read from lower byte - Pages 0-63, 128-191, etc. (bit 6=0): Stored in lower byte, read from upper byte
3.3 Memory Type Codes¶
| Symbol | Value (Octal) | Value (Dec/Hex) | Memory Type |
|---|---|---|---|
KMPM3 |
000001₈ | 1 / 0x01 | MPM3 memory |
KMPM4 |
000002₈ | 2 / 0x02 | MPM4 memory |
KMPM5 |
000004₈ | 4 / 0x04 | MPM5 memory |
KMECCR |
000010₈ | 8 / 0x08 | Local/OnCpu memory |
KMPIOC |
000020₈ | 16 / 0x10 | PIOC memory |
3.4 SMEMTYPE Routine¶
Purpose: Store memory type code for a page in MEMARRAY
Parameters: - A: Physical page number - T: Memory type code (KMPM3, KMPM4, KMPM5, KMECCR, KMPIOC)
Implementation:
% From PH-P2-OPPSTART.NPL, lines 3880-3891
SUBR SMEMTYPE
SMEMTYPE: TAD=:TRARDR; X=:XR
A=:D SHZ -7+MEMARRAY=:X; T:=MBMEMARRAY; *LDATX % Load MEMARRAY entry
IF D BIT 6 THEN
A/\177400\/TR % Update upper byte (bits 15-8)
ELSE
A/\377; T:=TR SH 10; A\/T; T:=MBMEMARRAY % Update lower byte (bits 7-0)
FI; *STATX % Store updated entry
X:=XR; TAD:=TRARDR
EXIT
Process (from lines 3883-3887):
1. Calculate MEMARRAY index: (page >> 7) + MEMARRAY
2. Load current MEMARRAY entry
3. Check bit 6 of page number:
- Bit 6 = 1: Update upper byte (A/\177400\/TR) - bits 15-8
- Bit 6 = 0: Update lower byte (A/\377; T:=TR SH 10; A\/T) - bits 7-0
4. Store updated entry back to MEMARRAY
4. MPM5 Identification Algorithm¶
4.1 FPMPMPAGE Routine¶
Purpose: Find the first page in MPM memory (MPM3, MPM4, or MPM5)
Entry Points: - FPMPMPAGE: Find first page IN MPM memory (K=0) - LPMPMPAGE: Find first page NOT IN MPM memory (K=1)
Implementation:
% From PH-P2-OPPSTART.NPL, lines 2477-2489
FPMPMPAGE: K:="0"; GO FMPMFELLS % FIND FIRST PAGE IN MPM MEMORY
LPMPMPAGE: K:=1 % FIND FIRST PAGE NOT IN MPM MEMORY
FMPMFELLS:
A=:D % D = starting page number
DO WHILE D<<ENDPAGE
A:=D SHZ -7+MEMARRAY=:X; T:=MBMEMARRAY; *LDATX % Load MEMARRAY entry
IF D BIT 6 THEN A/\377 ELSE A SHZ -10 FI % Extract memory type code
IF A=KMPM3 OR A=KMPM4 OR A=KMPM5 THEN
IF K NBIT THEN A:=D; EXITA FI % FIRST PAGE IN MPM PART
ELSE
IF K THEN A:=D; EXITA FI % FIRST PAGE IN NOT-MPM PART
FI; A:=100; D+A % Next page (increment by 128)
OD; A:=D; EXIT
Algorithm:
1. Start at page number D (passed in A register)
2. Calculate MEMARRAY index: (D >> 7) + MEMARRAY
3. Load MEMARRAY entry
4. Extract memory type code (from line 2483):
- If bit 6 of D = 1: Extract lower byte (A/\377) - bits 7-0
- If bit 6 of D = 0: Extract upper byte (A SHZ -10) - bits 15-8
5. Check if memory type is MPM (KMPM3, KMPM4, or KMPM5):
- If K=0 (FPMPMPAGE): Return first page that IS MPM
- If K=1 (LPMPMPAGE): Return first page that is NOT MPM
6. Increment D by 128 (100₈) and continue
Note: The increment by 128 is because each MEMARRAY entry covers 128 pages. The routine scans in 128-page chunks.
4.2 MPM5 Refinement (FMPM5)¶
Purpose: Confirm that MPM5 memory exists (set MEMTYPE |= BMPM5)
Implementation:
% From PH-P2-OPPSTART.NPL, lines 2510-2519
FMPM5: X:=MEMARRAY; A:=X+200=:D % Scan first 200₈ (128 decimal) entries
DO WHILE X<<D
T:=MBMEMARRAY; *LDATX % Load MEMARRAY entry
IF A SHZ -10=KMPM5 GO SMPM5 % Check upper byte (even pages)
T:=MBMEMARRAY; *LDATX % Reload entry
IF A/\377=KMPM5 GO SMPM5 % Check lower byte (odd pages)
X+1 % Next entry
OD; GO MEMFINE
SMPM5: MEMTYPE BONE BMPM5=:MEMTYPE % Set MPM5 flag
GO MEMFINE
Algorithm:
1. Scan MEMARRAY entries from 0 to 200₈ (128 decimal entries = 16,384 pages)
2. For each entry:
- Check upper byte (A >> 10) for KMPM5 (4₈)
- Check lower byte (A & 377₈) for KMPM5 (4₈)
3. If KMPM5 found, set MEMTYPE |= BMPM5
4. Continue to MEMFINE
Purpose: This confirms that MPM5 memory exists in the system (used for ND-500 communication).
5. ADRZERO Calculation¶
5.1 ADRZERO Purpose¶
ADRZERO is the page number of the first MPM page (MPM3, MPM4, or MPM5) used for ND-500 communication.
Important: ADRZERO is a PAGE NUMBER, not a byte address!
5.2 ADRZERO Setting¶
Two Mechanisms:
Mechanism 1: MEMDEF from ND-500
% From 5P-P2-MON60.NPL:587 (CHMEMDEF routine)
5D12=:ADRZERO % ND-500 sends its view of shared memory
Mechanism 2: Startup Scan (if not set)
% From PH-P2-OPPSTART.NPL, lines 2492-2498
FN5MEM:
A:=0; CALL FPMPMPAGE; A:=-1; A=:FPIMPM % Find first MPM page
IF X:=PN500D><0 AND X.ADRZERO=-1 THEN
CURRPAGE; CALL FPMPMPAGE; A=:CURRPAGE % First MPM page in memory part
IF PN500D.ADRZERO=-1 THEN CURRPAGE=:X.ADRZERO FI % Set ADRZERO
FI
Process:
1. Call FPMPMPAGE starting at page 0
2. Find first page marked as MPM3, MPM4, or MPM5
3. If ADRZERO = -1 (not configured), set ADRZERO to this page number
ADRZERO = -1 means: "5MPM not yet configured"
5.3 ADRZERO to Address Conversion¶
ND-100 Context (word addressing):
Physical Word Address = ADRZERO × 1024
ND-500 Context (byte addressing):
Physical Byte Address = ADRZERO × 4096
Example:
If ADRZERO = 2048 (page number):
ND-100: 2048 × 1024 = 2,097,152 words = word address 20000000₈
ND-500: 2048 × 4096 = 8,388,608 bytes = 0x800000
NPL Code:
% From MP-P2-N500.NPL:170
A:="N500DF".ADRZERO=:D:=0; AD SH 12 % Load ADRZERO, shift left 12
% Shift left 12 = multiply by 4096 = convert page number to ND-500 byte address
5.4 5MBBANK Calculation¶
5MBBANK is the bank number containing 5MPM.
Calculation:
5MBBANK = ADRZERO / 256 % 256 pages per bank
Usage: Used for bank register selection when accessing 5MPM.
6. Concrete Identification Method¶
6.1 Step-by-Step Algorithm¶
For Emulator Implementation:
def identify_mpm5_memory(memarray, endpage):
"""
Identify which memory pages are MPM5 (5MPM) for ND-500 communication.
Args:
memarray: Array of 16-bit words storing memory type codes
endpage: Maximum page number to scan
Returns:
List of page numbers that are MPM5
"""
KMPM5 = 4 # MPM5 memory type code
mpm5_pages = []
# Scan MEMARRAY entries
for memarray_index in range(len(memarray)):
entry = memarray[memarray_index]
# Extract upper and lower bytes
upper_byte = (entry >> 8) & 0xFF # Bits 15-8
lower_byte = entry & 0xFF # Bits 7-0
# Calculate page range for this entry
base_page = memarray_index * 128 # 128 pages per entry
# Check each page in this entry
for page_offset in range(128):
page_number = base_page + page_offset
if page_number >= endpage:
break
# Determine which byte to use based on bit 6 (from FPMPMPAGE line 2483)
if (page_number >> 6) & 1 == 1:
# Bit 6 = 1: read from lower byte
memory_type = lower_byte
else:
# Bit 6 = 0: read from upper byte
memory_type = upper_byte
# Check if this page is MPM5
if memory_type == KMPM5:
mpm5_pages.append(page_number)
return mpm5_pages
def find_adrzero(memarray, start_page=0, endpage=16384):
"""
Find ADRZERO - the first MPM page (MPM3, MPM4, or MPM5).
Args:
memarray: Array of 16-bit words storing memory type codes
start_page: Starting page number to scan
endpage: Maximum page number to scan
Returns:
Page number of first MPM page, or None if not found
"""
KMPM3 = 1
KMPM4 = 2
KMPM5 = 4
# Scan pages in 128-page chunks (MEMARRAY entry size)
page = start_page
while page < endpage:
memarray_index = page >> 7 # Divide by 128
page_offset = page & 0x7F # Page within entry (0-127)
if memarray_index >= len(memarray):
break
entry = memarray[memarray_index]
# Extract memory type code
if page_offset % 2 == 0:
# Even page: use upper byte
memory_type = (entry >> 8) & 0xFF
else:
# Odd page: use lower byte
memory_type = entry & 0xFF
# Check if this is MPM memory
if memory_type in (KMPM3, KMPM4, KMPM5):
return page
# Increment by 128 (next MEMARRAY entry)
page = ((page >> 7) + 1) << 7
return None
6.2 Complete Detection Flow¶
For Emulator:
class MPM5Detector:
"""Detects and identifies MPM5 memory for ND-500 communication."""
# Memory type codes
KMPM3 = 1 # MPM3 memory
KMPM4 = 2 # MPM4 memory
KMPM5 = 4 # MPM5 memory (5MPM)
KMECCR = 8 # Local/OnCpu memory
KMPIOC = 16 # PIOC memory
def __init__(self, memarray, endpage=16384):
"""
Initialize detector.
Args:
memarray: Array of 16-bit words (MEMARRAY structure)
endpage: Maximum page number to scan
"""
self.memarray = memarray
self.endpage = endpage
def get_memory_type(self, page_number):
"""
Get memory type code for a specific page.
Args:
page_number: Physical page number (0-16383)
Returns:
Memory type code (KMPM3, KMPM4, KMPM5, KMECCR, KMPIOC)
"""
if page_number >= self.endpage:
return None
memarray_index = page_number >> 7 # Divide by 128
page_offset = page_number & 0x7F # Page within entry
if memarray_index >= len(self.memarray):
return None
entry = self.memarray[memarray_index]
# Extract memory type code (from FPMPMPAGE line 2483)
if (page_number >> 6) & 1 == 1:
# Bit 6 = 1: read from lower byte (bits 7-0)
memory_type = entry & 0xFF
else:
# Bit 6 = 0: read from upper byte (bits 15-8)
memory_type = (entry >> 8) & 0xFF
return memory_type
def find_first_mpm_page(self, start_page=0):
"""
Find first page marked as MPM3, MPM4, or MPM5.
This is used to determine ADRZERO.
Args:
start_page: Starting page number to scan
Returns:
Page number of first MPM page, or None if not found
"""
# Scan in 128-page chunks (MEMARRAY entry size)
page = start_page
while page < self.endpage:
memory_type = self.get_memory_type(page)
if memory_type in (self.KMPM3, self.KMPM4, self.KMPM5):
return page
# Increment to next MEMARRAY entry boundary
page = ((page >> 7) + 1) << 7
return None
def get_all_mpm5_pages(self):
"""
Get all pages marked as MPM5 (5MPM).
Returns:
List of page numbers that are MPM5
"""
mpm5_pages = []
for page in range(self.endpage):
memory_type = self.get_memory_type(page)
if memory_type == self.KMPM5:
mpm5_pages.append(page)
return mpm5_pages
def calculate_adrzero(self):
"""
Calculate ADRZERO (first MPM page for ND-500).
Returns:
ADRZERO page number, or None if no MPM memory found
"""
return self.find_first_mpm_page(start_page=0)
def calculate_5mbbank(self, adrzero):
"""
Calculate 5MBBANK (bank number containing 5MPM).
Args:
adrzero: ADRZERO page number
Returns:
Bank number (0-63)
"""
if adrzero is None:
return None
return adrzero >> 8 # Divide by 256 (256 pages per bank)
6.3 Usage Example¶
# Example: Identify MPM5 memory
# Assume MEMARRAY has been populated during boot
memarray = [0x0404, 0x0404, 0x0404, ...] # Example: pages 0-127 all MPM5
endpage = 16384 # 16MW scan limit
detector = MPM5Detector(memarray, endpage)
# Find ADRZERO (first MPM page)
adrzero = detector.calculate_adrzero()
print(f"ADRZERO = page {adrzero}")
# Calculate 5MBBANK
mbbank = detector.calculate_5mbbank(adrzero)
print(f"5MBBANK = {mbbank}")
# Get all MPM5 pages
mpm5_pages = detector.get_all_mpm5_pages()
print(f"MPM5 pages: {mpm5_pages}")
# Convert ADRZERO to addresses
if adrzero is not None:
nd100_word_addr = adrzero * 1024
nd500_byte_addr = adrzero * 4096
print(f"ND-100 word address: {nd100_word_addr:o}₈ ({nd100_word_addr} words)")
print(f"ND-500 byte address: 0x{nd500_byte_addr:X} ({nd500_byte_addr} bytes)")
7. Emulator Implementation Guide¶
7.1 Key Points for Emulation¶
-
MEMARRAY Structure:
- One 16-bit word per 128 pages
- Upper byte: Even pages (0, 2, 4, ...)
- Lower byte: Odd pages (1, 3, 5, ...)
-
Memory Type Codes:
KMPM5 = 4₈(4 decimal, 0x04 hex)- Pages still marked as KMPM5 after refinement are 5MPM
-
ADRZERO:
- Page number (not byte address)
- First page marked as MPM3, MPM4, or MPM5
- Can be set via MEMDEF from ND-500 or found via FPMPMPAGE
-
5MBBANK:
- Bank number = ADRZERO / 256
- Used for bank register selection
7.2 Detection Sequence¶
During SINTRAN Boot:
- Physical Memory Scan: Build TMMAP bitmap
- Initial Assignment: Mark all memory as MPM5 (
KMPM5 = 4₈) - Controller Detection: Detect MPM3, MPM4, Local, PIOC
- Page-Level Mapping: Refine memory types
- MPM5 Refinement: Confirm remaining MPM5 pages
- ADRZERO Calculation: Find first MPM page
For Emulator:
- Initialize MEMARRAY: Mark all detected memory as MPM5
- Detect Controllers: Update MEMARRAY based on hardware detection
- Identify MPM5: Pages still marked as KMPM5 are 5MPM
- Set ADRZERO: First MPM page found
7.3 Validation¶
To verify MPM5 identification:
- Check MEMARRAY: Pages marked as
KMPM5 = 4₈are 5MPM - Check ADRZERO: Should point to first MPM page
- Check 5MBBANK: Should be ADRZERO / 256
- Check ND-500 Interface: 3022 interface should be configured with ADRZERO
8. Summary¶
8.1 How SINTRAN Identifies MPM5 Memory¶
- All memory initially marked as MPM5 (
KMPM5 = 4₈) - Other memory types overwrite MPM5 during detection
- Remaining MPM5 pages are 5MPM for ND-500 communication
- ADRZERO = first MPM page (MPM3, MPM4, or MPM5)
8.2 Concrete Identification¶
For a given page number:
memarray_index = page_number >> 7
page_offset = page_number & 0x7F
entry = memarray[memarray_index]
if page_offset % 2 == 0:
memory_type = (entry >> 8) & 0xFF # Upper byte
else:
memory_type = entry & 0xFF # Lower byte
is_mpm5 = (memory_type == 4) # KMPM5 = 4₈
To find ADRZERO:
adrzero = find_first_mpm_page(memarray, start_page=0)
To get all MPM5 pages:
mpm5_pages = [p for p in range(endpage)
if get_memory_type(memarray, p) == 4]
End of Document