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