1 /* hp2100_mem.c: HP 21xx/1000 Main Memory/Memory Expansion Module/Memory Protect simulator
2 
3    Copyright (c) 1993-2016, Robert M. Supnik
4    Copyright (c) 2017-2019, J. David Bryan
5 
6    Permission is hereby granted, free of charge, to any person obtaining a copy
7    of this software and associated documentation files (the "Software"), to deal
8    in the Software without restriction, including without limitation the rights
9    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10    copies of the Software, and to permit persons to whom the Software is
11    furnished to do so, subject to the following conditions:
12 
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15 
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19    AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 
23    Except as contained in this notice, the names of the authors shall not be
24    used in advertising or otherwise to promote the sale, use or other dealings
25    in this Software without prior written authorization from the authors.
26 
27    Main memory  12615A/12839A/12885A Core Memory Subsystems
28                 2102B MOS Memory Subsystem
29    MEM          12731A Memory Expansion Module
30    MP           12581A/12892B Memory Protect
31 
32    27-Mar-19    JDB     "dm_violation" now correctly freezes the violation register
33    02-Aug-18    JDB     Added MEM device
34    30-Jul-18    JDB     Renamed "iop_sp" to "SPR" (stack pointer register)
35    20-Jul-18    JDB     Split out from hp2100_cpu.c
36 
37    References:
38      - HP 1000 M/E/F-Series Computers Technical Reference Handbook
39          (5955-0282, March 1980)
40      - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
41          (92851-90001, March 1981)
42      - HP 1000 M/E/F-Series Computers I/O Interfacing Guide
43          (02109-90006, September 1980)
44      - 12892B Memory Protect Installation Manual
45          (12892-90007, June 1978)
46      - 2100A Computer Reference Manual
47          (02100-90001, December 1971)
48      - Central Processor Options Computer Maintenance Course Student's Manual
49        Volumes V, VI, VII, VIII, & IX
50          (5950-8707, April 1969)
51 
52    This module simulates the HP 12615A and 12839A core memory subsystems for the
53    2116 and 2115/2114, respectively, the 12885A core memory subsystem for the
54    2100, and the 2102B MOS memory subsystem for the 1000 M/E/F-Series CPUs.
55    Main memory is implemented as a dynamically allocated array, "M", of
56    MEMORY_WORD words.  The MEMORY_WORD type is a 16-bit unsigned type,
57    corresponding with the 16-bit main memory word of the HP 21xx/1000.  The
58    largest supported memory size (one megaword for the HP 1000) is allocated
59    when the simulator is started, while the configured memory size for the
60    current CPU is kept in the "mem_size" variable.  Installed memory sizes may
61    range from 4K words to 1M words.
62 
63    HP 21xx and 1000 CPUs address a maximum of 32K words with 15-bit addresses.
64    This is the logical address space.  1000-series machines may employ an
65    optional Memory Expansion Module to map the logical address space anywhere
66    with a 1M-word physical memory on a 1K-per-page basis.  For all machines,
67    reads to addresses outside of installed memory return all-zeros worda, and
68    writes outside of memory are ignored.  Neither operation causes an error.
69 
70    The core memory machines (2114, 2115, 2116, and 2100) have a protected area
71    of memory where a binary loader program may be stored.  The protected loader
72    area resides in the last 64 words of installed memory and is normally
73    protected against reading and writing; as with non-existent memory, reads
74    returns all zeros words, and writes are ignored.  The loader is unprotected
75    by a switch on the CPU front panel so that it may be executed, typically to
76    bootstrap a system from paper tape, magnetic tape, or disc.  The loader is
77    automatically protected when the machine executes a HLT instruction.  It may
78    also be protected manually through the front panel.  In simulation, loader
79    protection is controlled by the "mem_end" variable.  When it is equal to
80    "mem_size", the loader is unprotected and available for execution.  When it
81    is less than "mem_size", the loader is protected, and memory logically ends
82    at the "mem_end" address.
83 
84    This module provides routines to read and write memory words and bytes.  All
85    memory accesses are classified as to the type of the access, which determines
86    the mapping mode and protection applied.  Utility routines to initialize,
87    zero, and copy loaders to and from memory are also supplied.
88 
89 
90 
91    This module also simulates the 12731A Memory Expansion Module for the 1000
92    M/E/F-Series machines.  The MEM provides mapping of the 32 1K-word logical
93    memory pages into a one-megaword physical memory.  Four separate 32-page maps
94    are provided: system, user, DCPC port A (used by channel 1), and DCPC port B
95    (used by channel 2).
96 
97    The MEM is controlled by the associated Dynamic Mapping System instructions.
98    While enabled, all programmed memory accesses are translated via the system
99    or user map, depending on which is currently enabled, and all DCPC accesses
100    are translated through one of the two port maps, depending on which channel
101    is making the access.
102 
103    In addition, page 0 (the base page) accesses have an additional translation
104    step.  A base page fence separates a mapped portion from an unmapped potion
105    in the system and user maps.  The mapped portion is mapped to the physical
106    page that resides in the first map register.  The unmapped portion is not
107    mapped and accesses physical page 0.  A MEM setting controls whether the
108    mapped portion is above or below the fence.
109 
110    Each map page may be protected against reading or writing.  Write protection
111    also extends to executing jump instructions that target the page.  Attempting
112    a protected access results in a MEM violation, which is handled by the Memory
113    Protect card.  If MP is enabled, a MEM violation causes an interrupt on
114    select code 05; if MP is disabled, no violation occurs, and the read or write
115    proceeds normally.  MP and MEM violations are distinguished by executing an
116    SFS 05 instruction, which skips for MEM violations but not for MP violations.
117    Read and write protections are ignored for DCPC accesses.
118 
119    In addition, MEM violations also occur for attempts to write into the
120    unmapped portion of the base page (i.e., to physical page 0), as well as
121    attempts to execute privileged DMS instructions (i.e., those that load any of
122    the map registers).  The MEM status and violation registers reflect the
123    current status of the MEM and the last violation, if any.  They are formatted
124    as follows.
125 
126    MEM Status Register:
127 
128       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
129      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
130      | I | M | E | U | P | B |        base page fence address        |
131      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
132 
133    Where:
134 
135      I = MEM was disabled/enabled (0/1) at last interrupt
136      M = System/user map (0/1) was selected at last interrupt
137      E = MEM is disabled/enabled (0/1) currently
138      U = System/user map (0/1) is selected currently
139      P = Protected mode is disabled/enabled (0/1) currently
140      B = Base-page portion mapped is above/below (0/1) the fence
141 
142 
143    MEM Violation Register:
144 
145       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
146      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
147      | R | W | B | P | -   -   -   - | S | E | M |   page address    |
148      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
149 
150    Where:
151 
152      R = Read violation
153      W = Write violation
154      B = Base-page violation
155      P = Privileged instruction violation
156      S = ME bus disabled/enabled (0/1) at violation
157      E = MEM disabled/enabled (0/1) at violation
158      M = System/user map (0/1) selected at violation
159 
160 
161    The MEM card has four hardware configuration jumpers:
162 
163      W1 - configure for 1000 M-Series (A)
164           or 1000 E/F-Series (B)
165 
166      W2 - normal operation (IN)
167           or factory test (OUT)
168 
169      W3 - factory test (IN)
170           or normal operation (OUT)
171 
172      W4 (RME) - MEM remains in the system map after IAK for IOG trap instruction (A)
173                 or returns to the prior map (B)
174 
175    These jumpers are not simulated.  Instead, the simulation behaves as though
176    W1 is set correctly for the current CPU type, W2 is IN, W3 is OUT, and W4 is
177    set to the A position.
178 
179 
180 
181    This module also simulates the 12581A/12892B Memory Protect accessories for
182    the 2116 and 1000 M/E/F-Series, respectively, and the memory protect feature
183    that is standard equipment for the 2100.  MP is addressed via select code 05
184    and provides a fence register that holds the address of the start of
185    unprotected memory and a violation register that holds the address of the
186    instruction that has caused a memory protect violation.
187 
188    In hardware, if the Memory Protect accessory is installed and enabled, I/O
189    operations to select codes other than 01 are prohibited.  Also, in
190    combination with the MPCK micro-order, MP validates the M-register contents
191    (memory address) against the memory protect fence.  If a violation occurs, an
192    I/O instruction or memory write is inhibited, and a memory read returns
193    invalid data.
194 
195    In simulation, MP violations are usually detected automatically when the
196    "mem_write" routine is called to write to memory or the "cpu_iog" routine is
197    called to execute an I/O instruction.  A few instruction executors detect MP
198    violations explicitly and call the "mp_violation" routine.  If MP is enabled,
199    the routine sets the MP flag and then calls "cpu_microcode_abort" to abort
200    the instruction.  That routine executes a "longjmp" to the abort handler,
201    which is outside of and precedes the instruction execution loop.
202 
203    An MP interrupt (SC 05) is qualified by "interrupt_system" but not by
204    "cpu_interrupt_enable".  If the interrupt system is off when an MP violation
205    is detected, the violating instruction will be aborted, even though no
206    interrupt occurs.  In this case, neither the flag nor the flag buffer are
207    set.
208 
209    MP is controlled by I/O instructions directed to select code 05, as follows.
210 
211    Output Data Word format (OTA and OTB):
212 
213       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
214      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
215      | 0 |          starting address of unprotected memory           | fence
216      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
217 
218 
219    Input Data Word formats (LIA, LIB, MIA, and MIB):
220 
221       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
222      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
223      | 0 |               violating instruction address               | MP violation
224      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
225      | 1 |               violating instruction address               | PE violation
226      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
227 
228    After setting the fence regiater with an OTA 05 or OTB 05 instruction, MP is
229    enabled by an STC 05.  MP cannot be disabled programmatically; it is disabled
230    only by a violation.  The SFS 05 and SFC 05 instructions test the Memory
231    Expansion Violation flip-flop, not the MP flag flip-flop.  The MEV flip-flop
232    is set for a MEM violation and is clear for an MP violation.
233 
234 
235    The 12892B card has six hardware configuration jumpers:
236 
237      W3 (HLTPE) - parity violation register clocked when an error occurs (OUT)
238                   or not clocked when error an occurs and the CPU switch is in
239                   the HLT PE position (IN)
240 
241      W4 (MX)    - timing is for an E/F-Series (OUT)
242                   or for an M-Series (IN)
243 
244      W5 (JSB)   - JSB to locations 0 and 1 are prohibited (OUT)
245                   or permitted (IN)
246 
247      W6 (INT)   - interrupts are enabled immediately if MP is enabled (OUT)
248                   or only after three levels of indirection (IN)
249 
250      W7 (SEL1)  - permit I/O only to select code 01 (OUT)
251                   or to all select codes (IN)
252 
253      W8 (RME)   - MEM remains in the system map after IAK for IOG trap instruction (OUT)
254                   or returns to the prior map (IN)
255 
256    In simulation, jumpers W5, W6, and W7 may be set via the SCP command line;
257    the default (normal) positions are W5 IN, W6 IN, and W7 OUT.  Jumpers W3, W4,
258    and W8 are not simulated.  Instead, the simulation behaves as though W3 is
259    OUT, W4 is set correctly for the current CPU type, and W8 is OUT.  The
260    jumpers designated as W1 and W2 do not exist.
261 
262 
263    Implementation notes:
264 
265     1. The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI
266        (Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used
267        somewhat interchangeably to refer to the logical-to-physical memory
268        address translation option provided on the 1000-Series.  DMS consists of
269        the MEM card (12731A) and the DMI firmware (13307A).  However, MEM and
270        MEU have been used interchangeably to refer to the mapping card, as have
271        DMI and DMS to refer to the firmware instructions.
272 
273        In this module, MEM routines and state variables are prefixed "meu_"
274        rather than "mem_" to avoid confusion with the main memory symbols.
275 */
276 
277 
278 
279 #include "hp2100_defs.h"
280 #include "hp2100_cpu.h"
281 #include "hp2100_cpu_dmm.h"
282 
283 
284 
285 /* Main memory instruction masks */
286 
287 #define IR_MRG              (MRG | AB_MASK)     /* MRG instructions mask */
288 
289 #define IR_ISZ              0034000u            /* ISZ instruction */
290 #define IR_STF              0102100u            /* STF instruction */
291 
292 
293 /* Main memory access classification table */
294 
295 typedef struct {
296     uint32      debug_flag;                     /* the debug flag for tracing */
297     const char  *name;                          /* the classification name */
298     } ACCESS_PROPERTIES;
299 
300 static const ACCESS_PROPERTIES mem_access [] = {    /* indexed by ACCESS_CLASS */
301 /*    debug_flag    name                */
302 /*    ------------  ------------------- */
303     { TRACE_FETCH,  "instruction fetch" },          /*   instruction fetch */
304     { TRACE_DATA,   "data"              },          /*   data access */
305     { TRACE_DATA,   "data"              },          /*   data access, alternate map */
306     { TRACE_DATA,   "unprotected"       },          /*   data access, system map */
307     { TRACE_DATA,   "unprotected"       },          /*   data access, user map */
308     { TRACE_DATA,   "dma"               },          /*   DMA channel 1, port A map */
309     { TRACE_DATA,   "dma"               }           /*   DMA channel 2, port B map */
310     };
311 
312 
313 /* Main memory OS base page addresses */
314 
315 static const uint32 m64  = 0000040u;            /* (DOS) constant -64 address */
316 static const uint32 p64  = 0000067u;            /* (DOS) constant +64 address */
317 
318 static const uint32 xeqt = 0001717u;            /* (RTE) XEQT address */
319 static const uint32 tbg  = 0001674u;            /* (RTE) TBG address */
320 
321 
322 /* Main memory tracing constants */
323 
324 static const char * const register_values [] = {    /* register values, indexed by EOI concatenation */
325     "e o i",                                        /*   E = 0, O = 0, interrupt_system = off */
326     "e o I",                                        /*   E = 0, O = 0, interrupt_system = on */
327     "e O i",                                        /*   E = 0, O = 1, interrupt_system = off */
328     "e O I",                                        /*   E = 0, O = 1, interrupt_system = on */
329     "E o i",                                        /*   E = 1, O = 0, interrupt_system = off */
330     "E o I",                                        /*   E = 1, O = 0, interrupt_system = on */
331     "E O i",                                        /*   E = 1, O = 1, interrupt_system = off */
332     "E O I"                                         /*   E = 1, O = 1, interrupt_system = on */
333     };
334 
335 static const char mp_value [] = {               /* memory protection value, indexed by mp_control */
336     '-',                                        /*   MP is off */
337     'P'                                         /*   MP is on */
338     };
339 
340 static const char * const register_formats [] = {       /* CPU register formats, indexed by is_1000 */
341     REGA_FORMAT "  A %06o, B %06o, ",                   /*   is_1000 = FALSE format */
342     REGA_FORMAT "  A %06o, B %06o, X %06o, Y %06o, "    /*   is_1000 = TRUE  format */
343     };
344 
345 static const char * const mp_mem_formats [] = {                 /* MP/MEM register formats, indexed by is_1000 */
346     REGB_FORMAT "  MPF %06o, MPV %06o\n",                       /*   is_1000 = FALSE format */
347     REGB_FORMAT "  MPF %06o, MPV %06o, MES %06o, MEV %06o\n"    /*   is_1000 = TRUE  format */
348     };
349 
350 
351 /* Main memory global state declarations */
352 
353 uint32 mem_size = 0;                            /* size of main memory in words */
354 uint32 mem_end  = 0;                            /* address of the first word beyond installed memory */
355 
356 
357 /* Main memory local state declarations */
358 
359 static MEMORY_WORD *M          = NULL;          /* the pointer to allocated memory */
360 static DIB         *tbg_dibptr = NULL;          /* a pointer to the time-base generator DIB (for RTE idle check) */
361 static t_bool      is_1000     = FALSE;         /* TRUE if the CPU is a 1000 M/E/F-Series */
362 
363 
364 /* Memory Expansion Unit command line switches */
365 
366 #define ALL_MAPMODES        (SWMASK ('S') | SWMASK ('U') |  \
367                              SWMASK ('P') | SWMASK ('Q'))
368 
369 
370 /* Memory Expansion Unit program limits */
371 
372 #define MAP_COUNT           4                   /* number of maps */
373 #define REG_COUNT           32                  /* number of map registers per map */
374 
375 
376 /* Memory Expansion Unit program constants */
377 
378 #define LWA_BASE_PAGE       0001777u            /* address of the last word on the base page */
379 
380 #define MAP_MASK            (MAP_COUNT - 1)     /* mask to the map selection bits */
381 
382 #define ALTERNATE_MAP(m)    ((MEU_MAP_SELECTOR) ((m) ^ 1))  /* switch to alternate map (user or system) */
383 
384 
385 /* Memory Expansion Unit state constant declarations */
386 
387 static const char map_indicator [] = {          /* MEU map indicator, indexed by MEU_MAP_SELECTOR */
388     'S',                                        /*   System_Map */
389     'U',                                        /*   User_Map   */
390     'A',                                        /*   Port_A_Map */
391     'B'                                         /*   Port_B_Map */
392     };
393 
394 
395 /* MEU Page Map Registers.
396 
397       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
398      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
399      | R | W | -   -   -   - |         physical page address         |
400      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
401 */
402 
403 #define READ_PROTECTED      0100000u            /* (R) read protection bit */
404 #define WRITE_PROTECTED     0040000u            /* (W) write protection bit */
405 #define MAP_RESERVED        0036000u            /* reserved bits */
406 #define PAGE_MASK           PP_MASK             /* physical page address mask */
407 
408 #define NO_PROTECTION       0000000u            /* no read/write protection */
409 
410 #define MAP_PAGE(r)         ((r) & PAGE_MASK)   /* extract the page number from a map register */
411 
412 
413 
414 /* MEU status register.
415 
416       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
417      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
418      | I | M | E | U | P | B |        base page fence address        |
419      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
420 */
421 
422 #define MEST_ENBL_INT       0100000u            /* (I) MEM was enabled at last interrupt */
423 #define MEST_UMAP_INT       0040000u            /* (M) User map was selected at last interrupt */
424 #define MEST_ENABLED        0020000u            /* (E) MEM is enabled currently */
425 #define MEST_USER_MAP       0010000u            /* (U) User map is selected currently (set dynamically) */
426 #define MEST_PROTECTED      0004000u            /* (P) Protected mode is enabled currently (set dynamically) */
427 #define MEST_BELOW          0002000u            /* (B) Base page below fence is mapped */
428 #define MEST_FENCE_MASK     0001777u            /* Base page fence mask */
429 
430 #define MEST_DYNAMIC        (MEST_USER_MAP | MEST_PROTECTED)
431 #define MEST_DYNAMIC_IAK    (MEST_ENBL_INT | MEST_UMAP_INT)
432 
433 
434 /* MEU Violation Register.
435 
436       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
437      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
438      | R | W | B | P | -   -   -   - | S | E | M |    page index     |
439      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
440 */
441 
442 #define MEVI_READ           0100000u            /* (R) = Read violation */
443 #define MEVI_WRITE          0040000u            /* (W) = Write violation */
444 #define MEVI_BASE_PAGE      0020000u            /* (B) = Base page violation */
445 #define MEVI_PRIVILEGE      0010000u            /* (P) = Privileged instruction violation */
446 #define MEVI_BUS_ENABLED    0000200u            /* (S) = ME bus was enabled at violation */
447 #define MEVI_MEM_ENABLED    0000100u            /* (E) = MEM was enabled at violation */
448 #define MEVI_USER_MAP       0000040u            /* (M) = User map was selected at violation */
449 #define MEVI_INDEX_MASK     0000037u            /* Page register index for which the violation occurred */
450 
451 
452 /* Memory Expansion Unit global state declarations */
453 
454 char   meu_indicator;                           /* last map access indicator (S | U | A | B | -) */
455 uint32 meu_page;                                /* last physical page number accessed */
456 
457 
458 /* Memory Expansion Unit local state declarations */
459 
460 static MEU_MAP_SELECTOR meu_current_map = System_Map;       /* the current map */
461 static t_bool           meu_bus_enabled = FALSE;            /* TRUE if the memory expansion bus is enabled */
462 static HP_WORD          meu_status      = 0;                /* the MEM status register */
463 static HP_WORD          meu_violation   = 0;                /* the MEM violation register */
464 static HP_WORD          meu_maps [MAP_COUNT] [REG_COUNT];   /* the MEM map registers */
465 
466 
467 /* Memory Expansion Unit local SCP support routine declarations */
468 
469 static t_stat meu_reset (DEVICE *dptr);
470 
471 
472 /* Memory Expansion Unit local utility routine declarations */
473 
474 static void   dm_violation (HP_WORD violation);
475 static t_bool is_mapped    (HP_WORD address);
476 static uint32 map_address  (HP_WORD address, MEU_MAP_SELECTOR map, HP_WORD protection);
477 
478 
479 /* Memory Expansion Unit SCP data declarations */
480 
481 
482 /* Unit list */
483 
484 static UNIT meu_unit [] = {
485 /*           Event Routine  Unit Flags  Capacity  Delay */
486 /*           -------------  ----------  --------  ----- */
487     { UDATA (NULL,              0,         0)           }   /* dummy unit */
488     };
489 
490 
491 /* Register list.
492 
493 
494    Implementation notes:
495 
496     1. The REG definitions for the maps must be 17 bits (not 16) to ensure that
497        the map entries are accessed as 32-bit HP_WORDs and not uint16s.
498 */
499 
500 static REG meu_reg [] = {
501 /*    Macro   Name       Location                Radix  Width   Offset     Depth           Flags       */
502 /*    ------  ---------  ----------------------  -----  -----  --------  ----------  ----------------- */
503     { FLDATA (ENABLED,   meu_status,                             13)                                   },
504     { FLDATA (CURMAP,    meu_current_map,                         0)                                   },
505     { ORDATA (STATUS,    meu_status,                     16)                                           },
506     { ORDATA (VIOL,      meu_violation,                  16)                                           },
507     { BRDATA (SMAP,      meu_maps [System_Map],     8,   17,             REG_COUNT)                    },
508     { BRDATA (UMAP,      meu_maps [User_Map],       8,   17,             REG_COUNT)                    },
509     { BRDATA (PAMAP,     meu_maps [Port_A_Map],     8,   17,             REG_COUNT)                    },
510     { BRDATA (PBMAP,     meu_maps [Port_B_Map],     8,   17,             REG_COUNT)                    },
511     { FLDATA (MEBEN,     meu_bus_enabled,                         0),                REG_HRO           },
512 
513     { NULL }
514     };
515 
516 
517 /* Device descriptor */
518 
519 DEVICE meu_dev = {
520     "MEM",                                      /* device name */
521     meu_unit,                                   /* unit array */
522     meu_reg,                                    /* register array */
523     NULL,                                       /* modifier array */
524     1,                                          /* number of units */
525     8,                                          /* address radix */
526     1,                                          /* address width */
527     1,                                          /* address increment */
528     8,                                          /* data radix */
529     16,                                         /* data width */
530     NULL,                                       /* examine routine */
531     NULL,                                       /* deposit routine */
532     &meu_reset,                                 /* reset routine */
533     NULL,                                       /* boot routine */
534     NULL,                                       /* attach routine */
535     NULL,                                       /* detach routine */
536     NULL,                                       /* device information block pointer */
537     DEV_DIS,                                    /* device flags */
538     0,                                          /* debug control flags */
539     NULL,                                       /* debug flag name table */
540     NULL,                                       /* memory size change routine */
541     NULL                                        /* logical device name */
542     };
543 
544 
545 
546 /* Memory Protect unit flags */
547 
548 #define UNIT_V_MP_JSB       (UNIT_V_UF + 0)             /* MP jumper W5 */
549 #define UNIT_V_MP_INT       (UNIT_V_UF + 1)             /* MP jumper W6 */
550 #define UNIT_V_MP_SEL1      (UNIT_V_UF + 2)             /* MP jumper W7 */
551 
552 #define UNIT_MP_JSB         (1 << UNIT_V_MP_JSB)        /* 1 = W5 is out */
553 #define UNIT_MP_INT         (1 << UNIT_V_MP_INT)        /* 1 = W6 is out */
554 #define UNIT_MP_SEL1        (1 << UNIT_V_MP_SEL1)       /* 1 = W7 is out */
555 
556 
557 /* Memory Protect violation register.
558 
559       15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
560      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
561      | P |               violating instruction address               |
562      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
563 */
564 
565 #define MPVR_PARITY_ERROR   0100000u            /* (P) parity error violation */
566 
567 
568 /* Memory Protect global state declarations */
569 
570 HP_WORD mp_fence = 0;                           /* MP fence register */
571 
572 
573 /* Memory Protect local state declarations */
574 
575 static HP_WORD   mp_VR          = 0;            /* MP violation register */
576 static FLIP_FLOP mp_control     = CLEAR;        /* MP control flip-flop */
577 static FLIP_FLOP mp_flag_buffer = CLEAR;        /* MP flag buffer flip-flop */
578 static FLIP_FLOP mp_flag        = CLEAR;        /* MP flag flip-flop */
579 static FLIP_FLOP mp_mevff       = CLEAR;        /* memory expansion violation flip-flop */
580 static FLIP_FLOP mp_evrff       = SET;          /* enable violation register flip-flop */
581 static FLIP_FLOP mp_enabled     = CLEAR;        /* MP was enabled at interrupt */
582 static FLIP_FLOP mp_reenable    = CLEAR;        /* MP will be reenabled after IAK */
583 static t_bool    mp_mem_changed = TRUE;         /* TRUE if the MP or MEM registers have been altered */
584 static uint32    jsb_bound      = 2;            /* protected lower bound for JSB */
585 
586 
587 /* Memory Protect I/O interface routine declarations */
588 
589 static INTERFACE mp_interface;
590 
591 
592 /* Memory Protect local SCP support routine declarations */
593 
594 static t_stat mp_set_jsb (UNIT *uptr, int32 value, char *cptr, void *desc);
595 static t_stat mp_reset   (DEVICE *dptr);
596 
597 
598 /* Memory Protect local utility routine declarations */
599 
600 static t_stat mp_service (UNIT *uptr);
601 
602 
603 /* Memory Protect SCP data declarations */
604 
605 /* Unit list.
606 
607 
608    Implementation notes:
609 
610     1. The default flags correspond to the following jumper settings: JSB in,
611        INT in, SEL1 out.
612 */
613 
614 static UNIT mp_unit [] = {
615 /*           Event Routine  Unit Flags    Capacity  Delay */
616 /*           -------------  ------------  --------  ----- */
617     { UDATA (&mp_service,   UNIT_MP_SEL1,    0)           }
618     };
619 
620 
621 /* Device information block */
622 
623 static DIB mp_dib = {
624     &mp_interface,                              /* the device's I/O interface function pointer */
625     MPPE,                                       /* the device's select code (02-77) */
626     0,                                          /* the card index */
627     NULL,                                       /* the card description */
628     NULL                                        /* the ROM description */
629     };
630 
631 
632 /* Register list */
633 
634 static REG mp_reg [] = {
635 /*    Macro   Name      Location        Width  Flags   */
636 /*    ------  --------  --------------  -----  ------- */
637     { FLDATA (CTL,      mp_control,       0)           },
638     { FLDATA (FLG,      mp_flag,          0)           },
639     { FLDATA (FBF,      mp_flag_buffer,   0)           },
640     { ORDATA (FR,       mp_fence,        15)           },
641     { ORDATA (VR,       mp_VR,           16)           },
642     { FLDATA (EVR,      mp_evrff,         0)           },
643     { FLDATA (MEV,      mp_mevff,         0)           },
644 
645     { FLDATA (ENABLED,  mp_enabled,       0),  REG_HRO },
646     { FLDATA (REENABLE, mp_reenable,      0),  REG_HRO },
647     { ORDATA (PLBOUND,  jsb_bound,       16),  REG_HRO },
648     { NULL }
649     };
650 
651 
652 /* Modifier list */
653 
654 static MTAB mp_mod [] = {
655 /*    Mask Value     Match Value   Print String     Match String  Validation   Display  Descriptor */
656 /*    -------------  ------------  ---------------  ------------  -----------  -------  ---------- */
657     { UNIT_MP_JSB,   UNIT_MP_JSB,  "JSB (W5) out",  "JSBOUT",     &mp_set_jsb, NULL,    NULL       },
658     { UNIT_MP_JSB,   0,            "JSB (W5) in",   "JSBIN",      &mp_set_jsb, NULL,    NULL       },
659 
660     { UNIT_MP_INT,   UNIT_MP_INT,  "INT (W6) out",  "INTOUT",     NULL,        NULL,    NULL       },
661     { UNIT_MP_INT,   0,            "INT (W6) in",   "INTIN",      NULL,        NULL,    NULL       },
662 
663     { UNIT_MP_SEL1,  UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT",    NULL,        NULL,    NULL       },
664     { UNIT_MP_SEL1,  0,            "SEL1 (W7) in",  "SEL1IN",     NULL,        NULL,    NULL       },
665 
666     { 0 }
667     };
668 
669 
670 /* Trace list */
671 
672 static DEBTAB mp_deb [] = {
673     { "IOBUS", TRACE_IOBUS },                   /* trace I/O bus signals and data words received and returned */
674     { NULL,    0           }
675     };
676 
677 
678 /* Device descriptor */
679 
680 DEVICE mp_dev = {
681     "MP",                                       /* device name */
682     mp_unit,                                    /* unit array */
683     mp_reg,                                     /* register array */
684     mp_mod,                                     /* modifier array */
685     1,                                          /* number of units */
686     8,                                          /* address radix */
687     1,                                          /* address width */
688     1,                                          /* address increment */
689     8,                                          /* data radix */
690     16,                                         /* data width */
691     NULL,                                       /* examine routine */
692     NULL,                                       /* deposit routine */
693     &mp_reset,                                  /* reset routine */
694     NULL,                                       /* boot routine */
695     NULL,                                       /* attach routine */
696     NULL,                                       /* detach routine */
697     &mp_dib,                                    /* device information block pointer */
698     DEV_DISABLE | DEV_DIS | DEV_DEBUG,          /* device flags */
699     0,                                          /* debug control flags */
700     mp_deb,                                     /* debug flag name table */
701     NULL,                                       /* memory size change routine */
702     NULL                                        /* logical device name */
703     };
704 
705 
706 
707 /* Main memory global utility routines */
708 
709 
710 /* Initialize main memory.
711 
712    This routine allocates and zeros the array of MEMORY_WORDs that represent the
713    main memory of the CPU.  It also obtains and saves a pointer to the DIB of
714    the Time Base Generator device for RTE idle detection.
715 
716    On entry, the "memory_size" parameter will be the number of words to allocate
717    and will represent the largest possible memory supported by the most
718    expansive CPU model.  If memory allocation failed, or the TBG device was not
719    found, the routine returns an appropriate error.
720 
721 
722    Implementation notes:
723 
724     1. This routine is called only once during simulator startup.  If it returns
725        an error status, then the simulator exits.
726 
727     2. The TBG device is initially named "CLK" (for backward compatibility).
728        The logical name "TBG" is assigned by the TBG device reset routine, but
729        we are called before that routine is executed, so the logical name does
730        not exist when we are called.
731 */
732 
mem_initialize(uint32 memory_size)733 t_stat mem_initialize (uint32 memory_size)
734 {
735 DEVICE *tbg_dptr;
736 
737 M = (MEMORY_WORD *) calloc (memory_size,                /* allocate the maximum amount of memory needed */
738                             sizeof (MEMORY_WORD));
739 
740 tbg_dptr = find_dev ("CLK");                            /* get a pointer to the time-base generator device */
741 
742 if (tbg_dptr == NULL)                                   /* if the TBG device is not present */
743     return SCPE_IERR;                                   /*   then something is seriously wrong */
744 else                                                    /* otherwise */
745     tbg_dibptr = (DIB *) tbg_dptr->ctxt;                /*   set a pointer to the device's DIB */
746 
747 if (M == NULL)                                          /* if memory allocation failed */
748     return SCPE_MEM;                                    /*   then report the error */
749 else                                                    /* otherwise */
750     return SCPE_OK;                                     /*   report successful allocation */
751 }
752 
753 
754 /* Read a word from memory.
755 
756    This routine reads and returns a word from memory at the indicated logical
757    address.  On entry, "dptr" points to the DEVICE structure of the device
758    requesting access, "classification" is the type of access requested, and
759    "address" is the offset into the 32K logical address space implied by the
760    classification.
761 
762    If memory expansion is enabled, the logical address is mapped into a physical
763    memory location; the map used is determined by the access classification.
764    The current map (user or system), alternate map (the map not currently
765    selected), or an explicit map (system, user, DCPC port A, or DCPC port B) may
766    be requested.  Read protection is enabled for current or alternate map access
767    and disabled for the others.  If memory expansion is disabled or not present,
768    the logical address directly accesses the first 32K of memory.
769 
770    The Memory Protect (MP) and Memory Expansion Module (MEM) accessories provide
771    a protected mode that guards against improper accesses by user programs.
772    They may be enabled or disabled independently, although protection requires
773    that both be enabled.  MEM checks that read protection rules on the target
774    page are compatible with the access desired.  If the check fails, and MP is
775    enabled, then the request is aborted.
776 
777    The 1000 family maps memory location 0 to the A-register and location 1 to
778    the B-register.  CPU reads of these locations return the A- or B-register
779    values, while DCPC reads access physical memory locations 0 and 1 instead.
780 
781 
782    Implementation notes:
783 
784     1. A read beyond the limit of physical memory returns 0.  This is handled by
785        allocating the maximum memory array and initializing memory beyond the
786        defined limit to zero, so no special handling is needed here.
787 
788     2. A MEM read protection violation with MP enabled causes an MP abort
789        instead of a normal return from the "map_address" routine..
790 
791     3. In hardware, a FTCH micro-order clocks the address on the MBUS into the
792        MP Violation Register if the EVR (Enable Violation Register) flip-flop is
793        set.  An MP or MEM violation clears EVR, preserving the address of the
794        violating instruction until the Violation Register is read during abort
795        processing.
796 */
797 
mem_read(DEVICE * dptr,ACCESS_CLASS classification,HP_WORD address)798 HP_WORD mem_read (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address)
799 {
800 uint32  index;
801 MEU_MAP_SELECTOR map;
802 HP_WORD protection;
803 
804 MR = address;                                           /* save the logical memory address */
805 
806 switch (classification) {                               /* dispatch on the access classification */
807 
808     case Fetch:
809         if (mp_evrff)                                   /* if the violation register is enabled */
810             mp_VR = address;                            /*   then update it with the instruction address */
811 
812         map = meu_current_map;                          /* use the currently selected map (user or system) */
813         protection = READ_PROTECTED;                    /*   and enable read protection */
814         break;
815 
816 
817     case Data:
818     default:                                            /* needed to quiet the compiler's anxiety */
819         map = meu_current_map;                          /* use the currently selected map (user or system) */
820         protection = READ_PROTECTED;                    /*   and enable read protection */
821         break;
822 
823 
824     case Data_Alternate:
825         map = ALTERNATE_MAP (meu_current_map);          /* use the alternate map (user or system) */
826         protection = READ_PROTECTED;                    /*   and enable read protection */
827         break;
828 
829 
830     case Data_System:
831         map = System_Map;                               /* use the system map explicitly */
832         protection = NO_PROTECTION;                     /*   without protection */
833         break;
834 
835 
836     case Data_User:
837         map = User_Map;                                 /* use the user map explicitly */
838         protection = NO_PROTECTION;                     /*   without protection */
839         break;
840 
841 
842     case DMA_Channel_1:
843         map = Port_A_Map;                               /* use the DCPC port A map */
844         protection = NO_PROTECTION;                     /*   without protection */
845         break;
846 
847 
848     case DMA_Channel_2:
849         map = Port_B_Map;                               /* use the DCPC port B map */
850         protection = NO_PROTECTION;                     /*   without protection */
851         break;
852     }                                                   /* all cases are handled */
853 
854 index = map_address (address, map, protection);         /* translate the logical address to a physical address */
855 
856 if (index > 1 || map >= Port_A_Map)                     /* if memory is referenced or this is a DCPC transfer */
857     TR = (HP_WORD) M [index];                           /*   then return the physical memory value */
858 else                                                    /* otherwise */
859     TR = ABREG [index];                                 /*   return the selected register value */
860 
861 tpprintf (dptr, mem_access [classification].debug_flag,
862           DMS_FORMAT "  %s%s\n",
863           meu_indicator, meu_page, MR, TR,
864           mem_access [classification].name,
865           mem_access [classification].debug_flag == TRACE_FETCH ? "" : " read");
866 
867 return TR;                                              /* return the word that was read */
868 }
869 
870 
871 /* Write a word to memory.
872 
873    This routine writes a word to memory at the indicated logical address.  On
874    entry, "dptr" points to the DEVICE structure of the device requesting access,
875    "classification" is the type of access requested, "address" is the offset
876    into the 32K logical address space implied by the classification, and "value"
877    is the value to write.
878 
879    If memory expansion is enabled, the logical address is mapped into a physical
880    memory location; the map used is determined by the access classification.
881    The current map (user or system), alternate map (the map not currently
882    selected), or an explicit map (system, user, DCPC port A, or port B) may be
883    requested.  Write protection is enabled for current or alternate map access
884    and disabled for the others.  If memory expansion is disabled or not present,
885    the logical address directly accesses the first 32K of memory.
886 
887    The Memory Protect (MP) and Memory Expansion Module (MEM) accessories provide
888    a protected mode that guards against improper accesses by user programs.
889    They may be enabled or disabled independently, although protection requires
890    that both be enabled.  MP checks that memory writes do not fall below the
891    Memory Protect Fence Register (MPFR) value, and MEM checks that write
892    protection rules on the target page are compatible with the access desired.
893    If either check fails, and MP is enabled, then the request is aborted (so, to
894    pass, a page must be writable AND the target must be above the MP fence).  In
895    addition, a MEM write violation will occur if MP is enabled and the alternate
896    map is selected, regardless of the page protection.
897 
898    The 1000 family maps memory location 0 to the A-register and location 1 to
899    the B-register.  CPU writes to these locations store the values into the A or
900    B register, while DCPC writes access physical memory locations 0 and 1
901    instead.  MP uses a lower bound of 2 for memory writes, allowing unrestricted
902    access to the A and B registers.
903 
904 
905    Implementation notes:
906 
907     1. if memoy expansion is disabled, a write beyond the limit of physical
908        memory is a no-operation.  If expansion is enabled, it is a NOP if the
909        page is not write-protected.
910 
911     2. When the alternate map is enabled, writes are permitted only in the
912        unprotected mode, regardless of page protections or the MP fence setting.
913        This behavior is not mentioned in the MEM documentation, but it is tested by
914        the MEM diagnostic and is evident from the MEM schematic.  Referring to
915        Sheet 2 in the ERD, gates U125 and U127 provide this logic:
916 
917          WTV = MPCNDB * MAPON * (WPRO + ALTMAP)
918 
919        The ALTMAP signal is generated by the not-Q output of flip-flop U117,
920        which toggles on control signal -CL3 assertion (generated by the MESP
921        microorder) to select the alternate map.  Therefore, a write violation is
922        indicated whenever a memory protect check occurs while the MEM is enabled
923        and either the page is write-protected or the alternate map is selected.
924 
925        The hardware reference manuals that contain descriptions of those DMS
926        instructions that write to the alternate map (e.g., MBI) say, "This
927        instruction will always cause a MEM violation when executed in the
928        protected mode and no bytes [or words] will be transferred."  However,
929        they do not state that a write violation will be indicated, nor does the
930        description of the write violation state that this is a potential cause.
931 */
932 
mem_write(DEVICE * dptr,ACCESS_CLASS classification,HP_WORD address,HP_WORD value)933 void mem_write (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD address, HP_WORD value)
934 {
935 uint32  index;
936 MEU_MAP_SELECTOR map;
937 HP_WORD protection;
938 
939 MR = address;                                           /* save the logical memory address */
940 
941 switch (classification) {                               /* dispatch on the access classification */
942 
943     case Data:
944     default:                                            /* needed to quiet the compiler's anxiety */
945         map = meu_current_map;                          /* use the currently selected map (user or system) */
946         protection = WRITE_PROTECTED;                   /*   and enable write protection */
947         break;
948 
949 
950     case Data_Alternate:
951         map = ALTERNATE_MAP (meu_current_map);          /* use the alternate map (user or system) */
952         protection = WRITE_PROTECTED;                   /*   and enable write protection */
953 
954         if (meu_status & MEST_ENABLED)                  /* if the MEM is enabled */
955             dm_violation (MEVI_WRITE);                  /*   then a violation always occurs if in protected mode */
956         break;
957 
958 
959     case Data_System:
960         map = System_Map;                               /* use the system map explicitly */
961         protection = NO_PROTECTION;                     /*   without protection */
962         break;
963 
964 
965     case Data_User:
966         map = User_Map;                                 /* use the user map explicitly */
967         protection = NO_PROTECTION;                     /*   without protection */
968         break;
969 
970 
971     case DMA_Channel_1:
972         map = Port_A_Map;                               /* use the DCPC port A map */
973         protection = NO_PROTECTION;                     /*   without protection */
974         break;
975 
976 
977     case DMA_Channel_2:
978         map = Port_B_Map;                               /* use the DCPC port B map */
979         protection = NO_PROTECTION;                     /*   without protection */
980         break;
981 
982 
983     case Fetch:                                         /* instruction fetches */
984         return;                                         /*   do not cause writes */
985 
986     }                                                   /* all cases are handled */
987 
988 index = map_address (address, map, protection);         /* translate the logical address to a physical address */
989 
990 if (protection == WRITE_PROTECTED                       /* if protection is wanted */
991   && address >= 2 && address < mp_fence)                /*   and the MP check fails */
992     mp_violation ();                                    /*     then a memory protect violation occurs */
993 
994 if (index <= 1 && map <= User_Map)                      /* if the A/B register is referenced in the system or user map */
995     ABREG [index] = value;                              /*   then write the value to the selected register */
996 
997 else if (index < mem_end)                               /* otherwise if the location is within defined memory */
998     M [index] = (MEMORY_WORD) value;                    /*   then write the value to memory */
999 
1000 TR = value;                                             /* save the value just written */
1001 
1002 tpprintf (dptr, mem_access [classification].debug_flag,
1003           DMS_FORMAT "  %s write\n",
1004           meu_indicator, meu_page, MR, TR,
1005           mem_access [classification].name);
1006 
1007 return;
1008 }
1009 
1010 
1011 /* Read a byte from memory.
1012 
1013    This routine reads and returns a byte from memory at the indicated logical
1014    address.  On entry, "dptr" points to the DEVICE structure of the device
1015    requesting access, "classification" is the type of access requested, and
1016    "byte_address" is the byte offset into the 32K logical address space implied
1017    by the classification.
1018 
1019    The HP 1000 is a word-oriented machine.  To permit byte accesses, a logical
1020    byte address is defined as two times the associated word address.  The LSB of
1021    the byte address designates the byte to access: 0 for the upper byte, and 1
1022    for the lower byte.  As all 16 bits are used, byte addresses cannot be
1023    indirect.
1024 
1025 
1026    Implementation notes:
1027 
1028     1. Word buffering is not used to minimize memory reads, as the HP 1000
1029        microcode does a full word read for each byte accessed.
1030 */
1031 
mem_read_byte(DEVICE * dptr,ACCESS_CLASS classification,HP_WORD byte_address)1032 uint8 mem_read_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address)
1033 {
1034 const HP_WORD word_address = byte_address >> 1;         /* the address of the word containing the byte */
1035 HP_WORD word;
1036 
1037 word = mem_read (dptr, classification, word_address);   /* read the addressed word */
1038 
1039 if (byte_address & LSB)                                 /* if the byte address is odd */
1040     return LOWER_BYTE (word);                           /*   then return the right-hand byte */
1041 else                                                    /* otherwise */
1042     return UPPER_BYTE (word);                           /*   return the left-hand byte */
1043 }
1044 
1045 
1046 /* Write a byte to memory.
1047 
1048    This routine writes a byte to memory at the indicated logical address.  On
1049    entry, "dptr" points to the DEVICE structure of the device requesting access,
1050    "classification" is the type of access requested, "byte_address" is the byte
1051    offset into the 32K logical address space implied by the classification, and
1052    "value" is the value to write.
1053 
1054    The HP 1000 is a word-oriented machine.  To permit byte accesses, a logical
1055    byte address is defined as two times the associated word address.  The LSB of
1056    the byte address designates the byte to access: 0 for the upper byte, and 1
1057    for the lower byte.  As all 16 bits are used, byte addresses cannot be
1058    indirect.
1059 
1060 
1061    Implementation notes:
1062 
1063     1. Word buffering is not used to minimize memory writes, as the HP 1000
1064        base-set microcode does a full word write for each byte accessed.  (The
1065        DMS byte instructions, e.g., MBI, do full-word accesses for each pair of
1066        bytes, but that is to minimize the number of map switches.)
1067 */
1068 
mem_write_byte(DEVICE * dptr,ACCESS_CLASS classification,HP_WORD byte_address,uint8 value)1069 void mem_write_byte (DEVICE *dptr, ACCESS_CLASS classification, HP_WORD byte_address, uint8 value)
1070 {
1071 const HP_WORD word_address = byte_address >> 1;         /* the address of the word containing the byte */
1072 HP_WORD word;
1073 
1074 word = mem_read (dptr, classification, word_address);   /* read the addressed word */
1075 
1076 if (byte_address & LSB)                                 /* if the byte address is odd */
1077     word = REPLACE_LOWER (word, value);                 /*   then replace the right-hand byte */
1078 else                                                    /* otherwise */
1079     word = REPLACE_UPPER (word, value);                 /*   replace the left-hand byte */
1080 
1081 mem_write (dptr, classification, word_address, word);   /* write the updated word back */
1082 
1083 return;
1084 }
1085 
1086 
1087 /* Fast read from memory.
1088 
1089    This routine reads and returns a word from memory at the indicated logical
1090    address using the specified map.  Memory protection is not used, and tracing
1091    is not available.
1092 
1093    This routine is used when fast, unchecked access to mapped memory is
1094    required.
1095 */
1096 
mem_fast_read(HP_WORD address,MEU_MAP_SELECTOR map)1097 HP_WORD mem_fast_read (HP_WORD address, MEU_MAP_SELECTOR map)
1098 {
1099 if (map == Current_Map)                                 /* if the current map is requested */
1100     map = meu_current_map;                              /*   then use it */
1101 
1102 return mem_examine (map_address (address, map, NO_PROTECTION)); /* return the value from the selected map */
1103 }
1104 
1105 
1106 /* Zero a range of memory locations.
1107 
1108    Main memory locations from a supplied starting address through the end of
1109    defined memory are filled with the specified value.  This routine is
1110    typically called to zero non-existent memory when the main memory size is
1111    reduced (so that non-existent locations will read as zero).
1112 */
1113 
mem_zero(uint32 starting_address,uint32 fill_count)1114 void mem_zero (uint32 starting_address, uint32 fill_count)
1115 {
1116 memset (M + starting_address, 0,                        /* zero the words */
1117         fill_count * sizeof (MEMORY_WORD));             /*   in the specified memory range */
1118 
1119 return;
1120 }
1121 
1122 
1123 /* Check for a non-zero value within a memory address range.
1124 
1125    This routine checks a range of memory locations for the presence of a
1126    non-zero value.  The starting address of the range is supplied, and the check
1127    continues through the end of defined memory.  The routine returns TRUE if the
1128    memory range was empty (i.e., contained only zero values) and FALSE
1129    otherwise.
1130 */
1131 
mem_is_empty(uint32 starting_address)1132 t_bool mem_is_empty (uint32 starting_address)
1133 {
1134 uint32 address;
1135 
1136 for (address = starting_address; address < mem_size; address++) /* loop through the specified address range */
1137     if (M [address] != 0)                                       /* if this location is non-zero */
1138         return FALSE;                                           /*   then indicate that memory is not empty */
1139 
1140 return TRUE;                                            /* return TRUE if all locations contain zero values */
1141 }
1142 
1143 
1144 /* Copy a binary loader to or from protected memory.
1145 
1146    This routine is called to copy a 64-word binary loader from a buffer to
1147    memory or vice versa.  On entry, "buffer" points at an array of MEMORY_WORDs
1148    sufficiently large to hold a 64-word binary loader, "starting_address" is the
1149    address in memory corresponding to the loader target, and "mode" is
1150    "To_Memory" to copy from the buffer to memory or "From_Memory" to copy from
1151    memory to the buffer.  If copying from memory, the copied memory area is
1152    zeroed before returning (memory is zeroed in preparation to protecting the
1153    reserved loader area).
1154 */
1155 
mem_copy_loader(MEMORY_WORD * buffer,uint32 starting_address,COPY_DIRECTION mode)1156 void mem_copy_loader (MEMORY_WORD *buffer, uint32 starting_address, COPY_DIRECTION mode)
1157 {
1158 if (mode == To_Memory)                                  /* if copying into memory */
1159     memcpy (M + starting_address, buffer,               /*   then transfer the loader */
1160             IBL_SIZE * sizeof (MEMORY_WORD));           /*     from the buffer to memory */
1161 
1162 else {                                                  /* otherwise */
1163     memcpy (buffer, M + starting_address,               /*   transfer the loader */
1164             IBL_SIZE * sizeof (MEMORY_WORD));           /*     from memory to the buffer */
1165 
1166     mem_zero (starting_address, IBL_SIZE);              /* zero the vacated memory area */
1167     }
1168 
1169 return;
1170 }
1171 
1172 
1173 /* Determine if the CPU is idle.
1174 
1175    This routine determines whether the CPU is executing an operating system idle
1176    loop.  It is called when a JMP or JMP,I instruction is executed with CPU
1177    idling enabled and no interrupt pending.
1178 
1179    The 21xx/1000 CPUs have no "wait for interrupt" instruction.  Idling in HP
1180    operating systems consists of sitting in "idle loops" that end with JMP
1181    instructions.  We test for certain known patterns when a JMP instruction is
1182    executed to decide if the simulator should idle.
1183 
1184    If execution is within a recognized idle loop, the routine returns TRUE; in
1185    response, the simulator will call the "sim_idle" routine to suspend execution
1186    until the next event service is due.  If the CPU is not executing an idle
1187    loop, the routine returns FALSE to continue normal execution.
1188 
1189    On entry, MR contains the address of the jump target, and "err_PR" contains
1190    the address of the jump instruction.  The difference gives the jump
1191    displacement.  The recognized idle patterns are operating-system-specific, as
1192    follows:
1193 
1194      for RTE-6/VM:
1195        - ISZ <n> / JMP *-1
1196        - mp_fence = 0
1197        - XEQT (address 1717B) = 0
1198        - MEU on with system map enabled
1199        - RTE verification: TBG (address 1674B) = TBG select code
1200 
1201      for RTE though RTE-IVB:
1202        - JMP *
1203        - mp_fence = 0
1204        - XEQT (address 1717B) = 0
1205        - MEU on with user map enabled (RTE-III through RTE-IVB only)
1206        - RTE verification: TBG (address 1674B) = TBG select code
1207 
1208      for DOS through DOS-III:
1209        - STF 0 / CCA / CCB / JMP *-3
1210        - DOS verification: A = B = -1, address 40B = -64, address 67B = +64
1211 
1212    Note that in DOS, the TBG is set to 100 milliseconds vs. 10 milliseconds for
1213    RTE.
1214 */
1215 
mem_is_idle_loop(void)1216 t_bool mem_is_idle_loop (void)
1217 {
1218 const int32 displacement = (int32) MR - (int32) err_PR; /* the jump displacement */
1219 
1220 if ((displacement == 0                                  /* if the jump target is * (RTE through RTE-IVB) */
1221   || displacement == -1 && (M [MR] & IR_MRG) == IR_ISZ) /*   or the target is *-1 (RTE-6/VM) and *-1 is ISZ <n> */
1222   && mp_fence == 0                                      /*   and the MP fence is zero */
1223   && M [xeqt] == 0                                      /*   and no program is executing */
1224   && M [tbg] == tbg_dibptr->select_code                 /*   and the TBG select code is correct */
1225 
1226   || displacement == -3                                 /*   or the jump target is *-3 (DOS through DOS-III) */
1227   && M [MR] == IR_STF                                   /*   and *-3 is STF 0 */
1228   && AR == 0177777u                                     /*   and the A and B registers */
1229   && BR == 0177777u                                     /*     are both set to -1 */
1230   && M [m64] == 0177700u                                /*   and the -64 and +64 base-page constants */
1231   && M [p64] == 0000100u)                               /*     are set as expected */
1232     return TRUE;                                        /*   then the system is executing an idle lop */
1233 
1234 else                                                    /* otherwise */
1235     return FALSE;                                       /*   the system is not executing an idle loop */
1236 }
1237 
1238 
1239 /* Trace the working and MP/MEM registers.
1240 
1241    This routine is called when CPU register tracing is enabled.  It reports the
1242    content of the working registers (S. A, B, X, Y, E, and O), memory protection
1243    status (on or off), interrupt system status (on or off), and the current MEU
1244    base page fence value.  If the MP or MEM working registers changed since the
1245    last trace report, an additional line is printed to report the memory protect
1246    fence and violation registers and the memory expansion status and violation
1247    registers.
1248 
1249 
1250    Implementation notes:
1251 
1252     1. The "is_1000" flag is used to include or omit, based on the CPU model,
1253        the X and Y registers from the working register trace and the MEVR and
1254        MESR from the memory protection trace.
1255 */
1256 
mem_trace_registers(FLIP_FLOP interrupt_system)1257 void mem_trace_registers (FLIP_FLOP interrupt_system)
1258 {
1259 hp_trace (&cpu_dev, TRACE_REG,              /* output the working registers */
1260           register_formats [is_1000],       /*   using a format appropriate for the CPU model */
1261           mp_value [mp_control],
1262           meu_status & MEST_FENCE_MASK,
1263           SR, AR, BR, XR, YR);
1264 
1265 fputs (register_values [E << 2 | O << 1 | interrupt_system], sim_deb);  /* output E, O, and interrupt system */
1266 fputc ('\n', sim_deb);
1267 
1268 if (mp_mem_changed) {                       /* if the MP/MEM registers have been altered */
1269     hp_trace (&cpu_dev, TRACE_REG,          /*   then output the register values */
1270               mp_mem_formats [is_1000],     /*     using a format appropriate for the CPU model */
1271               mp_value [mp_control],
1272               mp_fence, mp_VR, meu_status, meu_violation);
1273 
1274     mp_mem_changed = FALSE;                 /* clear the MP/MEM registers changed flag */
1275     }
1276 }
1277 
1278 
1279 /* Examine a physical memory address.
1280 
1281    This routine reads and returns a word from memory at the indicated physical
1282    address.  If the address lies outside of allocated memory, a zero value is
1283    returned.  There are no protections or error indications.
1284 */
1285 
mem_examine(uint32 address)1286 HP_WORD mem_examine (uint32 address)
1287 {
1288 if (address <= 1 && !(sim_switches & SIM_SW_REST))      /* if the address is 0 or 1 and not restoring memory */
1289     return ABREG [address];                             /*   then return the A or B register value */
1290 
1291 else if (address <= PA_MAX)                             /* otherwise if the address is within allocated memory */
1292     return (HP_WORD) M [address];                       /*   then return the memory value */
1293 
1294 else                                                    /* otherwise the access is outside of memory */
1295     return 0;                                           /*   which reads as zero */
1296 }
1297 
1298 
1299 /* Deposit into a physical memory address.
1300 
1301    This routine writes a word into memory at the indicated physical address.  If
1302    the address lies outside of defined memory, the write is ignored.  There are
1303    no protections or error indications.
1304 */
1305 
mem_deposit(uint32 address,HP_WORD value)1306 void mem_deposit (uint32 address, HP_WORD value)
1307 {
1308 if (address <= 1 && !(sim_switches & SIM_SW_REST))      /* if the address is 0 or 1 and not restoring memory */
1309     ABREG [address] = value & DV_MASK;                  /*   then store into the A or B register */
1310 
1311 else if (address < mem_end)                             /* otherwise if the address is within defined memory */
1312     M [address] = (MEMORY_WORD) value & DV_MASK;        /*   then store the value */
1313 
1314 return;
1315 }
1316 
1317 
1318 
1319 /* Memory Expansion Unit global utility routines */
1320 
1321 
1322 /* Configure the Memory Expansion Module.
1323 
1324    This routine enables or disables the MEM, depending on the "configuration"
1325    parameter.  If the MEM is being enabled, the "device disabled" flag is
1326    cleared.  Otherwise, the flag is set, and mapping is disabled so that address
1327    translation will not occur.
1328 
1329    The routine is called when the DMS instruction set is enabled or disabled.
1330    The MEM device state tracks the instruction state and cannot be set
1331    independently, i.e., with a SET MEM DISABLED command.
1332 */
1333 
meu_configure(MEU_STATE configuration)1334 void meu_configure (MEU_STATE configuration)
1335 {
1336 if (configuration == ME_Enabled)                        /* if DMS instructions are enabled */
1337     meu_dev.flags &= ~DEV_DIS;                          /*   then enable the MEM device */
1338 
1339 else {                                                  /* otherwise */
1340     meu_dev.flags |= DEV_DIS;                           /*   disable the MEM */
1341     meu_set_state (ME_Disabled, System_Map);            /*     and disable mapping */
1342     }
1343 
1344 return;
1345 }
1346 
1347 
1348 /* Read a map register.
1349 
1350    This routine is called to read one map register from the specified map.  The
1351    map index may be from 0-31 to read from a specific map (System_Map, User_Map,
1352    etc.) or may be from 0-127 to read a linear sequence of maps (Linear_Map).
1353    The map content (the protection bits and a physical page number corresponding
1354    to the logical page number specified by the index) is returned.
1355 */
1356 
meu_read_map(MEU_MAP_SELECTOR map,uint32 index)1357 HP_WORD meu_read_map (MEU_MAP_SELECTOR map, uint32 index)
1358 {
1359 if (map == Linear_Map)                                  /* if linear access is specified */
1360     return meu_maps [index / REG_COUNT & MAP_MASK]      /*   then use the upper index bits for the map */
1361                     [index % REG_COUNT];                /*     and the lower index bits for the register */
1362 
1363 else                                                    /* otherwise */
1364     return meu_maps [map] [index];                      /*   read from the specified map and register */
1365 }
1366 
1367 
1368 /* Write a map register.
1369 
1370    This routine is called to write a value into one map register of the
1371    specified map.  The map index may be from 0-31 to write to a specific map
1372    (System_Map, User_Map, etc.) or may be from 0-127 to write a linear sequence
1373    of maps (Linear_Map).  The map content (the protection bits and a physical
1374    page number corresponding to the logical page number specified by the index)
1375    is stored in the indicated register.
1376 */
1377 
meu_write_map(MEU_MAP_SELECTOR map,uint32 index,uint32 value)1378 void meu_write_map (MEU_MAP_SELECTOR map, uint32 index, uint32 value)
1379 {
1380 if (map == Linear_Map)                                      /* if linear access is specified */
1381     meu_maps [index / REG_COUNT & MAP_MASK]                 /*   then use the upper index bits for the map */
1382              [index % REG_COUNT] = value & ~MAP_RESERVED;   /*     and the lower index bits for the register */
1383 
1384 else                                                        /* otherwise */
1385     meu_maps [map] [index] = value & ~MAP_RESERVED;         /*   write to the specified map and register */
1386 
1387 return;
1388 }
1389 
1390 
1391 /* Set the MEM fence register.
1392 
1393    This routine sets a new value into the MEM base-page fence register.  The
1394    value must have the "portion mapped" flag in bit 10 and the fence address is
1395    bits 9-0.  No error checking is performed.
1396 */
1397 
meu_set_fence(HP_WORD new_fence)1398 void meu_set_fence (HP_WORD new_fence)
1399 {
1400 meu_status = meu_status & ~(MEST_BELOW | MEST_FENCE_MASK)   /* mask off the old mapping and address */
1401            | new_fence & (MEST_BELOW | MEST_FENCE_MASK);    /*   and merge in the new values */
1402 
1403 mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */
1404 
1405 return;
1406 }
1407 
1408 
1409 /* Set the Memory Expansion Unit state.
1410 
1411    This routine is called to enable or disable the MEM and to set the current
1412    map.
1413 */
1414 
meu_set_state(MEU_STATE operation,MEU_MAP_SELECTOR map)1415 void meu_set_state (MEU_STATE operation, MEU_MAP_SELECTOR map)
1416 {
1417 if (operation == ME_Enabled)                            /* if the MEM is being enabled */
1418     meu_status |= MEST_ENABLED;                         /*   then set the MEM enabled status bit */
1419 
1420 else {                                                  /* otherwise disable the MEM */
1421     meu_status &= ~MEST_ENABLED;                        /*   by clearing the MEM enabled status bit */
1422     meu_bus_enabled = FALSE;                            /*     and disabling the MEM expansion bus */
1423     }
1424 
1425 meu_current_map = map;                                  /* set the current map in either case */
1426 mp_mem_changed = TRUE;                                  /*   and set the MP/MEM registers changed flag */
1427 
1428 return;
1429 }
1430 
1431 
1432 /* Update the MEM violation register.
1433 
1434    This routine is called to update the MEM violation register.  This is done
1435    whenever the value in the register might be examined.
1436 
1437    In hardware, the MEM violation register (MEVR) is clocked on every memory
1438    read, every JMP or memory write (actually, every use of the MPCK micro-order)
1439    above the lower bound of protected memory, and every execution of a
1440    privileged DMS instruction.  The register is not clocked when MP is disabled
1441    by an MP or MEM error (i.e., when MEVFF sets or CTL5FF clears), in order to
1442    capture the state of the MEM.  In other words, the MEVR continually tracks
1443    the memory map register accessed plus the MEM state (MEBEN, MAPON, and USR)
1444    until a violation occurs, and then it's "frozen."
1445 
1446    Under simulation, we do not have to update the MEVR on every memory access,
1447    because the visible state is only available via a programmed RVA/B
1448    instruction or via the SCP interface.  Therefore, it is sufficient if the
1449    register is updated:
1450 
1451      - at a MEM violation (when freezing)
1452      - at an MP violation (when freezing)
1453      - during RVA/B execution (if not frozen)
1454      - before returning to SCP after a simulator stop (if not frozen)
1455 
1456    The routine returns the updated content of the violation register.
1457 */
1458 
meu_update_violation(void)1459 HP_WORD meu_update_violation (void)
1460 {
1461 if (mp_control == SET && mp_mevff == CLEAR) {           /* if the violation register is not frozen */
1462     meu_violation = PAGE (MR);                          /*   then set the last map addressed */
1463 
1464     if (meu_status & MEST_ENABLED)                      /* if the MEM is currently enabled */
1465         meu_violation |= MEVI_MEM_ENABLED;              /*   then add the status bit */
1466 
1467     if (meu_current_map == User_Map)                    /* if the user map is currently enabled */
1468         meu_violation |= MEVI_USER_MAP;                 /*   then add the status bit */
1469 
1470     if (meu_bus_enabled)                                /* if the last memory address was mapped */
1471         meu_violation |= MEVI_BUS_ENABLED;              /*  then add the "ME bus is enabled" bit */
1472 
1473     mp_mem_changed = TRUE;                              /* set the MP/MEM registers changed flag */
1474     }
1475 
1476 return meu_violation;                                   /* return the violation register content */
1477 }
1478 
1479 
1480 /* Update the MEM status register.
1481 
1482    This routine is called to update the MEM status register.  This is done
1483    whenever the value in the register might be examined.
1484 
1485    In hardware, the MEM status register (MESR) is not a physical register but
1486    rather a set of tristate drivers that enable the base-page fence register,
1487    the current state of the MEM (disabled or enabled, system or user map), and
1488    the MEM state at last interrupt onto the CPU's S-bus.
1489 
1490    Under simulation, we do not have to update the MESR each time the current map
1491    changes, because the visible state is only available via programmed RSA/B and
1492    SSM instructions, via an RTE OS trap cell instruction (where it is used to
1493    save the MEM state), or via the SCP interface.  Therefore, it is sufficient
1494    if the register is updated:
1495 
1496      - during RSA/B or SSM or RTE OS trap cell instruction execution
1497      - before returning to SCP after a simulator stop
1498 
1499    The routine returns the updated content of the status register.
1500 */
1501 
meu_update_status(void)1502 HP_WORD meu_update_status (void)
1503 {
1504 meu_status &= ~MEST_DYNAMIC;                            /* clear the current MEM state */
1505 
1506 if (meu_current_map == User_Map)                        /* if the user map is enabled */
1507     meu_status |= MEST_USER_MAP;                        /*   then set the currently enabled bit */
1508 
1509 if (mp_control == SET)                                  /* if MP is enabled */
1510     meu_status |= MEST_PROTECTED;                       /*   then set the protected mode bit */
1511 
1512 mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */
1513 
1514 return meu_status;                                      /* return the status register value */
1515 }
1516 
1517 
1518 /* Assert an Interrupt Acknowledge signal to the MEM.
1519 
1520    This routine asserts the IAK signal to the Memory Expansion Module.  It is
1521    called when the CPU acknowledges an interrupt.  In response, the MEM saves
1522    its current state and switches to the system map for interrupt processing.
1523 
1524    In addition, if the CPU is tracing instructions, the routine calls
1525    "map_address" to set the current map indicator and the page number of the
1526    next instruction to execute.  This will be used by the CPU to print the
1527    interrupt location.
1528 */
1529 
meu_assert_IAK(void)1530 void meu_assert_IAK (void)
1531 {
1532 meu_status &= ~MEST_DYNAMIC_IAK;                        /* clear the MEM interrupt state */
1533 
1534 if (meu_status & MEST_ENABLED)                          /* if the MEM is enabled */
1535     meu_status |= MEST_ENBL_INT;                        /*   then add the enabled-at-interrupt bit */
1536 
1537 if (meu_current_map == User_Map)                        /* if the user map is enabled */
1538     meu_status |= MEST_UMAP_INT;                        /*   then add the user-map-at-interrupt bit */
1539 
1540 if (TRACING (cpu_dev, TRACE_INSTR))                     /* if instruction tracing is active */
1541     map_address (PR, meu_current_map, NO_PROTECTION);   /*   then set the MEM page and indicator */
1542 
1543 meu_current_map = System_Map;                           /* switch to the system map for the interrupt */
1544 
1545 mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */
1546 
1547 return;
1548 }
1549 
1550 
1551 /* Generate a MEM privilege violation.
1552 
1553    This routine conditionally generates a dynamic mapping violation.  If the
1554    condition is "Always", then a privilege violation is generated.  If the
1555    condition is "If_User_Map", then a violation occurs if the user map is the
1556    current map; otherwise, no violation occurs.
1557 
1558 
1559    Implmentation notes:
1560 
1561     1. If the MEM is in the protected mode, i.e., memory protect is on, a DM
1562        violation will cause a microcode abort, and this routine will not return.
1563 */
1564 
meu_privileged(MEU_CONDITION condition)1565 void meu_privileged (MEU_CONDITION condition)
1566 {
1567 if (condition == Always || meu_current_map == User_Map)
1568     dm_violation (MEVI_PRIVILEGE);
1569 
1570 return;
1571 }
1572 
1573 
1574 /* Get the current MEM breakpoint type.
1575 
1576    This routine returns a command line switch value representing the breakpoint
1577    type that corresponds to the current MEM configuration.  It is used to get
1578    the current default breakpoint type, as follows:
1579 
1580      MEM State  Current Map  Breakpoint Type
1581      ---------  -----------  ---------------
1582      disabled       --              N
1583       enabled     System            S
1584       enabled      User             U
1585 
1586    The "is_iak" parameter is used to qualify the "U" type.  If the user map is
1587    currently enabled but an interupt acknowledgement is pending, then the
1588    returned type is "S", as the IAK will be handled in the system map.
1589 */
1590 
meu_breakpoint_type(t_bool is_iak)1591 uint32 meu_breakpoint_type (t_bool is_iak)
1592 {
1593 if (meu_status & MEST_ENABLED)                          /* if MEM is currently enabled */
1594     if (meu_current_map == User_Map && !is_iak)         /*   then if the user map is currently enabled */
1595         return SWMASK ('U');                            /*     then return the user breakpoint switch */
1596     else                                                /*   otherwise */
1597         return SWMASK ('S');                            /*     return the system breakpoint switch */
1598 else                                                    /* otherwise MEM is disabled */
1599     return SWMASK ('N');                                /*   so return the non-MEM breakpoint switch */
1600 }
1601 
1602 
1603 /* Translate a logical address for console access.
1604 
1605    This routine translates a logical address interpreted in the context of the
1606    translation map implied by the specified switch to a physical address.  It is
1607    called to map addresses when the user is examining or depositing memory.  It
1608    is also called to restore a saved configuration, although mapping is not used
1609    for restoration.  All memory protection checks are off for console access.
1610 
1611    Command line switches modify the interpretation of logical addresses as
1612    follows:
1613 
1614      Switch  Meaning
1615      ------  --------------------------------------------------
1616        -N    Use the address directly with no mapping
1617        -S    If memory expansion is enabled, use the system map
1618        -U    If memory expansion is enabled, use the user map
1619        -P    If memory expansion is enabled, use the port A map
1620        -Q    If memory expansion is enabled, use the port B map
1621 
1622    If no switch is specified, then the address is interpreted using the current
1623    map if memory expansion is enabled; otherwise, the address is not mapped.  If
1624    the current or specified map is used, then the address must lie within the
1625    32K logical address space; if not, then an address larger than the current
1626    memory size is returned to indicate that a translation error occurred.
1627 */
1628 
meu_map_address(HP_WORD logical,int32 switches)1629 uint32 meu_map_address (HP_WORD logical, int32 switches)
1630 {
1631 MEU_MAP_SELECTOR map;
1632 
1633 if (switches & (SWMASK ('N') | SIM_SW_REST))            /* if no mapping is requested */
1634     return logical;                                     /*   then the address is already a physical address */
1635 
1636 else if ((meu_status & MEST_ENABLED) == 0               /* otherwise if the MEM is disabled */
1637   && switches & ALL_MAPMODES)                           /*   but a mapping mode was given */
1638     return D32_UMAX;                                    /*     then the command is not allowed */
1639 
1640 else if ((meu_status & MEST_ENABLED || switches & ALL_MAPMODES) /* otherwise if mapping is enabled or requested */
1641   && logical > LA_MAX)                                          /*   and the address is not a logical address */
1642     return mem_size;                                            /*     then report a memory overflow */
1643 
1644 else if (switches & SWMASK ('S'))                       /* otherwise if the -S switch is specified */
1645     map = System_Map;                                   /*   then use the system map */
1646 
1647 else if (switches & SWMASK ('U'))                       /* otherwise if the -U switch is specified */
1648     map = User_Map;                                     /*   then use the user map */
1649 
1650 else if (switches & SWMASK ('P'))                       /* otherwise if the -P switch is specified */
1651     map = Port_A_Map;                                   /*   then use the DCPC port A map */
1652 
1653 else if (switches & SWMASK ('Q'))                       /* otherwise if the -Q switch is specified */
1654     map = Port_B_Map;                                   /*   then use the DCPC port B map */
1655 
1656 else                                                    /* otherwise */
1657     map = meu_current_map;                              /*   use the current map (system or user) */
1658 
1659 return map_address (logical, map, NO_PROTECTION);       /* translate the address without protection */
1660 }
1661 
1662 
1663 
1664 /* Memory Expansion Unit local SCP support routines */
1665 
1666 
1667 /* Memory Expansion Unit reset.
1668 
1669    The MEM processes POPIO but is not addressed by a select code and so does not
1670    have an I/O interface.  Therefore, we handle POPIO here.
1671 */
1672 
meu_reset(DEVICE * dptr)1673 static t_stat meu_reset (DEVICE *dptr)
1674 {
1675 meu_current_map = System_Map;                           /* enable the system map */
1676 
1677 meu_status = 0;                                         /* disable MEM and clear the status register */
1678 meu_violation = 0;                                      /* clear the violation register */
1679 
1680 mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */
1681 
1682 return SCPE_OK;
1683 }
1684 
1685 
1686 
1687 /* Memory Expansion Unit local utility routines */
1688 
1689 
1690 /* Process a MEM violation.
1691 
1692    A MEM violation will report the cause in the violation register.  The
1693    register is not clocked when MP is disabled by an MP or MEM error (i.e., when
1694    MEVFF sets or CTL5FF clears), in order to capture the state of the MEM.  In
1695    other words, the MEVR continually tracks the memory map register accessed
1696    plus the MEM state (MEBEN, MAPON, and USR) until a violation occurs, and then
1697    it's "frozen."
1698 
1699    If MP is enabled, an MP abort is taken with the MEV flip-flop set.
1700    Otherwise, we return to the caller.
1701 */
1702 
dm_violation(HP_WORD violation)1703 static void dm_violation (HP_WORD violation)
1704 {
1705 if (mp_control == SET) {                                        /* if memory protect is enabled */
1706     if (mp_mevff == CLEAR)                                      /*   then if the violation register is not frozen */
1707         meu_violation = violation | meu_update_violation ();    /*     then set the cause in the violation register */
1708 
1709     mp_mem_changed = TRUE;                              /* set the MP/MEM registers changed flag */
1710 
1711     mp_mevff = SET;                                     /* record a memory expansion violation */
1712     mp_violation ();                                    /*   and a memory protect violation */
1713     }
1714 
1715 return;
1716 }
1717 
1718 
1719 /* Determine whether an address is mapped.
1720 
1721    This routine determines whether a logical address is mapped to a physical
1722    address or represents a physical address itself.  It corresponds to the
1723    hardware MEBEN (Memory Expansion Bus Enable) signal and indicates that a
1724    memory access is not in the unmapped portion of the base page.  The routine
1725    is called only if the MEM is enabled and returns TRUE if the address is
1726    mapped or FALSE if it is unmapped.  Before returning, "meu_bus_enabled" is
1727    set to reflect the mapping state.
1728 */
1729 
is_mapped(HP_WORD address)1730 static t_bool is_mapped (HP_WORD address)
1731 {
1732 HP_WORD dms_fence;
1733 
1734 if (address <= 1)                                       /* if the reference is to the A or B register */
1735     meu_bus_enabled = FALSE;                            /*   then the location is not mapped */
1736 
1737 else if (address <= LWA_BASE_PAGE) {                    /* otherwise if the address is on the base page */
1738     dms_fence = meu_status & MEST_FENCE_MASK;           /*   then get the base-page fence value */
1739 
1740     if (meu_status & MEST_BELOW)                        /* if the lower portion is mapped */
1741         meu_bus_enabled = (address < dms_fence);        /*   then mapping occurs if the address is below the fence */
1742     else                                                /* otherwise the upper portion is mapped */
1743         meu_bus_enabled = (address >= dms_fence);       /*   so mapping occurs if the address is at or above the fence */
1744     }
1745 
1746 else                                                    /* otherwise the address is not on page 0 */
1747     meu_bus_enabled = TRUE;                             /*   so it is always mapped */
1748 
1749 return meu_bus_enabled;                                 /* return the mapping state */
1750 }
1751 
1752 
1753 /* Map a logical address to a physical address.
1754 
1755    This routine translates logical to physical addresses.  The logical address,
1756    desired map, and desired access protection are supplied.  If the access is
1757    legal, the mapped physical address is returned; if it is not, then a MEM
1758    violation occurs.
1759 
1760    The current map may be specified by passing "meu_current_map" as the "map"
1761    parameter, or a specific map may be used.  Normally, read and write accesses
1762    pass READ_PROTECTED or WRITE_PROTECTED, respectively, as the "protection"
1763    parameter to request access checking.  For DCPC accesses, NO_PROTECTION must
1764    be passed to inhibit access checks.
1765 
1766    This routine checks for read, write, and base-page violations and will call
1767    "dm_violation" as appropriate.  The latter routine will abort if MP is
1768    enabled, or will return if protection is off.
1769 */
1770 
map_address(HP_WORD address,MEU_MAP_SELECTOR map,HP_WORD protection)1771 static uint32 map_address (HP_WORD address, MEU_MAP_SELECTOR map, HP_WORD protection)
1772 {
1773 uint32 map_register;
1774 
1775 if (meu_status & MEST_ENABLED) {                        /* if the Memory Expansion Unit is enabled */
1776     meu_indicator = map_indicator [map];                /*   then set the map indicator to the applied map */
1777 
1778     if (address > LWA_BASE_PAGE                         /* if the address is not on the base page */
1779       || map >= Port_A_Map                              /*   or this is a DCPC transfer */
1780       || is_mapped (address)) {                         /*     or it is to the mapped portion of the base page */
1781         map_register = meu_maps [map] [PAGE (address)]; /*       then get the map register for the logical page */
1782 
1783         meu_page = MAP_PAGE (map_register);             /* save the physical page number */
1784 
1785         if (map_register & protection)                  /* if the desired access is not allowed */
1786             dm_violation (protection);                  /*   then a read or write protection violation occurs */
1787 
1788         return TO_PA (meu_page, address);               /* form the physical address from the mapped page and offset */
1789         }
1790 
1791     else {                                              /* otherwise the address is unmapped */
1792         meu_page = 0;                                   /*   so the physical page is page 0 */
1793 
1794         if (address > 1 && protection == WRITE_PROTECTED)   /* a write to the unmapped part of the base page */
1795             dm_violation (MEVI_BASE_PAGE);                  /*   causes a base-page violation if protection is enabled */
1796 
1797         return address;                                 /* the address is already physical */
1798         }
1799     }
1800 
1801 else {                                                  /* otherwise the MEU is disabled */
1802     meu_page = PAGE (address);                          /*   so the physical page is the logical page */
1803     meu_indicator = '-';                                /*     and no mapping occurs */
1804 
1805     return address;                                     /* the physical address is the logical address */
1806     }
1807 }
1808 
1809 
1810 
1811 /* Memory Protect I/O interface routine */
1812 
1813 
1814 /* Memory Protect/Parity Error interface (select code 05).
1815 
1816    I/O operations directed to select code 5 manipulate the Memory Protect
1817    accessory.  They also affect main memory parity error and memory expansion
1818    violation reporting.
1819 
1820    STC turns on memory protect, which is turned off only by an MP violation or a
1821    POPIO.  CLC does nothing.  STF and CLF turn parity error interrupts on and
1822    off.  SFS skips if a MEM violation occurred, while SFC skips if an MP
1823    violation occurred.  IOI reads the MP violation register; bit 15 of the
1824    register is 1 for a parity error and 0 for an MP error.  IOO outputs the
1825    address of the start of unprotected memory to the MP fence.  PRL and IRQ are
1826    a function of the MP flag flip-flop only, not the flag and control flip-flops
1827    as is usual.
1828 
1829    IAK is asserted when any interrupt is acknowledged by the CPU.  Normally, an
1830    interface qualifies IAK with its own IRQ to ensure that it responds only to
1831    an acknowledgement of its own request.  The MP card does this to reset its
1832    flag buffer and flag flip-flops, and to reset the parity error indication.
1833    However, it also responds to an unqualified IAK (i.e., for any interface) by
1834    clearing the MPV flip-flop, clearing the indirect counter, clearing the
1835    control flip-flop, and setting the INTPT flip-flop.
1836 
1837    The hardware INTPT flip-flop indicates an occurrence of an interrupt.  If the
1838    trap cell of the interrupting device contains an I/O instruction that is not
1839    a HLT, action equivalent to STC 05 is taken, i.e., the interface sets the
1840    control and EVR (Enable Violation Register) flip-flops and clears the MEV
1841    (Memory Expansion Violation) and PARERR (Parity Error) flip-flops.
1842 
1843    In simulation, this is handled during IAK processing by setting "mp_enabled"
1844    to the state of the MP control flip-flop and scheduling the MP event service
1845    routine to enter after the next instruction.  If the next instruction, which
1846    is the trap cell instruction, is an I/O instruction, "cpu_iog" will call
1847    "mp_check_io" as part of its processing.  If that routine is called for a
1848    non-HLT instruction, it sets "mp_reenable" to the value saved in
1849    "mp_enabled", i.e., "mp_reenable" will be SET if MP was enabled when the
1850    interrupt occurred (it's initialized to CLEAR).  When the service routine is
1851    entered after the trap instruction executes, it sets "mp_control" to the
1852    value of "mp_reenable", which reenables MP if MP was on.
1853 
1854    The effect of all of this is to turn MP off when an interrupt occurs but then
1855    to reenable it if the interrupt trap cell contained a non-HLT I/O
1856    instruction.  For example, consider a program executing with MP on and an
1857    interrupt from an interface whose trap cell contains a CLF instruction.  When
1858    the interrupt occurs, MP is turned off, the CLF is executed, MP is turned on,
1859    and the program continues.  If the trap cell contained a HLT, MP would be
1860    turned off, and then the CPU would halt.  If the trap cell contained a JSB,
1861    MP would be turned off and would remain off while the interrupt subroutine
1862    executes.
1863 
1864 
1865    Implementation notes:
1866 
1867     1. Because the MP card uses IAK unqualified, this routine is called whenever
1868        any interrupt occurs.  It is also called when the MP card itself is
1869        interrupting.  The latter condition is detected by the MP flag flip-flop
1870        being set.  As MP has higher priority than all devices except power fail,
1871        if the flag is set, the IAK must be for the MP card.
1872 
1873     2. The MEV flip-flop records memory expansion violations.  It is set when a
1874        MEM violation is encountered and can be tested via SFC/SFS.
1875 
1876     3. The Parity Error logic is not currently implemented.
1877 */
1878 
mp_interface(const DIB * dibptr,INBOUND_SET inbound_signals,HP_WORD inbound_value)1879 static SIGNALS_VALUE mp_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
1880 {
1881 INBOUND_SIGNAL signal;
1882 INBOUND_SET    working_set  = inbound_signals;
1883 SIGNALS_VALUE  outbound     = { ioNONE, 0 };
1884 t_bool         irq_enabled  = FALSE;
1885 
1886 while (working_set) {                                   /* while signals remain */
1887     signal = IONEXTSIG (working_set);                   /*   isolate the next signal */
1888 
1889     switch (signal) {                                   /* dispatch the I/O signal */
1890 
1891         case ioCLF:                                     /* Clear Flag flip-flop */
1892             break;                                      /* turn parity error interrupts off */
1893 
1894 
1895         case ioSTF:                                     /* Set Flag flip-flop */
1896             break;                                      /* turn parity error interrupts on */
1897 
1898 
1899         case ioENF:                                     /* Enable Flag */
1900             if (mp_flag_buffer == SET)                  /* if the flag buffer flip-flop is set */
1901                 if (inbound_signals & ioIEN) {          /*   then if the interrupt system is on */
1902                     mp_flag = SET;                      /*     then set the flag flip-flop */
1903                     mp_evrff = CLEAR;                   /*       and inhibit violation register updates */
1904                     }
1905 
1906                 else                                    /*   otherwise interrupts are off */
1907                     mp_flag_buffer = CLEAR;             /*     and the flag buffer does not set if IEN5 is denied */
1908             break;
1909 
1910 
1911         case ioSFC:                                     /* Skip if Flag is Clear */
1912             if (mp_mevff == CLEAR)                      /* if this is a memory protect violation */
1913                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
1914             break;
1915 
1916 
1917         case ioSFS:                                     /* Skip if Flag is Set */
1918             if (mp_mevff == SET)                        /* if this is a memory expansion violation */
1919                 outbound.signals |= ioSKF;              /*   then assert the Skip on Flag signal */
1920             break;
1921 
1922 
1923         case ioIOI:                                     /* I/O Data Input */
1924             outbound.value = mp_VR;                     /* return the MP violation register */
1925             break;
1926 
1927 
1928         case ioIOO:                                     /* I/O Data Output */
1929             mp_fence = inbound_value & LA_MASK;         /* store the address in the MP fence register */
1930 
1931             if (cpu_configuration & CPU_2100)           /* the 2100 IOP instructions */
1932                 SPR = mp_fence;                         /*   use the MP fence as a stack pointer */
1933 
1934             mp_mem_changed = TRUE;                      /* set the MP/MEM registers changed flag */
1935             break;
1936 
1937 
1938         case ioPOPIO:                                   /* Power-On Preset to I/O */
1939             mp_control     = CLEAR;                     /* clear the control flip-flop */
1940             mp_flag_buffer = CLEAR;                     /*   and the flag buffer flip-flop */
1941             mp_flag        = CLEAR;                     /*     and the flag flip-flop */
1942 
1943             mp_mevff = CLEAR;                           /* clear the Memory Expansion Violation flip-flop */
1944             mp_evrff = SET;                             /*   and set the Enable Violation Fegister flip-flop */
1945 
1946             mp_reenable = CLEAR;                        /* clear the MP reenable */
1947             mp_enabled  = CLEAR;                        /*   and MP currently enabled flip-flops */
1948 
1949             mp_mem_changed = TRUE;                      /* set the MP/MEM registers changed flag */
1950             break;
1951 
1952 
1953         case ioSTC:                                     /* Set Control flip-flop */
1954             mp_control = SET;                           /* set the control flip-flop to turn on MP */
1955 
1956             mp_mevff = CLEAR;                           /* clear the Memory Expansion Violation flip-flop */
1957             mp_evrff = SET;                             /*   and set the Enable Violation Register flip-flop */
1958             break;
1959 
1960 
1961         case ioSIR:                                     /* Set Interrupt Request */
1962             if (mp_flag)                                /* if the flag flip-flop is set */
1963                 outbound.signals |= cnIRQ | cnVALID;    /*   then deny PRL and conditionally assert IRQ */
1964             else                                        /* otherwise */
1965                 outbound.signals |= cnPRL | cnVALID;    /*   conditionally assert PRL */
1966             break;
1967 
1968 
1969         case ioIAK:                                     /* Interrupt Acknowledge */
1970             if (mp_flag) {                              /* if the MP itself is interrupting */
1971                 mp_flag_buffer = CLEAR;                 /*   then clear the flag buffer */
1972                 mp_flag        = CLEAR;                 /*     and flag flip-flops */
1973                 }
1974 
1975             mp_enabled = mp_control;                    /* set the MP interrupt flip-flop if MP was on */
1976             mp_control = CLEAR;                         /*   and then turn memory protection off */
1977 
1978             sim_activate (mp_unit, mp_unit [0].wait);   /* schedule a status check for the next instruction */
1979             break;
1980 
1981 
1982         case ioIEN:                                     /* Interrupt Enable */
1983             irq_enabled = TRUE;                         /* permit IRQ to be asserted */
1984             break;
1985 
1986 
1987         case ioPRH:                                         /* Priority High */
1988             if (irq_enabled && outbound.signals & cnIRQ)    /* if IRQ is enabled and conditionally asserted */
1989                 outbound.signals |= ioIRQ | ioFLG;          /*   then assert IRQ and FLG */
1990 
1991             if (!irq_enabled || outbound.signals & cnPRL)   /* if IRQ is disabled or PRL is conditionally asserted */
1992                 outbound.signals |= ioPRL;                  /*   then assert it unconditionally */
1993             break;
1994 
1995 
1996         case ioCRS:                                     /* not used by this interface */
1997         case ioCLC:                                     /* not used by this interface */
1998         case ioEDT:                                     /* not used by this interface */
1999         case ioPON:                                     /* not used by this interface */
2000             break;
2001         }
2002 
2003     IOCLEARSIG (working_set, signal);                   /* remove the current signal from the set */
2004     }                                                   /*   and continue until all signals are processed */
2005 
2006 return outbound;                                        /* return the outbound signals and value */
2007 }
2008 
2009 
2010 
2011 /* Memory Protect global utility routines */
2012 
2013 
2014 /* Initialize memory protect.
2015 
2016    This routine is called from the instruction execution prelude to set up the
2017    internal state of the memory protect accessory.  It returns the state of the
2018    MP device (enabled or disabled) to avoid having to make the DEVICE structure
2019    global.
2020 */
2021 
mp_initialize(void)2022 t_bool mp_initialize (void)
2023 {
2024 is_1000 = (cpu_configuration & CPU_1000) != 0;          /* set the CPU model index */
2025 
2026 mp_mem_changed = TRUE;                                  /* request an initial MP/MEM trace */
2027 
2028 return (mp_dev.flags & DEV_DIS) == 0;                   /* return TRUE if MP is enabled and FALSE if not */
2029 }
2030 
2031 
2032 /* Configure the Memory Protect accessory.
2033 
2034    This routine enables or disables MP, depending on the "is_enabled" parameter,
2035    and makes the MP configurable or non-configurable, depending on the
2036    "is_optional" parameter.  It adds or removes the DEV_DIS device flag to
2037    disable or enable the device, and adds or removes the DEV_DISABLE device flag
2038    to allow or deny the use of SET MP ENABLED/DISABLED SCP commands to change
2039    the device state.
2040 */
2041 
mp_configure(t_bool is_enabled,t_bool is_optional)2042 void mp_configure (t_bool is_enabled, t_bool is_optional)
2043 {
2044 if (is_enabled)                                         /* if MP is to be enabled */
2045     mp_dev.flags &= ~DEV_DIS;                           /*   then remove the "disabled" flag */
2046 else                                                    /* otherwise */
2047     mp_dev.flags |= DEV_DIS;                            /*   add the flag to disable the device */
2048 
2049 if (is_optional)                                        /* if MP is to be made configurable */
2050     mp_dev.flags |= DEV_DISABLE;                        /*   then add the "can be disabled" flag */
2051 else                                                    /* otherwise */
2052     mp_dev.flags &= ~DEV_DISABLE;                       /*   make the current setting unalterable */
2053 
2054 return;
2055 }
2056 
2057 
2058 /* Check a jump for memory protect or memory expansion violations.
2059 
2060    This routine checks a jump target address for protection violations.  On
2061    entry, "address" is the logical address of the jump target, and "lower_bound"
2062    is the lowest protected memory address.  If a violation occurs, the routine
2063    does not return; instead, a microcode abort is taken.
2064 
2065    Program execution jumps are a special case of write validation.  The target
2066    address is treated as a write, even when no physical write takes place (e.g.,
2067    when executing a JMP instead of a JSB), so jumping to a write-protected page
2068    causes a MEM violation.  In addition, a MEM violation occurs if the jump is
2069    to the unmapped portion of the base page.  Finally, jumping to a location
2070    under the memory-protect fence causes an MP violation.
2071 
2072    Because the MP and MEM hardware works in parallel, all three violations may
2073    exist concurrently.  For example, a JMP to the unmapped portion of the base
2074    page that is write protected and under the MP fence will indicate a
2075    base-page, a write, and an MP violation, whereas a JMP to the mapped portion
2076    will indicate a write and an MP violation (BPV is inhibited by the MEBEN
2077    signal).  If MEM and MP violations occur concurrently, the MEM violation
2078    takes precedence, as the SFS and SFC instructions test the MEV flip-flop.
2079 
2080    The lower bound of protected memory must be either 0 or 2.  All violations
2081    are qualified by the MPCND signal, which responds to the lower bound.
2082    Therefore, if the lower bound is 2, and if the part below the base-page fence
2083    is unmapped, or if the base page is write-protected, then a MEM violation
2084    will occur only if the access is not to locations 0 or 1.  The instruction
2085    set firmware uses a lower bound of 0 for JMP, JLY, and JPY (and for JSB with
2086    W5 out), and of 2 for DJP, SJP, UJP, JRS, and .GOTO (and JSB with W5 in).
2087 
2088    Finally, all violations are inhibited if MP is off (i.e., the MP control
2089    flip-flop is clear), and MEM violations are inhibited if the MEM is disabled.
2090 */
2091 
mp_check_jmp(HP_WORD address,uint32 lower_bound)2092 void mp_check_jmp (HP_WORD address, uint32 lower_bound)
2093 {
2094 const uint32 lp = PAGE (address);                       /* the logical page number */
2095 HP_WORD violation = 0;                                  /* the MEM violation conditions */
2096 
2097 if (mp_control == SET) {                                        /* if memory protect is enabled */
2098     if (meu_status & MEST_ENABLED) {                            /*   then if the MEM is enabled */
2099         if (meu_maps [meu_current_map] [lp] & WRITE_PROTECTED)  /*     then if the page is write protected */
2100             violation = MEVI_WRITE;                             /*       then a write violation occurs */
2101 
2102         if (address >= lower_bound && ! is_mapped (address))    /* if the unmapped base page is the target */
2103             violation |= MEVI_BASE_PAGE;                        /*   then a base page violation occurs */
2104 
2105         if (violation)                                  /* if a violation occurred */
2106             dm_violation (violation);                   /*   then assert a MEM violation */
2107         }
2108 
2109     if (address >= lower_bound && address < mp_fence)   /* if the jump is under the memory protect fence */
2110         mp_violation ();                                /*   then a memory protect violation occurs */
2111     }
2112 
2113 return;
2114 }
2115 
2116 
2117 /* Check a jump-to-subroutine for memory protect or memory expansion violations.
2118 
2119    This routine checks a jump-to-subroutine target address for protection
2120    violations.  On entry, "address" is the logical address of the jump target.
2121    If a violation occurs, the routine does not return; instead, a microcode
2122    abort is taken.
2123 
2124    The protected lower bound address for the JSB instruction depends on the W5
2125    jumper setting.  If W5 is in, then the lower bound is 2, allowing JSBs to the
2126    A and B registers.  If W5 is out, then the lower bound is 0, just as with
2127    JMP.
2128 */
2129 
mp_check_jsb(HP_WORD address)2130 void mp_check_jsb (HP_WORD address)
2131 {
2132 mp_check_jmp (address, jsb_bound);                      /* check the jump target with the selectex bound */
2133 
2134 return;
2135 }
2136 
2137 
2138 /* Check an I/O operation for memory protect violations.
2139 
2140    This routine is called by the IOG instruction executor to verify that an I/O
2141    instruction is allowed under the current protection settings.  On entry,
2142    "select_code" is set to the select code addressed by the instruction, and
2143    "micro_op" is the IOG operation to be executed.  The routine returns if the
2144    operation is allowed.  Otherwise, an MP abort is performed.
2145 
2146    If MP is off, then all I/O instructions are allowed.  MP will be off during
2147    execution of an IOG instruction in an interrupt trap cell; in this case, MP
2148    will be reenabled if the instruction is not a HLT and MP was enabled prior to
2149    the interrupt.
2150 
2151    If MP is on, then HLT instructions are illegal and will cause a memory
2152    protect violation.  If jumper W7 (SEL1) is in, then all other I/O
2153    instructions are legal; if W7 is out, then only I/O instructions that address
2154    select code 1 are legal, and I/O to other select codes will cause a
2155    violation.
2156 */
2157 
mp_check_io(uint32 select_code,IO_GROUP_OP micro_op)2158 void mp_check_io (uint32 select_code, IO_GROUP_OP micro_op)
2159 {
2160 if (mp_control == CLEAR) {                              /* if memory protect is off */
2161     if (micro_op != iog_HLT && micro_op != iog_HLT_C)   /*   then if the instruction is not a HLT */
2162         mp_reenable = mp_enabled;                       /*     then set up to reenable if servicing an interrupt */
2163     }
2164 
2165 else if (micro_op == iog_HLT || micro_op == iog_HLT_C           /* otherwise if checking a HLT instruction */
2166   || select_code != OVF && (mp_unit [0].flags & UNIT_MP_SEL1))  /*   or SC is not 1 and the SEL1 jumper is out */
2167     mp_violation ();                                            /*     then a memory protect violation occurs */
2168 
2169 return;
2170 }
2171 
2172 
2173 /* Process a memory protect violation.
2174 
2175    If memory protect is on, this routine updates the MEM violation register (if
2176    this is an MP and not a MEM violation), sets the MP flag buffer and flag
2177    flip-flops (if interupts are enabled), and performs a microcode abort.  The
2178    latter does a "longjmp" back to the microcode abort handler just prior to the
2179    CPU instruction execution loop.
2180 
2181    If memory protect is off, MP violations are ignored.
2182 
2183 
2184    Implementation notes:
2185 
2186     1. The "cpu_microcode_abort" routine is called both for MP and MEM
2187        violations.  The MEV flip-flop will be clear for the former and set for
2188        the latter.  The MEV violation register will be updated by
2189        "meu_update_violation" only if the call is NOT for an MEM violation; if
2190        it is, then the register has already been set and should not be
2191        disturbed.
2192 */
2193 
mp_violation(void)2194 void mp_violation (void)
2195 {
2196 if (mp_control == SET) {                                /* if memory protect is on */
2197     meu_update_violation ();                            /*   then update the MEVR (if not a MEV) */
2198 
2199     mp_flag_buffer = SET;                               /* set the MP flag buffer flip-flop (if IEN) */
2200     io_assert (&mp_dev, ioa_ENF);                       /*   and the flag flip-flop (if IEN) */
2201 
2202     cpu_microcode_abort (Memory_Protect);               /* abort the instruction */
2203     }
2204 
2205 return;
2206 }
2207 
2208 
2209 /* Turn memory protect off.
2210 
2211    This routine is called to disable memory protect.  In hardware, MP cannot be
2212    turned off, except by causing a violation.  Microcode typically does this by
2213    executing an IOG micro-order with a select code not equal to 1, followed by
2214    an IAK to clear the interrupt, and a FTCH to clear the INTPT flip-flop.
2215    Under simulation, clearing the MP control flip-flop produces the same effect.
2216 
2217    This routine also cancels any scheduled MP event service, in case it's called
2218    during execution of a microcoded trap cell instruction.
2219 */
2220 
mp_disable(void)2221 void mp_disable (void)
2222 {
2223 mp_control = CLEAR;                                     /* clear the control flip-flop to turn MP off */
2224 
2225 mp_reenable = CLEAR;                                    /* clear the MP reenable */
2226 mp_enabled  = CLEAR;                                    /*   and MP currently enabled flip-flops */
2227 
2228 sim_cancel (mp_unit);                                   /* cancel any pending MP reenable */
2229 
2230 return;
2231 }
2232 
2233 
2234 /* Report the memory protect state.
2235 
2236    This routine returns TRUE if MP is on and FALSE otherwise.  It is used by the
2237    RTE OS microcode executors to check the protection state.  In hardware, this
2238    is done by reading the MEM status register and checking the protected mode
2239    bit (bit 11).  In simulation, the MP control flip-flop is checked, as the
2240    MEM status register is not global.
2241 */
2242 
mp_is_on(void)2243 t_bool mp_is_on (void)
2244 {
2245 return (mp_control == SET);                             /* return TRUE if MP is on and FALSE if it is off */
2246 }
2247 
2248 
2249 /* Report the INT (W6) jumper position.
2250 
2251    This routine returns TRUE if jumper W6 is not installed and MP is on, and
2252    FALSE otherwise.  It is called when an interrupt is pending but deferred
2253    because the Interrupt Enable flip-flop is clear.  If jumper W6 is installed,
2254    instructions that reference memory will hold off pending but deferred
2255    interrupts until three levels of indirection have been followed.  If W6 is
2256    removed, then deferred interrupts are recognized immediately if MP is on.
2257 */
2258 
mp_reenable_interrupts(void)2259 t_bool mp_reenable_interrupts (void)
2260 {
2261 return mp_unit [0].flags & UNIT_MP_INT && mp_control;   /* return TRUE if interrupts are always recognized */
2262 }
2263 
2264 
2265 /* Trace a memory protect violation.
2266 
2267    This routine is called when CPU operand tracing is enabled and the microcoded
2268    memory protect trap cell instruction is executed.  It reports the reason for
2269    the interrupt (MP, MEM, or PE violation).
2270 
2271    The routine returns TRUE for a MP/MEM violation and FALSE for a PE violation.
2272    This information is used by the instruction microcode.
2273 */
2274 
mp_trace_violation(void)2275 t_bool mp_trace_violation (void)
2276 {
2277 tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT "  entry is for a %s\n",
2278          PR, IR,
2279          (mp_VR & MPVR_PARITY_ERROR
2280            ? "parity error"
2281            : (mp_mevff == SET
2282                ? "dynamic mapping violation"
2283                : "memory protect violation")));
2284 
2285 return (mp_VR & MPVR_PARITY_ERROR) == 0;                /* return TRUE for MP, FALSE for PE */
2286 }
2287 
2288 
2289 
2290 /* Memory Protect local SCP support routines */
2291 
2292 
2293 /* Service the memory protect accessory.
2294 
2295    This routine is scheduled whenever IAK is asserted to the MP interface, and
2296    the MP card itself is not interrupting.  The purpose is to reenable memory
2297    protection if the interrupt trap cell contains a non-HLT I/O instruction.
2298 
2299    In hardware, the MP card responds to a "foreign" IAK (i.e., one acknowledging
2300    another interface's interrupt request) by disabling memory protection while
2301    the trap cell instruction is executed.  If that instruction is a non-HLT IOG
2302    instruction, MP is automatically reenabled before instruction resumes at the
2303    point of interruption.  Otherwise, MP remains off while the interrupt handler
2304    executes.
2305 
2306    In simulation, this is handled during IAK processing by setting "mp_enabled"
2307    to the state of the MP control flip-flop and scheduling the MP event service
2308    routine to enter after the next instruction.  If the trap cell instruction is
2309    an I/O instruction, "cpu_iog" will call "mp_check_io" as part of its
2310    processing.  If that routine is called for a non-HLT instruction, it sets
2311    "mp_reenable" to the value saved in "mp_enabled", i.e., "mp_reenable" will be
2312    SET if MP was enabled when the interrupt occurred (it's initialized to
2313    CLEAR).  When this routine is entered after the trap instruction executes, it
2314    sets "mp_control" to the value of "mp_reenable", which reenables MP if MP was
2315    on.
2316 
2317 
2318    Implementation notes:
2319 
2320     1. The two-level setting (mp_enabled -> mp_reenable -> mp_control) is
2321        necessary to avoid having to clear the reenable flag on every instruction
2322        execution.  Consider if "mp_reenable" is set directly from "mp_control"
2323        in the IAK processor.  The "mp_check_io" routine would clear it if the
2324        instruction is a HLT.  But it would also have to be cleared for all other
2325        non-IOG instructions, which means inserting a "mp_reenable = CLEAR"
2326        statement in all other instruction execution paths.  With the two-level
2327        setting, "mp_reenable" is set from "mp_enabled" only in the "mp_check_io"
2328        routine, and then only if the instruction is not a HLT instruction.  This
2329        saves the delay inherent in clearing "mp_reenable" in the 99.99% of the
2330        cases where an IAK is not being serviced.
2331 */
2332 
mp_service(UNIT * uptr)2333 static t_stat mp_service (UNIT *uptr)
2334 {
2335 mp_control = mp_reenable;                               /* reenable MP if a non-HLT I/O instruction was executed */
2336 
2337 mp_reenable = CLEAR;                                    /* clear the reenable */
2338 mp_enabled  = CLEAR;                                    /*   and enabled-at-interrupt flip-flops */
2339 
2340 if (mp_control == SET) {                                /* if MP was reenabled */
2341     mp_mevff = CLEAR;                                   /*   then clear the Memory Expansion Violation flip-flop */
2342     mp_evrff = SET;                                     /*     and set the Enable Violation Register flip-flop */
2343     }
2344 
2345 return SCPE_OK;
2346 }
2347 
2348 
2349 /* Set the JSB (W5) jumper mode.
2350 
2351    This validation routine is entered with the "value" parameter set to zero or
2352    UNIT_MP_JSB, depending on whether jumper W5 is being installed or removed.
2353    The unit, character, and descriptor pointers are not used.
2354 
2355    The protected lower bound address for JSB instruction protection depends on
2356    the W5 jumper setting.  If W5 is in, then the lower bound is 2, allowing JSBs
2357    to the A and B registers.  If W5 is out, then the lower bound is 0, just as
2358    with JMP.
2359 */
2360 
mp_set_jsb(UNIT * uptr,int32 value,char * cptr,void * desc)2361 static t_stat mp_set_jsb (UNIT *uptr, int32 value, char *cptr, void *desc)
2362 {
2363 if (value == UNIT_MP_JSB)                               /* if jumper W5 is out */
2364     jsb_bound = 0;                                      /*   then the protected lower bound is address 0 */
2365 else                                                    /* otherwise W5 is installed */
2366     jsb_bound = 2;                                      /*   so the protected bound is address 2 */
2367 
2368 return SCPE_OK;
2369 }
2370 
2371 
2372 /* Reset memory protect.
2373 
2374    This routine is called for a RESET, RESET MP, RUN, or BOOT command.  It is
2375    the simulation equivalent of an initial power-on condition (corresponding to
2376    PON, POPIO, and CRS signal assertion) or a front-panel PRESET button press
2377    (corresponding to POPIO and CRS assertion).  SCP delivers a power-on reset to
2378    all devices when the simulator is started.
2379 */
2380 
mp_reset(DEVICE * dptr)2381 static t_stat mp_reset (DEVICE *dptr)
2382 {
2383 io_assert (dptr, ioa_POPIO);                            /* PRESET the device (does not use PON) */
2384 
2385 mp_mem_changed = TRUE;                                  /* set the MP/MEM registers changed flag */
2386 
2387 return SCPE_OK;
2388 }
2389