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