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