Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* hp2100_cpu.c: HP 21xx CPU simulator\r |
2 | \r | |
3 | Copyright (c) 1993-2008, Robert M. Supnik\r | |
4 | \r | |
5 | Permission is hereby granted, free of charge, to any person obtaining a\r | |
6 | copy of this software and associated documentation files (the "Software"),\r | |
7 | to deal in the Software without restriction, including without limitation\r | |
8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r | |
9 | and/or sell copies of the Software, and to permit persons to whom the\r | |
10 | Software is furnished to do so, subject to the following conditions:\r | |
11 | \r | |
12 | The above copyright notice and this permission notice shall be included in\r | |
13 | all copies or substantial portions of the Software.\r | |
14 | \r | |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r | |
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r | |
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r | |
18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r | |
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r | |
21 | \r | |
22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r | |
23 | used in advertising or otherwise to promote the sale, use or other dealings\r | |
24 | in this Software without prior written authorization from Robert M Supnik.\r | |
25 | \r | |
26 | CPU 2114C/2115A/2116C/2100A/1000-M/E/F central processing unit\r | |
27 | MP 12581A/12892B memory protect\r | |
28 | DMA0,DMA1 12607B/12578A/12895A direct memory access controller\r | |
29 | DCPC0,DCPC1 12897B dual channel port controller\r | |
30 | \r | |
31 | 30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag\r | |
32 | 24-Apr-08 JDB Fixed single stepping through interrupts\r | |
33 | 20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags\r | |
34 | 03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode\r | |
35 | 26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA\r | |
36 | 15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal,\r | |
37 | mp_mevff clear on interrupt with I/O instruction in trap cell\r | |
38 | 04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM)\r | |
39 | 28-Apr-07 RMS Removed clock initialization\r | |
40 | 02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter\r | |
41 | 11-Jan-07 JDB Added 12578A DMA byte packing\r | |
42 | 28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices\r | |
43 | 26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs\r | |
44 | Fixed improper interrupt servicing in resolve\r | |
45 | 21-Dec-06 JDB Added 21xx loader enable/disable support\r | |
46 | 16-Dec-06 JDB Added 2114 and 2115 CPU options.\r | |
47 | Added support for 12607B (2114) and 12578A (2115/6) DMA\r | |
48 | 01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64)\r | |
49 | SHOW CPU displays 1000-M/E instead of 21MX-M/E\r | |
50 | 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c\r | |
51 | 12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve\r | |
52 | 26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode\r | |
53 | 12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts\r | |
54 | resolve returns NOTE_INDINT to service held-off interrupt\r | |
55 | 16-Aug-06 JDB Added support for future microcode options, future F-Series\r | |
56 | 09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms\r | |
57 | Enhanced CPU option validity checking\r | |
58 | Added DCPC as a synonym for DMA for 21MX simulations\r | |
59 | 26-Dec-05 JDB Improved reporting in dev_conflict\r | |
60 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r | |
61 | 21-Jan-05 JDB Reorganized CPU option flags\r | |
62 | 15-Jan-05 RMS Split out EAU and MAC instructions\r | |
63 | 26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan)\r | |
64 | DMA reset shouldn't clear control words (from Dave Bryan)\r | |
65 | Alternate CTL flop not visible as register (from Dave Bryan)\r | |
66 | Fixed CBS, SBS, TBS to perform virtual reads\r | |
67 | Separated A/B from M[0/1] for DMA IO (from Dave Bryan)\r | |
68 | Fixed bug in JPY (from Dave Bryan)\r | |
69 | 25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E)\r | |
70 | TIMER/EXECUTE/DIAG instructions disabled for 21MX-M\r | |
71 | T-register reflects changes in M-register when halted\r | |
72 | 25-Sep-04 JDB Moved MP into its own device; added MP option jumpers\r | |
73 | Modified DMA to allow disabling\r | |
74 | Modified SET CPU 2100/2116 to truncate memory > 32K\r | |
75 | Added -F switch to SET CPU to force memory truncation\r | |
76 | Fixed S-register behavior on 2116\r | |
77 | Fixed LIx/MIx behavior for DMA on 2116 and 2100\r | |
78 | Fixed LIx/MIx behavior for empty I/O card slots\r | |
79 | Modified WRU to be REG_HRO\r | |
80 | Added BRK and DEL to save console settings\r | |
81 | Fixed use of "unsigned int16" in cpu_reset\r | |
82 | Modified memory size routine to return SCPE_INCOMP if\r | |
83 | memory size truncation declined\r | |
84 | 20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan)\r | |
85 | Back up PC on instruction errors (from Dave Bryan)\r | |
86 | 14-May-04 RMS Fixed bugs and added features from Dave Bryan\r | |
87 | - SBT increments B after store\r | |
88 | - DMS console map must check dms_enb\r | |
89 | - SFS x,C and SFC x,C work\r | |
90 | - MP violation clears automatically on interrupt\r | |
91 | - SFS/SFC 5 is not gated by protection enabled\r | |
92 | - DMS enable does not disable mem prot checks\r | |
93 | - DMS status inconsistent at simulator halt\r | |
94 | - Examine/deposit are checking wrong addresses\r | |
95 | - Physical addresses are 20b not 15b\r | |
96 | - Revised DMS to use memory rather than internal format\r | |
97 | - Added instruction printout to HALT message\r | |
98 | - Added M and T internal registers\r | |
99 | - Added N, S, and U breakpoints\r | |
100 | Revised IBL facility to conform to microcode\r | |
101 | Added DMA EDT I/O pseudo-opcode\r | |
102 | Separated DMA SRQ (service request) from FLG\r | |
103 | 12-Mar-03 RMS Added logical name support\r | |
104 | 02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny)\r | |
105 | 22-Nov-02 RMS Added 21MX IOP support\r | |
106 | 24-Oct-02 RMS Fixed bugs in IOP and extended instructions\r | |
107 | Fixed bugs in memory protection and DMS\r | |
108 | Added clock calibration\r | |
109 | 25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer)\r | |
110 | 26-Jul-02 RMS Restructured extended instructions, added IOP support\r | |
111 | 22-Mar-02 RMS Changed to allocate memory array dynamically\r | |
112 | 11-Mar-02 RMS Cleaned up setjmp/auto variable interaction\r | |
113 | 17-Feb-02 RMS Added DMS support\r | |
114 | Fixed bugs in extended instructions\r | |
115 | 03-Feb-02 RMS Added terminal multiplexor support\r | |
116 | Changed PCQ macro to use unmodified PC\r | |
117 | Fixed flop restore logic (found by Bill McDermith)\r | |
118 | Fixed SZx,SLx,RSS bug (found by Bill McDermith)\r | |
119 | Added floating point support\r | |
120 | 16-Jan-02 RMS Added additional device support\r | |
121 | 07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith)\r | |
122 | 07-Dec-01 RMS Revised to use breakpoint package\r | |
123 | 03-Dec-01 RMS Added extended SET/SHOW support\r | |
124 | 10-Aug-01 RMS Removed register in declarations\r | |
125 | 26-Nov-00 RMS Fixed bug in dual device number routine\r | |
126 | 21-Nov-00 RMS Fixed bug in reset routine\r | |
127 | 15-Oct-00 RMS Added dynamic device number support\r | |
128 | \r | |
129 | References:\r | |
130 | - 2100A Computer Reference Manual (02100-90001, Dec-1971)\r | |
131 | - Model 2100A Computer Installation and Maintenance Manual\r | |
132 | (02100-90002, Aug-1972)\r | |
133 | - HP 1000 M/E/F-Series Computers Technical Reference Handbook\r | |
134 | (5955-0282, Mar-1980)\r | |
135 | - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation\r | |
136 | (92851-90001, Mar-1981)\r | |
137 | - 12607A Direct Memory Access Operating and Service Manual\r | |
138 | (12607-90002, Jan-1970)\r | |
139 | - 12578A/12578A-01 Direct Memory Access Operating and Service Manual\r | |
140 | (12578-9001, Mar-1972)\r | |
141 | - 12892B Memory Protect Installation Manual (12892-90007, Jun-1978)\r | |
142 | \r | |
143 | The register state for the HP 2116 CPU is:\r | |
144 | \r | |
145 | AR<15:0> A register - addressable as location 0\r | |
146 | BR<15:0> B register - addressable as location 1\r | |
147 | PC<14:0> P register (program counter)\r | |
148 | SR<15:0> S register\r | |
149 | MR<14:0> M register - memory address\r | |
150 | TR<15:0> T register - memory data\r | |
151 | E extend flag (carry out)\r | |
152 | O overflow flag\r | |
153 | \r | |
154 | The 2100 adds memory protection logic:\r | |
155 | \r | |
156 | mp_fence<14:0> memory fence register\r | |
157 | mp_viol<15:0> memory protection violation register (F register)\r | |
158 | \r | |
159 | The 21MX adds a pair of index registers and memory expansion logic:\r | |
160 | \r | |
161 | XR<15:0> X register\r | |
162 | YR<15:0> Y register\r | |
163 | dms_sr<15:0> dynamic memory system status register\r | |
164 | dms_vr<15:0> dynamic memory system violation register\r | |
165 | \r | |
166 | The original HP 2116 has four instruction formats: memory reference,\r | |
167 | shift, alter/skip, and I/O. The HP 2100 added extended memory reference\r | |
168 | and extended arithmetic. The HP21MX added extended byte, bit, and word\r | |
169 | instructions as well as extended memory.\r | |
170 | \r | |
171 | The memory reference format is:\r | |
172 | \r | |
173 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
174 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
175 | |in| op |cp| offset | memory reference\r | |
176 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
177 | \r | |
178 | <14:11> mnemonic action\r | |
179 | \r | |
180 | 0010 AND A = A & M[MA]\r | |
181 | 0011 JSB M[MA] = P, P = MA + 1\r | |
182 | 0100 XOR A = A ^ M[MA]\r | |
183 | 0101 JMP P = MA\r | |
184 | 0110 IOR A = A | M[MA]\r | |
185 | 0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0\r | |
186 | 1000 ADA A = A + M[MA]\r | |
187 | 1001 ADB B = B + M[MA]\r | |
188 | 1010 CPA skip if A != M[MA]\r | |
189 | 1011 CPB skip if B != M[MA]\r | |
190 | 1100 LDA A = M[MA]\r | |
191 | 1101 LDB B = M[MA]\r | |
192 | 1110 STA M[MA] = A\r | |
193 | 1111 STB M[MA] = B\r | |
194 | \r | |
195 | <15,10> mode action\r | |
196 | \r | |
197 | 0,0 page zero direct MA = IR<9:0>\r | |
198 | 0,1 current page direct MA = PC<14:0>'IR,9:0>\r | |
199 | 1,0 page zero indirect MA = M[IR<9:0>]\r | |
200 | 1,1 current page indirect MA = M[PC<14:10>'IR<9:0>]\r | |
201 | \r | |
202 | Memory reference instructions can access an address space of 32K words.\r | |
203 | An instruction can directly reference the first 1024 words of memory\r | |
204 | (called page zero), as well as 1024 words of the current page; it can\r | |
205 | indirectly access all 32K.\r | |
206 | \r | |
207 | The shift format is:\r | |
208 | \r | |
209 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
210 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
211 | | 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift\r | |
212 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
213 | | | \---+---/ | | | \---+---/\r | |
214 | | | | | | | |\r | |
215 | | | | | | | +---- shift 2 opcode\r | |
216 | | | | | | +---------- skip if low bit == 0\r | |
217 | | | | | +------------- shift 2 enable\r | |
218 | | | | +---------------- clear Extend\r | |
219 | | | +---------------------- shift 1 opcode\r | |
220 | | +---------------------------- shift 1 enable\r | |
221 | +---------------------------------- A/B select\r | |
222 | \r | |
223 | The alter/skip format is:\r | |
224 | \r | |
225 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
226 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
227 | | 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip\r | |
228 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
229 | | \-+-/ \-+-/ | | | | | |\r | |
230 | | | | | | | | | +- reverse skip sense\r | |
231 | | | | | | | | +---- skip if register == 0\r | |
232 | | | | | | | +------- increment register\r | |
233 | | | | | | +---------- skip if low bit == 0\r | |
234 | | | | | +------------- skip if sign bit == 0\r | |
235 | | | | +---------------- skip if Extend == 0\r | |
236 | | | +--------------------- clr/com/set Extend\r | |
237 | | +--------------------------- clr/com/set register\r | |
238 | +---------------------------------- A/B select\r | |
239 | \r | |
240 | The I/O transfer format is:\r | |
241 | \r | |
242 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
243 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
244 | | 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer\r | |
245 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
246 | | | \---+---/\-------+-------/\r | |
247 | | | | |\r | |
248 | | | | +--------- device select\r | |
249 | | | +---------------------- opcode\r | |
250 | | +---------------------------- hold/clear flag\r | |
251 | +---------------------------------- A/B select\r | |
252 | \r | |
253 | The IO transfer instruction controls the specified device.\r | |
254 | Depending on the opcode, the instruction may set or clear\r | |
255 | the device flag, start or stop I/O, or read or write data.\r | |
256 | \r | |
257 | The 2100 added an extended memory reference instruction;\r | |
258 | the 21MX added extended arithmetic, operate, byte, word,\r | |
259 | and bit instructions. Note that the HP 21xx is, despite\r | |
260 | the right-to-left bit numbering, a big endian system.\r | |
261 | Bits <15:8> are byte 0, and bits <7:0> are byte 1.\r | |
262 | \r | |
263 | \r | |
264 | The extended memory reference format (HP 2100) is:\r | |
265 | \r | |
266 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
267 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
268 | | 1| 0 0 0|op| 0| opcode | extended mem ref\r | |
269 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
270 | |in| operand address |\r | |
271 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
272 | \r | |
273 | The extended arithmetic format (HP 2100) is:\r | |
274 | \r | |
275 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
276 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
277 | | 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic\r | |
278 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
279 | \r | |
280 | The extended operate format (HP 21MX) is:\r | |
281 | \r | |
282 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
283 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
284 | | 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate\r | |
285 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
286 | \r | |
287 | The extended byte and word format (HP 21MX) is:\r | |
288 | \r | |
289 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
290 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
291 | | 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word\r | |
292 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
293 | |in| operand address |\r | |
294 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
295 | | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|\r | |
296 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
297 | \r | |
298 | The extended bit operate format (HP 21MX) is:\r | |
299 | \r | |
300 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0\r | |
301 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
302 | | 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate\r | |
303 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
304 | |in| operand address |\r | |
305 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
306 | |in| operand address |\r | |
307 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
308 | \r | |
309 | This routine is the instruction decode routine for the HP 2100.\r | |
310 | It is called from the simulator control program to execute\r | |
311 | instructions in simulated memory, starting at the simulated PC.\r | |
312 | It runs until 'reason' is set non-zero.\r | |
313 | \r | |
314 | General notes:\r | |
315 | \r | |
316 | 1. Reasons to stop. The simulator can be stopped by:\r | |
317 | \r | |
318 | HALT instruction\r | |
319 | breakpoint encountered\r | |
320 | infinite indirection loop\r | |
321 | unimplemented instruction and stop_inst flag set\r | |
322 | unknown I/O device and stop_dev flag set\r | |
323 | I/O error in I/O simulator\r | |
324 | \r | |
325 | 2. Interrupts. I/O devices are modelled as five parallel arrays:\r | |
326 | \r | |
327 | device commands as bit array dev_cmd[2][31..0]\r | |
328 | device flags as bit array dev_flg[2][31..0]\r | |
329 | device flag buffers as bit array dev_fbf[2][31..0]\r | |
330 | device controls as bit array dev_ctl[2][31..0]\r | |
331 | device service requests as bit array dev_srq[3][31..0]\r | |
332 | \r | |
333 | The HP 2100 interrupt structure is based on flag, flag buffer,\r | |
334 | and control. If a device flag is set, the flag buffer is set,\r | |
335 | the control bit is set, and the device is the highest priority\r | |
336 | on the interrupt chain, it requests an interrupt. When the\r | |
337 | interrupt is acknowledged, the flag buffer is cleared, preventing\r | |
338 | further interrupt requests from that device. The combination of\r | |
339 | flag and control set blocks interrupts from lower priority devices.\r | |
340 | \r | |
341 | Command plays no direct role in interrupts. The command flop\r | |
342 | tells whether a device is active. It is set by STC and cleared\r | |
343 | by CLC; it is also cleared when the device flag is set. Simple\r | |
344 | devices don't need to track command separately from control.\r | |
345 | \r | |
346 | Service requests are used to trigger the DMA service logic.\r | |
347 | \r | |
348 | 3. Non-existent memory. On the HP 2100, reads to non-existent memory\r | |
349 | return zero, and writes are ignored. In the simulator, the\r | |
350 | largest possible memory is instantiated and initialized to zero.\r | |
351 | Thus, only writes need be checked against memory size.\r | |
352 | \r | |
353 | On the 21xx machines, doing SET CPU LOADERDISABLE decreases available\r | |
354 | memory size by 64 words.\r | |
355 | \r | |
356 | 4. Adding I/O devices. These modules must be modified:\r | |
357 | \r | |
358 | hp2100_defs.h add interrupt request definition\r | |
359 | hp2100_sys.c add sim_devices table entry\r | |
360 | \r | |
361 | 5. Instruction interruptibility. The simulator is fast enough, compared\r | |
362 | to the run-time of the longest instructions, for interruptibility not\r | |
363 | to matter. But the HP diagnostics explicitly test interruptibility in\r | |
364 | EIS and DMS instructions, and long indirect address chains. Accordingly,\r | |
365 | the simulator does "just enough" to pass these tests. In particular, if\r | |
366 | an interrupt is pending but deferred at the beginning of an interruptible\r | |
367 | instruction, the interrupt is taken at the appropriate point; but there\r | |
368 | is no testing for new interrupts during execution (that is, the event\r | |
369 | timer is not called).\r | |
370 | \r | |
371 | 6. Interrupt deferral. At instruction fetch time, a pending interrupt\r | |
372 | request will be deferred if the previous instruction was a JMP indirect,\r | |
373 | JSB indirect, STC, CLC, STF, CLF, or was executing from an interrupt trap\r | |
374 | cell. In addition, the following instructions will cause deferral on the\r | |
375 | 1000 series: SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, and UJS.\r | |
376 | \r | |
377 | On the HP 1000, the request is always deferred until after the current\r | |
378 | instruction completes. On the 21xx, the request is deferred unless the\r | |
379 | current instruction is an MRG instruction other than JMP or JMP,I or\r | |
380 | JSB,I. Note that for the 21xx, SFS and SFC are not included in the\r | |
381 | deferral criteria.\r | |
382 | */\r | |
383 | \r | |
384 | #include "hp2100_defs.h"\r | |
385 | #include <setjmp.h>\r | |
386 | #include "hp2100_cpu.h"\r | |
387 | \r | |
388 | /* Memory protect constants */\r | |
389 | \r | |
390 | #define UNIT_V_MP_JSB (UNIT_V_UF + 0) /* MP jumper W5 */\r | |
391 | #define UNIT_V_MP_INT (UNIT_V_UF + 1) /* MP jumper W6 */\r | |
392 | #define UNIT_V_MP_SEL1 (UNIT_V_UF + 2) /* MP jumper W7 */\r | |
393 | #define UNIT_MP_JSB (1 << UNIT_V_MP_JSB) /* 1 = W5 is out */\r | |
394 | #define UNIT_MP_INT (1 << UNIT_V_MP_INT) /* 1 = W6 is out */\r | |
395 | #define UNIT_MP_SEL1 (1 << UNIT_V_MP_SEL1) /* 1 = W7 is out */\r | |
396 | \r | |
397 | #define ABORT(val) longjmp (save_env, (val))\r | |
398 | \r | |
399 | #define DMAR0 1\r | |
400 | #define DMAR1 2\r | |
401 | \r | |
402 | #define ALL_BKPTS (SWMASK('E')|SWMASK('N')|SWMASK('S')|SWMASK('U'))\r | |
403 | #define ALL_MAPMODES (SWMASK('S')|SWMASK('U')|SWMASK('P')|SWMASK('Q'))\r | |
404 | \r | |
405 | uint16 *M = NULL; /* memory */\r | |
406 | uint32 saved_AR = 0; /* A register */\r | |
407 | uint32 saved_BR = 0; /* B register */\r | |
408 | uint16 ABREG[2]; /* during execution */\r | |
409 | uint32 PC = 0; /* P register */\r | |
410 | uint32 SR = 0; /* S register */\r | |
411 | uint32 MR = 0; /* M register */\r | |
412 | uint32 saved_MR = 0; /* between executions */\r | |
413 | uint32 TR = 0; /* T register */\r | |
414 | uint32 XR = 0; /* X register */\r | |
415 | uint32 YR = 0; /* Y register */\r | |
416 | uint32 E = 0; /* E register */\r | |
417 | uint32 O = 0; /* O register */\r | |
418 | uint32 dev_cmd[2] = { 0 }; /* device command */\r | |
419 | uint32 dev_ctl[2] = { 0 }; /* device control */\r | |
420 | uint32 dev_flg[2] = { 0 }; /* device flags */\r | |
421 | uint32 dev_fbf[2] = { 0 }; /* device flag bufs */\r | |
422 | uint32 dev_srq[2] = { 0 }; /* device svc reqs */\r | |
423 | struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */\r | |
424 | uint32 ion = 0; /* interrupt enable */\r | |
425 | uint32 ion_defer = 0; /* interrupt defer */\r | |
426 | uint32 intaddr = 0; /* interrupt addr */\r | |
427 | uint32 mp_fence = 0; /* mem prot fence */\r | |
428 | uint32 mp_viol = 0; /* mem prot viol reg */\r | |
429 | uint32 mp_mevff = 0; /* mem exp (dms) viol */\r | |
430 | uint32 mp_evrff = 1; /* update mp_viol */\r | |
431 | uint32 err_PC = 0; /* error PC */\r | |
432 | uint32 dms_enb = 0; /* dms enable */\r | |
433 | uint32 dms_ump = 0; /* dms user map */\r | |
434 | uint32 dms_sr = 0; /* dms status reg */\r | |
435 | uint32 dms_vr = 0; /* dms violation reg */\r | |
436 | uint16 dms_map[MAP_NUM * MAP_LNT] = { 0 }; /* dms maps */\r | |
437 | uint32 iop_sp = 0; /* iop stack */\r | |
438 | uint32 ind_max = 16; /* iadr nest limit */\r | |
439 | uint32 stop_inst = 1; /* stop on ill inst */\r | |
440 | uint32 stop_dev = 0; /* stop on ill dev */\r | |
441 | uint32 fwanxm = 0; /* first word addr of nx mem */\r | |
442 | uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r | |
443 | uint32 pcq_p = 0; /* PC queue ptr */\r | |
444 | REG *pcq_r = NULL; /* PC queue reg ptr */\r | |
445 | jmp_buf save_env; /* abort handler */\r | |
446 | \r | |
447 | /* Table of CPU features by model.\r | |
448 | \r | |
449 | Fields:\r | |
450 | - typ: standard features plus typically configured options.\r | |
451 | - opt: complete list of optional features.\r | |
452 | - maxmem: maximum configurable memory in 16-bit words.\r | |
453 | \r | |
454 | Features in the "typical" list are enabled when the CPU model is selected.\r | |
455 | If a feature appears in the "typical" list but NOT in the "optional" list,\r | |
456 | then it is standard equipment and cannot be disabled. If a feature appears\r | |
457 | in the "optional" list, then it may be enabled or disabled as desired by the\r | |
458 | user.\r | |
459 | */\r | |
460 | \r | |
461 | struct FEATURE_TABLE { /* CPU model feature table: */\r | |
462 | uint32 typ; /* - typical features */\r | |
463 | uint32 opt; /* - optional features */\r | |
464 | uint32 maxmem; /* - maximum memory */\r | |
465 | };\r | |
466 | \r | |
467 | static struct FEATURE_TABLE cpu_features[] = { /* features in UNIT_xxxx order*/\r | |
468 | { UNIT_DMA | UNIT_MP, /* UNIT_2116 */\r | |
469 | UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU,\r | |
470 | 32768 },\r | |
471 | { UNIT_DMA, /* UNIT_2115 */\r | |
472 | UNIT_PFAIL | UNIT_DMA | UNIT_EAU,\r | |
473 | 8192 },\r | |
474 | { UNIT_DMA, /* UNIT_2114 */\r | |
475 | UNIT_PFAIL | UNIT_DMA,\r | |
476 | 16384 },\r | |
477 | { 0, 0, 0 },\r | |
478 | { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* UNIT_2100 */\r | |
479 | UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP,\r | |
480 | 32768 },\r | |
481 | { 0, 0, 0 },\r | |
482 | { 0, 0, 0 },\r | |
483 | { 0, 0, 0 },\r | |
484 | { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_M */\r | |
485 | UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |\r | |
486 | UNIT_IOP | UNIT_FFP | UNIT_DS,\r | |
487 | 1048576 },\r | |
488 | { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_E */\r | |
489 | UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |\r | |
490 | UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA,\r | |
491 | 1048576 },\r | |
492 | { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* UNIT_1000_F */\r | |
493 | UNIT_FFP | UNIT_DBI | UNIT_DMS,\r | |
494 | UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_VIS |\r | |
495 | UNIT_IOP | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA,\r | |
496 | 1048576 }\r | |
497 | };\r | |
498 | \r | |
499 | extern int32 sim_interval;\r | |
500 | extern int32 sim_int_char;\r | |
501 | extern int32 sim_brk_char;\r | |
502 | extern int32 sim_del_char;\r | |
503 | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r | |
504 | extern FILE *sim_log;\r | |
505 | extern DEVICE *sim_devices[];\r | |
506 | extern int32 sim_switches;\r | |
507 | extern char halt_msg[];\r | |
508 | \r | |
509 | t_stat Ea (uint32 IR, uint32 *addr, uint32 irq);\r | |
510 | uint16 ReadIO (uint32 addr, uint32 map);\r | |
511 | uint16 ReadPW (uint32 addr);\r | |
512 | uint16 ReadTAB (uint32 addr);\r | |
513 | void WriteIO (uint32 addr, uint32 dat, uint32 map);\r | |
514 | void WritePW (uint32 addr, uint32 dat);\r | |
515 | uint32 dms (uint32 va, uint32 map, uint32 prot);\r | |
516 | uint32 dms_io (uint32 va, uint32 map);\r | |
517 | uint32 shift (uint32 inval, uint32 flag, uint32 oper);\r | |
518 | void dma_cycle (uint32 chan, uint32 map);\r | |
519 | uint32 calc_dma (void);\r | |
520 | uint32 calc_int (void);\r | |
521 | uint32 calc_defer (void);\r | |
522 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r | |
523 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r | |
524 | t_stat cpu_reset (DEVICE *dptr);\r | |
525 | t_stat cpu_boot (int32 unitno, DEVICE *dptr);\r | |
526 | t_stat mp_reset (DEVICE *dptr);\r | |
527 | t_stat dma0_reset (DEVICE *dptr);\r | |
528 | t_stat dma1_reset (DEVICE *dptr);\r | |
529 | t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc);\r | |
530 | t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc);\r | |
531 | t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
532 | t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc);\r | |
533 | t_stat cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc);\r | |
534 | t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc);\r | |
535 | t_bool dev_conflict (void);\r | |
536 | void hp_post_cmd (t_bool from_scp);\r | |
537 | \r | |
538 | extern t_stat cpu_eau (uint32 IR, uint32 intrq);\r | |
539 | extern t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap);\r | |
540 | extern t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap);\r | |
541 | extern void (*sim_vm_post) (t_bool from_scp);\r | |
542 | \r | |
543 | /* CPU data structures\r | |
544 | \r | |
545 | cpu_dev CPU device descriptor\r | |
546 | cpu_unit CPU unit descriptor\r | |
547 | cpu_reg CPU register list\r | |
548 | cpu_mod CPU modifiers list\r | |
549 | cpu_deb CPU debug flags\r | |
550 | */\r | |
551 | \r | |
552 | UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 0) };\r | |
553 | \r | |
554 | REG cpu_reg[] = {\r | |
555 | { ORDATA (P, PC, 15) },\r | |
556 | { ORDATA (A, saved_AR, 16) },\r | |
557 | { ORDATA (B, saved_BR, 16) },\r | |
558 | { ORDATA (M, MR, 15) },\r | |
559 | { ORDATA (T, TR, 16), REG_RO },\r | |
560 | { ORDATA (X, XR, 16) },\r | |
561 | { ORDATA (Y, YR, 16) },\r | |
562 | { ORDATA (S, SR, 16) },\r | |
563 | { FLDATA (E, E, 0) },\r | |
564 | { FLDATA (O, O, 0) },\r | |
565 | { FLDATA (ION, ion, 0) },\r | |
566 | { FLDATA (ION_DEFER, ion_defer, 0) },\r | |
567 | { ORDATA (CIR, intaddr, 6) },\r | |
568 | { FLDATA (DMSENB, dms_enb, 0) },\r | |
569 | { FLDATA (DMSCUR, dms_ump, VA_N_PAG) },\r | |
570 | { ORDATA (DMSSR, dms_sr, 16) },\r | |
571 | { ORDATA (DMSVR, dms_vr, 16) },\r | |
572 | { BRDATA (DMSMAP, dms_map, 8, 16, MAP_NUM * MAP_LNT) },\r | |
573 | { ORDATA (IOPSP, iop_sp, 16) },\r | |
574 | { FLDATA (STOP_INST, stop_inst, 0) },\r | |
575 | { FLDATA (STOP_DEV, stop_dev, 1) },\r | |
576 | { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },\r | |
577 | { ORDATA (FWANXM, fwanxm, 32), REG_HRO },\r | |
578 | { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC },\r | |
579 | { ORDATA (PCQP, pcq_p, 6), REG_HRO },\r | |
580 | { ORDATA (WRU, sim_int_char, 8), REG_HRO },\r | |
581 | { ORDATA (BRK, sim_brk_char, 8), REG_HRO },\r | |
582 | { ORDATA (DEL, sim_del_char, 8), REG_HRO },\r | |
583 | { ORDATA (HCMD, dev_cmd[0], 32), REG_HRO },\r | |
584 | { ORDATA (LCMD, dev_cmd[1], 32), REG_HRO },\r | |
585 | { ORDATA (HCTL, dev_ctl[0], 32), REG_HRO },\r | |
586 | { ORDATA (LCTL, dev_ctl[1], 32), REG_HRO },\r | |
587 | { ORDATA (HFLG, dev_flg[0], 32), REG_HRO },\r | |
588 | { ORDATA (LFLG, dev_flg[1], 32), REG_HRO },\r | |
589 | { ORDATA (HFBF, dev_fbf[0], 32), REG_HRO },\r | |
590 | { ORDATA (LFBF, dev_fbf[1], 32), REG_HRO },\r | |
591 | { ORDATA (HSRQ, dev_srq[0], 32), REG_HRO },\r | |
592 | { ORDATA (LSRQ, dev_srq[1], 32), REG_HRO },\r | |
593 | { NULL }\r | |
594 | };\r | |
595 | \r | |
596 | /* CPU modifier table.\r | |
597 | \r | |
598 | The 21MX monikers are deprecated in favor of the 1000 designations. See the\r | |
599 | "HP 1000 Series Naming History" on the back inside cover of the Technical\r | |
600 | Reference Handbook. */\r | |
601 | \r | |
602 | MTAB cpu_mod[] = {\r | |
603 | { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, "2116" },\r | |
604 | { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, "2115" },\r | |
605 | { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, "2114" },\r | |
606 | { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, "2100" },\r | |
607 | { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, "1000-E" },\r | |
608 | { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, "1000-E" },\r | |
609 | { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, "1000-M" },\r | |
610 | { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, "1000-M" },\r | |
611 | \r | |
612 | #if defined (HAVE_INT64)\r | |
613 | { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, "1000-F" },\r | |
614 | #endif\r | |
615 | \r | |
616 | { MTAB_XTD | MTAB_VDV, 1, NULL, "LOADERENABLE", &cpu_set_ldr, NULL, NULL },\r | |
617 | { MTAB_XTD | MTAB_VDV, 0, NULL, "LOADERDISABLE", &cpu_set_ldr, NULL, NULL },\r | |
618 | \r | |
619 | { UNIT_EAU, UNIT_EAU, "EAU", "EAU", &cpu_set_opt, NULL, NULL },\r | |
620 | { UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL },\r | |
621 | { MTAB_XTD | MTAB_VDV, UNIT_EAU, NULL, "NOEAU", &cpu_clr_opt, NULL, NULL },\r | |
622 | \r | |
623 | { UNIT_FP, UNIT_FP, "FP", "FP", &cpu_set_opt, NULL, NULL },\r | |
624 | { UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL },\r | |
625 | { MTAB_XTD | MTAB_VDV, UNIT_FP, NULL, "NOFP", &cpu_clr_opt, NULL, NULL },\r | |
626 | \r | |
627 | { UNIT_IOP, UNIT_IOP, "IOP", "IOP", &cpu_set_opt, NULL, NULL },\r | |
628 | { UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL },\r | |
629 | { MTAB_XTD | MTAB_VDV, UNIT_IOP, NULL, "NOIOP", &cpu_clr_opt, NULL, NULL },\r | |
630 | \r | |
631 | { UNIT_DMS, UNIT_DMS, "DMS", "DMS", &cpu_set_opt, NULL, NULL },\r | |
632 | { UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL },\r | |
633 | { MTAB_XTD | MTAB_VDV, UNIT_DMS, NULL, "NODMS", &cpu_clr_opt, NULL, NULL },\r | |
634 | \r | |
635 | { UNIT_FFP, UNIT_FFP, "FFP", "FFP", &cpu_set_opt, NULL, NULL },\r | |
636 | { UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL },\r | |
637 | { MTAB_XTD | MTAB_VDV, UNIT_FFP, NULL, "NOFFP", &cpu_clr_opt, NULL, NULL },\r | |
638 | \r | |
639 | { UNIT_DBI, UNIT_DBI, "DBI", "DBI", &cpu_set_opt, NULL, NULL },\r | |
640 | { UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL },\r | |
641 | { MTAB_XTD | MTAB_VDV, UNIT_DBI, NULL, "NODBI", &cpu_clr_opt, NULL, NULL },\r | |
642 | \r | |
643 | { UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &cpu_set_opt, NULL, NULL },\r | |
644 | { MTAB_XTD | MTAB_VDV, UNIT_EMA, NULL, "NOEMA", &cpu_clr_opt, NULL, NULL },\r | |
645 | \r | |
646 | { UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &cpu_set_opt, NULL, NULL },\r | |
647 | { MTAB_XTD | MTAB_VDV, UNIT_VMAOS, NULL, "NOVMA", &cpu_clr_opt, NULL, NULL },\r | |
648 | \r | |
649 | { UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &cpu_set_opt, NULL, NULL },\r | |
650 | \r | |
651 | #if defined (HAVE_INT64)\r | |
652 | { UNIT_VIS, UNIT_VIS, "VIS", "VIS", &cpu_set_opt, NULL, NULL },\r | |
653 | { UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL },\r | |
654 | { MTAB_XTD | MTAB_VDV, UNIT_VIS, NULL, "NOVIS", &cpu_clr_opt, NULL, NULL },\r | |
655 | \r | |
656 | { UNIT_SIGNAL, UNIT_SIGNAL,"SIGNAL", "SIGNAL", &cpu_set_opt, NULL, NULL },\r | |
657 | { UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL },\r | |
658 | { MTAB_XTD | MTAB_VDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &cpu_clr_opt, NULL, NULL },\r | |
659 | #endif\r | |
660 | \r | |
661 | /* Future microcode support.\r | |
662 | { UNIT_DS, UNIT_DS, "DS", "DS", &cpu_set_opt, NULL, NULL },\r | |
663 | { UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL },\r | |
664 | { MTAB_XTD | MTAB_VDV, UNIT_DS, NULL, "NODS", &cpu_clr_opt, NULL, NULL },\r | |
665 | */\r | |
666 | \r | |
667 | { MTAB_XTD | MTAB_VDV, 4096, NULL, "4K", &cpu_set_size, NULL, NULL },\r | |
668 | { MTAB_XTD | MTAB_VDV, 8192, NULL, "8K", &cpu_set_size, NULL, NULL },\r | |
669 | { MTAB_XTD | MTAB_VDV, 12288, NULL, "12K", &cpu_set_size, NULL, NULL },\r | |
670 | { MTAB_XTD | MTAB_VDV, 16384, NULL, "16K", &cpu_set_size, NULL, NULL },\r | |
671 | { MTAB_XTD | MTAB_VDV, 24576, NULL, "24K", &cpu_set_size, NULL, NULL },\r | |
672 | { MTAB_XTD | MTAB_VDV, 32768, NULL, "32K", &cpu_set_size, NULL, NULL },\r | |
673 | { MTAB_XTD | MTAB_VDV, 65536, NULL, "64K", &cpu_set_size, NULL, NULL },\r | |
674 | { MTAB_XTD | MTAB_VDV, 131072, NULL, "128K", &cpu_set_size, NULL, NULL },\r | |
675 | { MTAB_XTD | MTAB_VDV, 262144, NULL, "256K", &cpu_set_size, NULL, NULL },\r | |
676 | { MTAB_XTD | MTAB_VDV, 524288, NULL, "512K", &cpu_set_size, NULL, NULL },\r | |
677 | { MTAB_XTD | MTAB_VDV, 1048576, NULL, "1024K", &cpu_set_size, NULL, NULL },\r | |
678 | { 0 }\r | |
679 | };\r | |
680 | \r | |
681 | DEBTAB cpu_deb[] = {\r | |
682 | { "OS", DEB_OS },\r | |
683 | { "OSTBG", DEB_OSTBG },\r | |
684 | { "VMA", DEB_VMA },\r | |
685 | { "EMA", DEB_EMA },\r | |
686 | { "VIS", DEB_VIS },\r | |
687 | { "SIG", DEB_SIG },\r | |
688 | { NULL, 0 }\r | |
689 | };\r | |
690 | \r | |
691 | DEVICE cpu_dev = {\r | |
692 | "CPU", &cpu_unit, cpu_reg, cpu_mod,\r | |
693 | 1, 8, PA_N_SIZE, 1, 8, 16,\r | |
694 | &cpu_ex, &cpu_dep, &cpu_reset,\r | |
695 | &cpu_boot, NULL, NULL,\r | |
696 | NULL, DEV_DEBUG, \r | |
697 | 0, cpu_deb, NULL, NULL\r | |
698 | };\r | |
699 | \r | |
700 | /* Memory protect data structures\r | |
701 | \r | |
702 | mp_dev MP device descriptor\r | |
703 | mp_unit MP unit descriptor\r | |
704 | mp_reg MP register list\r | |
705 | mp_mod MP modifiers list\r | |
706 | */\r | |
707 | \r | |
708 | UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; /* default is JSB in, INT in, SEL1 out */\r | |
709 | \r | |
710 | REG mp_reg[] = {\r | |
711 | { FLDATA (CTL, dev_ctl[PRO/32], INT_V (PRO)) },\r | |
712 | { FLDATA (FLG, dev_flg[PRO/32], INT_V (PRO)) },\r | |
713 | { FLDATA (FBF, dev_fbf[PRO/32], INT_V (PRO)) },\r | |
714 | { ORDATA (FR, mp_fence, 15) },\r | |
715 | { ORDATA (VR, mp_viol, 16) },\r | |
716 | { FLDATA (MEV, mp_mevff, 0) },\r | |
717 | { FLDATA (EVR, mp_evrff, 0) },\r | |
718 | { NULL }\r | |
719 | };\r | |
720 | \r | |
721 | MTAB mp_mod[] = {\r | |
722 | { UNIT_MP_JSB, UNIT_MP_JSB, "JSB (W5) out", "JSBOUT", NULL },\r | |
723 | { UNIT_MP_JSB, 0, "JSB (W5) in", "JSBIN", NULL },\r | |
724 | { UNIT_MP_INT, UNIT_MP_INT, "INT (W6) out", "INTOUT", NULL },\r | |
725 | { UNIT_MP_INT, 0, "INT (W6) in", "INTIN", NULL },\r | |
726 | { UNIT_MP_SEL1, UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT", NULL },\r | |
727 | { UNIT_MP_SEL1, 0, "SEL1 (W7) in", "SEL1IN", NULL },\r | |
728 | { 0 }\r | |
729 | };\r | |
730 | \r | |
731 | DEVICE mp_dev = {\r | |
732 | "MP", &mp_unit, mp_reg, mp_mod,\r | |
733 | 1, 8, 1, 1, 8, 16,\r | |
734 | NULL, NULL, &mp_reset,\r | |
735 | NULL, NULL, NULL,\r | |
736 | NULL, DEV_DISABLE | DEV_DIS\r | |
737 | };\r | |
738 | \r | |
739 | /* DMA controller data structures\r | |
740 | \r | |
741 | dmax_dev DMAx device descriptor\r | |
742 | dmax_reg DMAx register list\r | |
743 | */\r | |
744 | \r | |
745 | UNIT dma0_unit = { UDATA (NULL, 0, 0) };\r | |
746 | \r | |
747 | REG dma0_reg[] = {\r | |
748 | { FLDATA (CMD, dev_cmd[DMA0/32], INT_V (DMA0)) },\r | |
749 | { FLDATA (CTL, dev_ctl[DMA0/32], INT_V (DMA0)) },\r | |
750 | { FLDATA (FLG, dev_flg[DMA0/32], INT_V (DMA0)) },\r | |
751 | { FLDATA (FBF, dev_fbf[DMA0/32], INT_V (DMA0)) },\r | |
752 | { FLDATA (CTLALT, dev_ctl[DMALT0/32], INT_V (DMALT0)) },\r | |
753 | { ORDATA (CW1, dmac[0].cw1, 16) },\r | |
754 | { ORDATA (CW2, dmac[0].cw2, 16) },\r | |
755 | { ORDATA (CW3, dmac[0].cw3, 16) },\r | |
756 | { DRDATA (LATENCY, dmac[0].latency, 8) },\r | |
757 | { FLDATA (BYTE, dmac[0].packer, 31) },\r | |
758 | { ORDATA (PACKER, dmac[0].packer, 8) },\r | |
759 | { NULL }\r | |
760 | };\r | |
761 | \r | |
762 | DEVICE dma0_dev = {\r | |
763 | "DMA0", &dma0_unit, dma0_reg, NULL,\r | |
764 | 1, 8, 1, 1, 8, 16,\r | |
765 | NULL, NULL, &dma0_reset,\r | |
766 | NULL, NULL, NULL,\r | |
767 | NULL, DEV_DISABLE\r | |
768 | };\r | |
769 | \r | |
770 | UNIT dma1_unit = { UDATA (NULL, 0, 0) };\r | |
771 | \r | |
772 | REG dma1_reg[] = {\r | |
773 | { FLDATA (CMD, dev_cmd[DMA1/32], INT_V (DMA1)) },\r | |
774 | { FLDATA (CTL, dev_ctl[DMA1/32], INT_V (DMA1)) },\r | |
775 | { FLDATA (FLG, dev_flg[DMA1/32], INT_V (DMA1)) },\r | |
776 | { FLDATA (FBF, dev_fbf[DMA1/32], INT_V (DMA1)) },\r | |
777 | { FLDATA (CTLALT, dev_ctl[DMALT1/32], INT_V (DMALT1)) },\r | |
778 | { ORDATA (CW1, dmac[1].cw1, 16) },\r | |
779 | { ORDATA (CW2, dmac[1].cw2, 16) },\r | |
780 | { ORDATA (CW3, dmac[1].cw3, 16) },\r | |
781 | { DRDATA (LATENCY, dmac[1].latency, 8) },\r | |
782 | { FLDATA (BYTE, dmac[1].packer, 31) },\r | |
783 | { ORDATA (PACKER, dmac[1].packer, 8) },\r | |
784 | { NULL }\r | |
785 | };\r | |
786 | \r | |
787 | DEVICE dma1_dev = {\r | |
788 | "DMA1", &dma1_unit, dma1_reg, NULL,\r | |
789 | 1, 8, 1, 1, 8, 16,\r | |
790 | NULL, NULL, &dma1_reset,\r | |
791 | NULL, NULL, NULL,\r | |
792 | NULL, DEV_DISABLE\r | |
793 | };\r | |
794 | \r | |
795 | /* Interrupt defer table (1000 version) */\r | |
796 | \r | |
797 | static int32 defer_tab[] = { 0, 1, 1, 1, 0, 0, 0, 1 };\r | |
798 | \r | |
799 | /* Device dispatch table */\r | |
800 | \r | |
801 | uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 outdat);\r | |
802 | int32 cpuio (int32 op, int32 IR, int32 outdat);\r | |
803 | int32 ovfio (int32 op, int32 IR, int32 outdat);\r | |
804 | int32 pwrio (int32 op, int32 IR, int32 outdat);\r | |
805 | int32 proio (int32 op, int32 IR, int32 outdat);\r | |
806 | int32 dmsio (int32 op, int32 IR, int32 outdat);\r | |
807 | int32 dmpio (int32 op, int32 IR, int32 outdat);\r | |
808 | int32 nulio (int32 op, int32 IR, int32 outdat);\r | |
809 | \r | |
810 | int32 (*dtab[64])() = {\r | |
811 | &cpuio, &ovfio, &dmsio, &dmsio, &pwrio, &proio, &dmpio, &dmpio,\r | |
812 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
813 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
814 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
815 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
816 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
817 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,\r | |
818 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL\r | |
819 | };\r | |
820 | \r | |
821 | t_stat sim_instr (void)\r | |
822 | {\r | |
823 | uint32 intrq, dmarq; /* set after setjmp */\r | |
824 | uint32 iotrap = 0; /* set after setjmp */\r | |
825 | t_stat reason; /* set after setjmp */\r | |
826 | int32 i, dev; /* temp */\r | |
827 | DEVICE *dptr; /* temp */\r | |
828 | DIB *dibp; /* temp */\r | |
829 | int abortval;\r | |
830 | \r | |
831 | /* Restore register state */\r | |
832 | \r | |
833 | if (dev_conflict ()) return SCPE_STOP; /* check consistency */\r | |
834 | AR = saved_AR & DMASK; /* restore reg */\r | |
835 | BR = saved_BR & DMASK;\r | |
836 | err_PC = PC = PC & VAMASK; /* load local PC */\r | |
837 | reason = 0;\r | |
838 | \r | |
839 | /* Restore I/O state */\r | |
840 | \r | |
841 | if (mp_dev.flags & DEV_DIS) dtab[PRO] = NULL;\r | |
842 | else dtab[PRO] = &proio; /* set up MP dispatch */\r | |
843 | if (dma0_dev.flags & DEV_DIS) dtab[DMA0] = dtab[DMALT0] = NULL;\r | |
844 | else {\r | |
845 | dtab[DMA0] = &dmpio; /* set up DMA0 dispatch */\r | |
846 | dtab[DMALT0] = &dmsio;\r | |
847 | }\r | |
848 | if (dma1_dev.flags & DEV_DIS) dtab[DMA1] = dtab[DMALT1] = NULL;\r | |
849 | else {\r | |
850 | dtab[DMA1] = &dmpio; /* set up DMA1 dispatch */\r | |
851 | dtab[DMALT1] = &dmsio;\r | |
852 | }\r | |
853 | \r | |
854 | for (i = VARDEV; i <= I_DEVMASK; i++) dtab[i] = NULL; /* clr disp table */\r | |
855 | dev_cmd[0] = dev_cmd[0] & M_FXDEV; /* clear dynamic info */\r | |
856 | dev_ctl[0] = dev_ctl[0] & M_FXDEV;\r | |
857 | dev_flg[0] = dev_flg[0] & M_FXDEV;\r | |
858 | dev_fbf[0] = dev_fbf[0] & M_FXDEV;\r | |
859 | dev_srq[0] = dev_srq[1] = 0; /* init svc requests */\r | |
860 | dev_cmd[1] = dev_ctl[1] = dev_flg[1] = dev_fbf[1] = 0;\r | |
861 | for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */\r | |
862 | dibp = (DIB *) dptr->ctxt; /* get DIB */\r | |
863 | if (dibp && !(dptr->flags & DEV_DIS)) { /* exist, enabled? */\r | |
864 | dev = dibp->devno; /* get dev # */\r | |
865 | if (dibp->cmd) { setCMD (dev); } /* restore cmd */\r | |
866 | if (dibp->ctl) { setCTL (dev); } /* restore ctl */\r | |
867 | if (dibp->flg) { setFLG (dev); } /* restore flg */\r | |
868 | clrFBF (dev); /* also sets fbf */\r | |
869 | if (dibp->fbf) { setFBF (dev); } /* restore fbf */\r | |
870 | if (dibp->srq) { setSRQ (dev); } /* restore srq */\r | |
871 | dtab[dev] = dibp->iot; /* set I/O dispatch */\r | |
872 | }\r | |
873 | }\r | |
874 | \r | |
875 | /* Configure interrupt deferral table */\r | |
876 | \r | |
877 | if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* 21xx series? */\r | |
878 | defer_tab[ioSFC] = defer_tab[ioSFS] = 0; /* SFC/S doesn't defer */\r | |
879 | else /* 1000 series */\r | |
880 | defer_tab[ioSFC] = defer_tab[ioSFS] = 1; /* SFC/S does defer */\r | |
881 | \r | |
882 | /* Abort handling\r | |
883 | \r | |
884 | If an abort occurs in memory protection, the relocation routine\r | |
885 | executes a longjmp to this area OUTSIDE the main simulation loop.\r | |
886 | Memory protection errors are the only sources of aborts in the\r | |
887 | HP 2100. All referenced variables must be globals, and all sim_instr\r | |
888 | scoped automatics must be set after the setjmp.\r | |
889 | */\r | |
890 | \r | |
891 | abortval = setjmp (save_env); /* set abort hdlr */\r | |
892 | if (abortval != 0) { /* mem mgt abort? */\r | |
893 | setFLG (PRO); /* req interrupt */\r | |
894 | mp_evrff = 0; /* block mp_viol upd */\r | |
895 | }\r | |
896 | dmarq = calc_dma (); /* recalc DMA masks */\r | |
897 | intrq = calc_int (); /* recalc interrupts */\r | |
898 | \r | |
899 | /* Main instruction fetch/decode loop */\r | |
900 | \r | |
901 | while (reason == 0) { /* loop until halted */\r | |
902 | uint32 IR, MA, absel, v1, t, skip;\r | |
903 | \r | |
904 | if (sim_interval <= 0) { /* check clock queue */\r | |
905 | if (reason = sim_process_event ()) break;\r | |
906 | dmarq = calc_dma (); /* recalc DMA reqs */\r | |
907 | intrq = calc_int (); /* recalc interrupts */\r | |
908 | }\r | |
909 | \r | |
910 | if (dmarq) {\r | |
911 | if (dmarq & DMAR0) dma_cycle (0, PAMAP); /* DMA1 cycle? */\r | |
912 | if (dmarq & DMAR1) dma_cycle (1, PBMAP); /* DMA2 cycle? */\r | |
913 | dmarq = calc_dma (); /* recalc DMA reqs */\r | |
914 | intrq = calc_int (); /* recalc interrupts */\r | |
915 | }\r | |
916 | \r | |
917 | if (intrq && ion_defer) /* interrupt pending but deferred? */\r | |
918 | ion_defer = calc_defer (); /* confirm deferral */\r | |
919 | \r | |
920 | /* (From Dave Bryan)\r | |
921 | Unlike most other I/O devices, the MP flag flip-flop is cleared\r | |
922 | automatically when the interrupt is acknowledged and not by a programmed\r | |
923 | instruction (CLF and STF affect the parity error enable FF instead).\r | |
924 | Section 4.4.3 "Memory Protect and I/O Interrupt Generation" of the "HP 1000\r | |
925 | M/E/F-Series Computers Engineering and Reference Documentation" (HP\r | |
926 | 92851-90001) says:\r | |
927 | \r | |
928 | "When IAK occurs and IRQ5 is asserted, the FLAGBFF is cleared, FLAGFF\r | |
929 | clocked off at next T2, and IRQ5 will no longer occur."\r | |
930 | */\r | |
931 | \r | |
932 | if (intrq && ((intrq <= PRO) || !ion_defer)) { /* interrupt request? */\r | |
933 | iotrap = 1; /* I/O trap cell instr */\r | |
934 | clrFBF (intrq); /* clear flag buffer */\r | |
935 | if (intrq == PRO) clrFLG (PRO); /* MP flag follows fbuf */\r | |
936 | intaddr = intrq; /* save int addr */\r | |
937 | if (dms_enb) dms_sr = dms_sr | MST_ENBI; /* dms enabled? */\r | |
938 | else dms_sr = dms_sr & ~MST_ENBI;\r | |
939 | if (dms_ump) { /* user map? */\r | |
940 | dms_sr = dms_sr | MST_UMPI;\r | |
941 | dms_ump = SMAP; /* switch to system */\r | |
942 | }\r | |
943 | else dms_sr = dms_sr & ~MST_UMPI;\r | |
944 | IR = ReadW (intrq); /* get dispatch instr */\r | |
945 | ion_defer = 1; /* defer interrupts */\r | |
946 | intrq = 0; /* clear request */\r | |
947 | if (((IR & I_NMRMASK) != I_IO) || /* if not I/O or */\r | |
948 | (I_GETIOOP (IR) == ioHLT)) /* if halt, */\r | |
949 | clrCTL (PRO); /* protection off */\r | |
950 | else /* I/O instr leaves MP on */\r | |
951 | mp_mevff = 0; /* but clears MEV flip-flop */\r | |
952 | }\r | |
953 | \r | |
954 | else { /* normal instruction */\r | |
955 | iotrap = 0;\r | |
956 | err_PC = PC; /* save PC for error */\r | |
957 | if (sim_brk_summ && /* any breakpoints? */\r | |
958 | sim_brk_test (PC, SWMASK ('E') | /* unconditional or */\r | |
959 | (dms_enb? (dms_ump? SWMASK ('U'): SWMASK ('S')):\r | |
960 | SWMASK ('N')))) { /* or right type for DMS? */\r | |
961 | reason = STOP_IBKPT; /* stop simulation */\r | |
962 | break;\r | |
963 | }\r | |
964 | if (mp_evrff) mp_viol = PC; /* if ok, upd mp_viol */\r | |
965 | IR = ReadW (PC); /* fetch instr */\r | |
966 | PC = (PC + 1) & VAMASK;\r | |
967 | ion_defer = 0;\r | |
968 | }\r | |
969 | \r | |
970 | sim_interval = sim_interval - 1; /* count instruction */\r | |
971 | \r | |
972 | /* Instruction decode. The 21MX does a 256-way decode on IR<15:8>\r | |
973 | \r | |
974 | 15 14 13 12 11 10 09 08 instruction\r | |
975 | \r | |
976 | x <-!= 0-> x x x x memory reference\r | |
977 | 0 0 0 0 x 0 x x shift\r | |
978 | 0 0 0 0 x 0 x x alter-skip\r | |
979 | 1 0 0 0 x 1 x x IO\r | |
980 | 1 0 0 0 0 0 x 0 extended arithmetic\r | |
981 | 1 0 0 0 0 0 0 1 divide (decoded as 100400)\r | |
982 | 1 0 0 0 1 0 0 0 double load (decoded as 104000)\r | |
983 | 1 0 0 0 1 0 0 1 double store (decoded as 104400)\r | |
984 | 1 0 0 0 1 0 1 0 extended instr group 0 (A/B must be set)\r | |
985 | 1 0 0 0 x 0 1 1 extended instr group 1 (A/B ignored) */\r | |
986 | \r | |
987 | absel = (IR & I_AB)? 1: 0; /* get A/B select */\r | |
988 | switch ((IR >> 8) & 0377) { /* decode IR<15:8> */\r | |
989 | \r | |
990 | /* Memory reference instructions */\r | |
991 | \r | |
992 | case 0020:case 0021:case 0022:case 0023:\r | |
993 | case 0024:case 0025:case 0026:case 0027:\r | |
994 | case 0220:case 0221:case 0222:case 0223:\r | |
995 | case 0224:case 0225:case 0226:case 0227:\r | |
996 | if (reason = Ea (IR, &MA, intrq)) break; /* AND */\r | |
997 | AR = AR & ReadW (MA);\r | |
998 | break;\r | |
999 | \r | |
1000 | /* JSB is a little tricky. It is possible to generate both an MP and a DM\r | |
1001 | violation simultaneously. Consider a JSB to a location under the MP fence\r | |
1002 | and on a write-protected page. This situation must be reported as a DM\r | |
1003 | violation, because it has priority (SFS 5 and SFC 5 check only the MEVFF,\r | |
1004 | which sets independently of the MP fence violation).\r | |
1005 | \r | |
1006 | Under simulation, this means that DM violations must be checked, and the\r | |
1007 | MEVFF must be set, before an MP abort is taken. This is done for JSB by the\r | |
1008 | WriteW call to store the return PC. However, WriteW only checks for fence\r | |
1009 | violations above location 2, as normally JSBs to locations 0 and 1 (i.e., the\r | |
1010 | A and B register) are allowed. However, if the W5 (JSB) jumper is out, then\r | |
1011 | JSB 0 and JSB 1 are MP violations as well and must be caught. We do this\r | |
1012 | with an explicit check before calling WriteW.\r | |
1013 | \r | |
1014 | This would seem to violate the above requirement for DM checks before MP\r | |
1015 | checks. However, a DM abort cannot occur on a write to 0/1, even if logical\r | |
1016 | page 0 is write-protected, because writes to 0/1 do not attempt to access\r | |
1017 | memory; they are intercepted and affect the A/B registers instead (micro-\r | |
1018 | order TAB is used in the Store field), so no MEV signal is generated.\r | |
1019 | */\r | |
1020 | \r | |
1021 | case 0230:case 0231:case 0232:case 0233:\r | |
1022 | case 0234:case 0235:case 0236:case 0237:\r | |
1023 | ion_defer = 1; /* defer if JSB,I */\r | |
1024 | \r | |
1025 | case 0030:case 0031:case 0032:case 0033:\r | |
1026 | case 0034:case 0035:case 0036:case 0037:\r | |
1027 | if (reason = Ea (IR, &MA, intrq)) break; /* JSB */\r | |
1028 | \r | |
1029 | if ((mp_unit.flags & UNIT_MP_JSB) && /* if W5 (JSB) out */\r | |
1030 | CTL (PRO) && (MA <= 1)) /* and MP on and JSB 0 or JSB 1 */\r | |
1031 | ABORT (ABORT_PRO); /* MP violation */\r | |
1032 | \r | |
1033 | WriteW (MA, PC); /* store PC */\r | |
1034 | PCQ_ENTRY;\r | |
1035 | PC = (MA + 1) & VAMASK; /* jump */\r | |
1036 | break;\r | |
1037 | \r | |
1038 | case 0040:case 0041:case 0042:case 0043:\r | |
1039 | case 0044:case 0045:case 0046:case 0047:\r | |
1040 | case 0240:case 0241:case 0242:case 0243:\r | |
1041 | case 0244:case 0245:case 0246:case 0247:\r | |
1042 | if (reason = Ea (IR, &MA, intrq)) break; /* XOR */\r | |
1043 | AR = AR ^ ReadW (MA);\r | |
1044 | break;\r | |
1045 | \r | |
1046 | case 0250:case 0251:case 0252:case 0253:\r | |
1047 | case 0254:case 0255:case 0256:case 0257:\r | |
1048 | ion_defer = 1; /* defer if JMP,I */\r | |
1049 | \r | |
1050 | case 0050:case 0051:case 0052:case 0053:\r | |
1051 | case 0054:case 0055:case 0056:case 0057:\r | |
1052 | if (reason = Ea (IR, &MA, intrq)) break; /* JMP */\r | |
1053 | mp_dms_jmp (MA); /* validate jump addr */\r | |
1054 | PCQ_ENTRY;\r | |
1055 | PC = MA; /* jump */\r | |
1056 | break;\r | |
1057 | \r | |
1058 | case 0060:case 0061:case 0062:case 0063:\r | |
1059 | case 0064:case 0065:case 0066:case 0067:\r | |
1060 | case 0260:case 0261:case 0262:case 0263:\r | |
1061 | case 0264:case 0265:case 0266:case 0267:\r | |
1062 | if (reason = Ea (IR, &MA, intrq)) break; /* IOR */\r | |
1063 | AR = AR | ReadW (MA);\r | |
1064 | break;\r | |
1065 | \r | |
1066 | case 0070:case 0071:case 0072:case 0073:\r | |
1067 | case 0074:case 0075:case 0076:case 0077:\r | |
1068 | case 0270:case 0271:case 0272:case 0273:\r | |
1069 | case 0274:case 0275:case 0276:case 0277:\r | |
1070 | if (reason = Ea (IR, &MA, intrq)) break; /* ISZ */\r | |
1071 | t = (ReadW (MA) + 1) & DMASK;\r | |
1072 | WriteW (MA, t);\r | |
1073 | if (t == 0) PC = (PC + 1) & VAMASK;\r | |
1074 | break;\r | |
1075 | \r | |
1076 | case 0100:case 0101:case 0102:case 0103:\r | |
1077 | case 0104:case 0105:case 0106:case 0107:\r | |
1078 | case 0300:case 0301:case 0302:case 0303:\r | |
1079 | case 0304:case 0305:case 0306:case 0307:\r | |
1080 | if (reason = Ea (IR, &MA, intrq)) break; /* ADA */\r | |
1081 | v1 = ReadW (MA);\r | |
1082 | t = AR + v1;\r | |
1083 | if (t > DMASK) E = 1;\r | |
1084 | if (((~AR ^ v1) & (AR ^ t)) & SIGN) O = 1;\r | |
1085 | AR = t & DMASK;\r | |
1086 | break;\r | |
1087 | \r | |
1088 | case 0110:case 0111:case 0112:case 0113:\r | |
1089 | case 0114:case 0115:case 0116:case 0117:\r | |
1090 | case 0310:case 0311:case 0312:case 0313:\r | |
1091 | case 0314:case 0315:case 0316:case 0317:\r | |
1092 | if (reason = Ea (IR, &MA, intrq)) break; /* ADB */\r | |
1093 | v1 = ReadW (MA);\r | |
1094 | t = BR + v1;\r | |
1095 | if (t > DMASK) E = 1;\r | |
1096 | if (((~BR ^ v1) & (BR ^ t)) & SIGN) O = 1;\r | |
1097 | BR = t & DMASK;\r | |
1098 | break;\r | |
1099 | \r | |
1100 | case 0120:case 0121:case 0122:case 0123:\r | |
1101 | case 0124:case 0125:case 0126:case 0127:\r | |
1102 | case 0320:case 0321:case 0322:case 0323:\r | |
1103 | case 0324:case 0325:case 0326:case 0327:\r | |
1104 | if (reason = Ea (IR, &MA, intrq)) break; /* CPA */\r | |
1105 | if (AR != ReadW (MA)) PC = (PC + 1) & VAMASK;\r | |
1106 | break;\r | |
1107 | \r | |
1108 | case 0130:case 0131:case 0132:case 0133:\r | |
1109 | case 0134:case 0135:case 0136:case 0137:\r | |
1110 | case 0330:case 0331:case 0332:case 0333:\r | |
1111 | case 0334:case 0335:case 0336:case 0337:\r | |
1112 | if (reason = Ea (IR, &MA, intrq)) break; /* CPB */\r | |
1113 | if (BR != ReadW (MA)) PC = (PC + 1) & VAMASK;\r | |
1114 | break;\r | |
1115 | \r | |
1116 | case 0140:case 0141:case 0142:case 0143:\r | |
1117 | case 0144:case 0145:case 0146:case 0147:\r | |
1118 | case 0340:case 0341:case 0342:case 0343:\r | |
1119 | case 0344:case 0345:case 0346:case 0347:\r | |
1120 | if (reason = Ea (IR, &MA, intrq)) break; /* LDA */\r | |
1121 | AR = ReadW (MA);\r | |
1122 | break;\r | |
1123 | \r | |
1124 | case 0150:case 0151:case 0152:case 0153:\r | |
1125 | case 0154:case 0155:case 0156:case 0157:\r | |
1126 | case 0350:case 0351:case 0352:case 0353:\r | |
1127 | case 0354:case 0355:case 0356:case 0357:\r | |
1128 | if (reason = Ea (IR, &MA, intrq)) break; /* LDB */\r | |
1129 | BR = ReadW (MA);\r | |
1130 | break;\r | |
1131 | \r | |
1132 | case 0160:case 0161:case 0162:case 0163:\r | |
1133 | case 0164:case 0165:case 0166:case 0167:\r | |
1134 | case 0360:case 0361:case 0362:case 0363:\r | |
1135 | case 0364:case 0365:case 0366:case 0367:\r | |
1136 | if (reason = Ea (IR, &MA, intrq)) break; /* STA */\r | |
1137 | WriteW (MA, AR);\r | |
1138 | break;\r | |
1139 | \r | |
1140 | case 0170:case 0171:case 0172:case 0173:\r | |
1141 | case 0174:case 0175:case 0176:case 0177:\r | |
1142 | case 0370:case 0371:case 0372:case 0373:\r | |
1143 | case 0374:case 0375:case 0376:case 0377:\r | |
1144 | if (reason = Ea (IR, &MA, intrq)) break; /* STB */\r | |
1145 | WriteW (MA, BR);\r | |
1146 | break;\r | |
1147 | \r | |
1148 | /* Alter/skip instructions */\r | |
1149 | \r | |
1150 | case 0004:case 0005:case 0006:case 0007:\r | |
1151 | case 0014:case 0015:case 0016:case 0017:\r | |
1152 | skip = 0; /* no skip */\r | |
1153 | if (IR & 000400) t = 0; /* CLx */\r | |
1154 | else t = ABREG[absel];\r | |
1155 | if (IR & 001000) t = t ^ DMASK; /* CMx */\r | |
1156 | if (IR & 000001) { /* RSS? */\r | |
1157 | if ((IR & 000040) && (E != 0)) skip = 1; /* SEZ,RSS */\r | |
1158 | if (IR & 000100) E = 0; /* CLE */\r | |
1159 | if (IR & 000200) E = E ^ 1; /* CME */\r | |
1160 | if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */\r | |
1161 | ((t & 0100001) == 0100001)) skip = 1;\r | |
1162 | if (((IR & 000030) == 000020) && /* SSx,RSS */\r | |
1163 | ((t & SIGN) != 0)) skip = 1;\r | |
1164 | if (((IR & 000030) == 000010) && /* SLx,RSS */\r | |
1165 | ((t & 1) != 0)) skip = 1;\r | |
1166 | if (IR & 000004) { /* INx */\r | |
1167 | t = (t + 1) & DMASK;\r | |
1168 | if (t == 0) E = 1;\r | |
1169 | if (t == SIGN) O = 1;\r | |
1170 | }\r | |
1171 | if ((IR & 000002) && (t != 0)) skip = 1; /* SZx,RSS */\r | |
1172 | if ((IR & 000072) == 0) skip = 1; /* RSS */\r | |
1173 | } /* end if RSS */\r | |
1174 | else {\r | |
1175 | if ((IR & 000040) && (E == 0)) skip = 1; /* SEZ */\r | |
1176 | if (IR & 000100) E = 0; /* CLE */\r | |
1177 | if (IR & 000200) E = E ^ 1; /* CME */\r | |
1178 | if ((IR & 000020) && /* SSx */\r | |
1179 | ((t & SIGN) == 0)) skip = 1;\r | |
1180 | if ((IR & 000010) && /* SLx */\r | |
1181 | ((t & 1) == 0)) skip = 1;\r | |
1182 | if (IR & 000004) { /* INx */\r | |
1183 | t = (t + 1) & DMASK;\r | |
1184 | if (t == 0) E = 1;\r | |
1185 | if (t == SIGN) O = 1;\r | |
1186 | }\r | |
1187 | if ((IR & 000002) && (t == 0)) skip = 1; /* SZx */\r | |
1188 | } /* end if ~RSS */\r | |
1189 | ABREG[absel] = t; /* store result */\r | |
1190 | PC = (PC + skip) & VAMASK; /* add in skip */\r | |
1191 | break; /* end if alter/skip */\r | |
1192 | \r | |
1193 | /* Shift instructions */\r | |
1194 | \r | |
1195 | case 0000:case 0001:case 0002:case 0003:\r | |
1196 | case 0010:case 0011:case 0012:case 0013:\r | |
1197 | t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */\r | |
1198 | if (IR & 000040) E = 0; /* CLE */\r | |
1199 | if ((IR & 000010) && ((t & 1) == 0)) /* SLx */\r | |
1200 | PC = (PC + 1) & VAMASK;\r | |
1201 | ABREG[absel] = shift (t, IR & 00020, IR); /* do second shift */\r | |
1202 | break; /* end if shift */\r | |
1203 | \r | |
1204 | /* I/O instructions */\r | |
1205 | \r | |
1206 | case 0204:case 0205:case 0206:case 0207:\r | |
1207 | case 0214:case 0215:case 0216:case 0217:\r | |
1208 | reason = iogrp (IR, iotrap); /* execute instr */\r | |
1209 | break; /* end if I/O */\r | |
1210 | \r | |
1211 | /* Extended arithmetic */\r | |
1212 | \r | |
1213 | case 0200: /* EAU group 0 */\r | |
1214 | case 0201: /* divide */\r | |
1215 | case 0202: /* EAU group 2 */\r | |
1216 | case 0210: /* DLD */\r | |
1217 | case 0211: /* DST */\r | |
1218 | reason = cpu_eau (IR, intrq); /* extended arith */\r | |
1219 | break;\r | |
1220 | \r | |
1221 | /* Extended instructions */\r | |
1222 | \r | |
1223 | case 0212: /* UIG 0 extension */\r | |
1224 | reason = cpu_uig_0 (IR, intrq, iotrap); /* extended opcode */\r | |
1225 | break;\r | |
1226 | \r | |
1227 | case 0203: /* UIG 1 extension */\r | |
1228 | case 0213:\r | |
1229 | reason = cpu_uig_1 (IR, intrq, iotrap); /* extended opcode */\r | |
1230 | break;\r | |
1231 | } /* end case IR */\r | |
1232 | \r | |
1233 | if (reason == NOTE_IOG) { /* I/O instr exec? */\r | |
1234 | dmarq = calc_dma (); /* recalc DMA masks */\r | |
1235 | intrq = calc_int (); /* recalc interrupts */\r | |
1236 | reason = 0; /* continue */\r | |
1237 | }\r | |
1238 | \r | |
1239 | else if (reason == NOTE_INDINT) { /* intr pend during indir? */\r | |
1240 | PC = err_PC; /* back out of inst */\r | |
1241 | reason = 0; /* continue */\r | |
1242 | }\r | |
1243 | } /* end while */\r | |
1244 | \r | |
1245 | /* Simulation halted */\r | |
1246 | \r | |
1247 | saved_AR = AR & DMASK;\r | |
1248 | saved_BR = BR & DMASK;\r | |
1249 | if (iotrap && (reason == STOP_HALT)) MR = intaddr; /* HLT in trap cell? */\r | |
1250 | else MR = (PC - 1) & VAMASK; /* no, M = P - 1 */\r | |
1251 | TR = ReadTAB (MR); /* last word fetched */\r | |
1252 | saved_MR = MR; /* save for T cmd update */\r | |
1253 | if ((reason == STOP_RSRV) || (reason == STOP_IODV) || /* instr error? */\r | |
1254 | (reason == STOP_IND)) PC = err_PC; /* back up PC */\r | |
1255 | dms_upd_sr (); /* update dms_sr */\r | |
1256 | if (reason == STOP_HALT) /* programmed halt? */\r | |
1257 | cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (ignore errors) */\r | |
1258 | for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */\r | |
1259 | dibp = (DIB *) dptr->ctxt; /* get DIB */\r | |
1260 | if (dibp) { /* exist? */\r | |
1261 | dev = dibp->devno;\r | |
1262 | dibp->cmd = CMD (dev);\r | |
1263 | dibp->ctl = CTL (dev);\r | |
1264 | dibp->flg = FLG (dev);\r | |
1265 | dibp->fbf = FBF (dev);\r | |
1266 | dibp->srq = SRQ (dev);\r | |
1267 | }\r | |
1268 | }\r | |
1269 | pcq_r->qptr = pcq_p; /* update pc q ptr */\r | |
1270 | if (dms_enb) /* default breakpoint type */\r | |
1271 | if (dms_ump) sim_brk_dflt = SWMASK ('U'); /* to current map mode */\r | |
1272 | else sim_brk_dflt = SWMASK ('S');\r | |
1273 | else sim_brk_dflt = SWMASK ('N');\r | |
1274 | return reason;\r | |
1275 | }\r | |
1276 | \r | |
1277 | /* Resolve indirect addresses.\r | |
1278 | \r | |
1279 | An indirect chain is followed until a direct address is obtained. Under\r | |
1280 | simulation, a maximum number of indirect levels are allowed (typically 16),\r | |
1281 | after which the instruction will be aborted.\r | |
1282 | \r | |
1283 | If the memory protect feature is present, an indirect counter is used that\r | |
1284 | allows a pending interrupt to be serviced if more than three levels of\r | |
1285 | indirection are encountered. If MP jumper W6 ("INT") is out and MP is\r | |
1286 | enabled, then pending interrupts are serviced immediately. When employing\r | |
1287 | the indirect counter, the hardware clears a pending interrupt deferral after\r | |
1288 | the third indirection and aborts the instruction after the fourth.\r | |
1289 | */\r | |
1290 | \r | |
1291 | t_stat resolve (uint32 MA, uint32 *addr, uint32 irq)\r | |
1292 | {\r | |
1293 | uint32 i;\r | |
1294 | t_bool pending = (irq && !(mp_unit.flags & DEV_DIS));\r | |
1295 | t_bool int_enable = ((mp_unit.flags & UNIT_MP_INT) && CTL(PRO));\r | |
1296 | \r | |
1297 | for (i = 0; (i < ind_max) && (MA & I_IA); i++) { /* resolve multilevel */\r | |
1298 | if (pending) { /* interrupt pending and MP enabled? */\r | |
1299 | if ((i == 2) || int_enable) /* 3rd level indirect or INT out? */\r | |
1300 | ion_defer = 0; /* reenable interrrupts */\r | |
1301 | if ((i > 2) || int_enable) /* 4th or higher or INT out? */\r | |
1302 | return NOTE_INDINT; /* break out now */\r | |
1303 | }\r | |
1304 | MA = ReadW (MA & VAMASK); /* follow address chain */\r | |
1305 | }\r | |
1306 | if (MA & I_IA) return STOP_IND; /* indirect loop? */\r | |
1307 | *addr = MA;\r | |
1308 | return SCPE_OK;\r | |
1309 | }\r | |
1310 | \r | |
1311 | /* Get effective address from IR */\r | |
1312 | \r | |
1313 | t_stat Ea (uint32 IR, uint32 *addr, uint32 irq)\r | |
1314 | {\r | |
1315 | uint32 MA;\r | |
1316 | \r | |
1317 | MA = IR & (I_IA | I_DISP); /* ind + disp */\r | |
1318 | if (IR & I_CP) MA = ((PC - 1) & I_PAGENO) | MA; /* current page? */\r | |
1319 | return resolve (MA, addr, irq); /* resolve indirects */\r | |
1320 | }\r | |
1321 | \r | |
1322 | /* Shift micro operation */\r | |
1323 | \r | |
1324 | uint32 shift (uint32 t, uint32 flag, uint32 op)\r | |
1325 | {\r | |
1326 | uint32 oldE;\r | |
1327 | \r | |
1328 | op = op & 07; /* get shift op */\r | |
1329 | if (flag) { /* enabled? */\r | |
1330 | switch (op) { /* case on operation */\r | |
1331 | \r | |
1332 | case 00: /* signed left shift */\r | |
1333 | return ((t & SIGN) | ((t << 1) & 077777));\r | |
1334 | \r | |
1335 | case 01: /* signed right shift */\r | |
1336 | return ((t & SIGN) | (t >> 1));\r | |
1337 | \r | |
1338 | case 02: /* rotate left */\r | |
1339 | return (((t << 1) | (t >> 15)) & DMASK);\r | |
1340 | \r | |
1341 | case 03: /* rotate right */\r | |
1342 | return (((t >> 1) | (t << 15)) & DMASK);\r | |
1343 | \r | |
1344 | case 04: /* left shift, 0 sign */\r | |
1345 | return ((t << 1) & 077777);\r | |
1346 | \r | |
1347 | case 05: /* ext right rotate */\r | |
1348 | oldE = E;\r | |
1349 | E = t & 1;\r | |
1350 | return ((t >> 1) | (oldE << 15));\r | |
1351 | \r | |
1352 | case 06: /* ext left rotate */\r | |
1353 | oldE = E;\r | |
1354 | E = (t >> 15) & 1;\r | |
1355 | return (((t << 1) | oldE) & DMASK);\r | |
1356 | \r | |
1357 | case 07: /* rotate left four */\r | |
1358 | return (((t << 4) | (t >> 12)) & DMASK);\r | |
1359 | } /* end case */\r | |
1360 | } /* end if */\r | |
1361 | if (op == 05) E = t & 1; /* disabled ext rgt rot */\r | |
1362 | if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */\r | |
1363 | return t; /* input unchanged */\r | |
1364 | }\r | |
1365 | \r | |
1366 | /* I/O instruction decode.\r | |
1367 | \r | |
1368 | If memory protect is enabled, and the instruction is not in a trap cell, then\r | |
1369 | HLT instructions are illegal and will cause a memory protect violation. If\r | |
1370 | jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is\r | |
1371 | out, then only I/O instructions to select code 1 are legal.\r | |
1372 | \r | |
1373 | We return NOTE_IOG for normal status instead of SCPE_OK to request that\r | |
1374 | interrupts be recalculated at the end of the instruction (execution of the\r | |
1375 | I/O group instructions can change the interrupt priority chain).\r | |
1376 | */\r | |
1377 | \r | |
1378 | t_stat iogrp (uint32 ir, uint32 iotrap)\r | |
1379 | {\r | |
1380 | uint32 dev, sop, iodata, iostat, ab;\r | |
1381 | \r | |
1382 | ab = (ir & I_AB)? 1: 0; /* get A/B select */\r | |
1383 | dev = ir & I_DEVMASK; /* get device */\r | |
1384 | sop = I_GETIOOP (ir); /* get subopcode */\r | |
1385 | if (!iotrap && CTL (PRO) && /* protected? */\r | |
1386 | ((sop == ioHLT) || /* halt or !ovf? */\r | |
1387 | ((dev != OVF) && (mp_unit.flags & UNIT_MP_SEL1)))) { /* sel code OK? */\r | |
1388 | if (sop == ioLIX) ABREG[ab] = 0; /* A/B writes anyway */\r | |
1389 | ABORT (ABORT_PRO);\r | |
1390 | }\r | |
1391 | iodata = devdisp (dev, sop, ir, ABREG[ab]); /* process I/O */\r | |
1392 | ion_defer = defer_tab[sop]; /* set defer */\r | |
1393 | if ((sop == ioMIX) || (sop == ioLIX)) /* store ret data */\r | |
1394 | ABREG[ab] = iodata & DMASK;\r | |
1395 | if (sop == ioHLT) { /* halt? */\r | |
1396 | int32 len = strlen (halt_msg); /* find end msg */\r | |
1397 | sprintf (&halt_msg[len - 6], "%06o", ir); /* add the halt */\r | |
1398 | return STOP_HALT;\r | |
1399 | }\r | |
1400 | iostat = iodata >> IOT_V_REASON;\r | |
1401 | if (iostat == SCPE_OK) return NOTE_IOG; /* normal status */\r | |
1402 | else return iostat; /* abnormal status */\r | |
1403 | }\r | |
1404 | \r | |
1405 | /* Device dispatch */\r | |
1406 | \r | |
1407 | uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 dat)\r | |
1408 | {\r | |
1409 | if (dtab[devno]) return dtab[devno] (inst, IR, dat);\r | |
1410 | else return nulio (inst, IR, dat);\r | |
1411 | }\r | |
1412 | \r | |
1413 | /* Calculate DMA requests */\r | |
1414 | \r | |
1415 | uint32 calc_dma (void)\r | |
1416 | {\r | |
1417 | uint32 r = 0;\r | |
1418 | \r | |
1419 | if (CMD (DMA0) && SRQ (dmac[0].cw1 & I_DEVMASK)) /* check DMA0 cycle */\r | |
1420 | r = r | DMAR0;\r | |
1421 | if (CMD (DMA1) && SRQ (dmac[1].cw1 & I_DEVMASK)) /* check DMA1 cycle */\r | |
1422 | r = r | DMAR1;\r | |
1423 | return r;\r | |
1424 | }\r | |
1425 | \r | |
1426 | /* Determine whether a pending interrupt deferral should be inhibited.\r | |
1427 | \r | |
1428 | Execution of certain instructions generally causes a pending interrupt to be\r | |
1429 | deferred until the succeeding instruction completes. However, the interrupt\r | |
1430 | deferral rules differ on the 21xx vs. the 1000.\r | |
1431 | \r | |
1432 | The 1000 always defers until the completion of the instruction following a\r | |
1433 | deferring instruction. The 21xx defers unless the following instruction is\r | |
1434 | an MRG instruction other than JMP or JMP,I or JSB,I. If it is, then the\r | |
1435 | deferral is inhibited, i.e., the pending interrupt will be serviced.\r | |
1436 | \r | |
1437 | See the "Set Phase Logic Flowchart," transition from phase 1A to phase 1B,\r | |
1438 | and the "Theory of Operation," "Control Section Detailed Theory," "Phase\r | |
1439 | Control Logic," "Phase 1B" paragraph in the Model 2100A Computer Installation\r | |
1440 | and Maintenance Manual for details.\r | |
1441 | */\r | |
1442 | \r | |
1443 | uint32 calc_defer (void)\r | |
1444 | {\r | |
1445 | uint16 IR;\r | |
1446 | \r | |
1447 | if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx series? */\r | |
1448 | IR = ReadW (PC); /* prefetch next instr */\r | |
1449 | \r | |
1450 | if (((IR & I_MRG & ~I_AB) != 0000000) && /* is MRG instruction? */\r | |
1451 | ((IR & I_MRG_I) != I_JSB_I) && /* but not JSB,I? */\r | |
1452 | ((IR & I_MRG) != I_JMP)) /* and not JMP or JMP,I? */\r | |
1453 | return 0; /* yes, so inhibit deferral */\r | |
1454 | else\r | |
1455 | return 1; /* no, so allow deferral */\r | |
1456 | }\r | |
1457 | else\r | |
1458 | return 1; /* 1000 always allows deferral */\r | |
1459 | }\r | |
1460 | \r | |
1461 | /* Calculate interrupt requests\r | |
1462 | \r | |
1463 | This routine takes into account all the relevant state of the\r | |
1464 | interrupt system: ion, dev_flg, dev_fbf, and dev_ctl.\r | |
1465 | \r | |
1466 | 1. dev_flg & dev_ctl determines the end of the priority grant.\r | |
1467 | The break in the chain will occur at the first device for\r | |
1468 | which dev_flg & dev_ctl is true. This is determined by\r | |
1469 | AND'ing the set bits with their 2's complement; only the low\r | |
1470 | order (highest priority) bit will differ. 1 less than\r | |
1471 | that, or'd with the single set bit itself, is the mask of\r | |
1472 | possible interrupting devices. If ION is clear, only devices\r | |
1473 | 4 and 5 are eligible to interrupt.\r | |
1474 | 2. dev_flg & dev_ctl & dev_fbf determines the outstanding\r | |
1475 | interrupt requests. All three bits must be on for a device\r | |
1476 | to request an interrupt. This is the masked under the\r | |
1477 | result from #1 to determine the highest priority interrupt,\r | |
1478 | if any.\r | |
1479 | */\r | |
1480 | \r | |
1481 | uint32 calc_int (void)\r | |
1482 | {\r | |
1483 | int32 j, lomask, mask[2], req[2];\r | |
1484 | \r | |
1485 | lomask = dev_flg[0] & dev_ctl[0] & ~M_NXDEV; /* start chain calc */\r | |
1486 | req[0] = lomask & dev_fbf[0]; /* calc requests */\r | |
1487 | lomask = lomask & (-lomask); /* chain & -chain */\r | |
1488 | mask[0] = lomask | (lomask - 1); /* enabled devices */\r | |
1489 | req[0] = req[0] & mask[0]; /* highest request */\r | |
1490 | if (ion) { /* ion? */\r | |
1491 | if (lomask == 0) { /* no break in chn? */\r | |
1492 | mask[1] = dev_flg[1] & dev_ctl[1]; /* do all devices */\r | |
1493 | req[1] = mask[1] & dev_fbf[1];\r | |
1494 | mask[1] = mask[1] & (-mask[1]);\r | |
1495 | mask[1] = mask[1] | (mask[1] - 1);\r | |
1496 | req[1] = req[1] & mask[1];\r | |
1497 | }\r | |
1498 | else req[1] = 0;\r | |
1499 | }\r | |
1500 | else {\r | |
1501 | req[0] = req[0] & (INT_M (PWR) | INT_M (PRO));\r | |
1502 | req[1] = 0;\r | |
1503 | }\r | |
1504 | if (req[0]) { /* if low request */\r | |
1505 | for (j = 0; j < 32; j++) { /* find dev # */\r | |
1506 | if (req[0] & INT_M (j)) return j;\r | |
1507 | }\r | |
1508 | }\r | |
1509 | if (req[1]) { /* if hi request */\r | |
1510 | for (j = 0; j < 32; j++) { /* find dev # */\r | |
1511 | if (req[1] & INT_M (j)) return (32 + j);\r | |
1512 | }\r | |
1513 | }\r | |
1514 | return 0;\r | |
1515 | }\r | |
1516 | \r | |
1517 | /* Memory access routines */\r | |
1518 | \r | |
1519 | uint8 ReadB (uint32 va)\r | |
1520 | {\r | |
1521 | int32 pa;\r | |
1522 | \r | |
1523 | if (dms_enb) pa = dms (va >> 1, dms_ump, RD);\r | |
1524 | else pa = va >> 1;\r | |
1525 | if (va & 1) return (ReadPW (pa) & 0377);\r | |
1526 | else return ((ReadPW (pa) >> 8) & 0377);\r | |
1527 | }\r | |
1528 | \r | |
1529 | uint8 ReadBA (uint32 va)\r | |
1530 | {\r | |
1531 | uint32 pa;\r | |
1532 | \r | |
1533 | if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, RD);\r | |
1534 | else pa = va >> 1;\r | |
1535 | if (va & 1) return (ReadPW (pa) & 0377);\r | |
1536 | else return ((ReadPW (pa) >> 8) & 0377);\r | |
1537 | }\r | |
1538 | \r | |
1539 | uint16 ReadW (uint32 va)\r | |
1540 | {\r | |
1541 | uint32 pa;\r | |
1542 | \r | |
1543 | if (dms_enb) pa = dms (va, dms_ump, RD);\r | |
1544 | else pa = va;\r | |
1545 | return ReadPW (pa);\r | |
1546 | }\r | |
1547 | \r | |
1548 | uint16 ReadWA (uint32 va)\r | |
1549 | {\r | |
1550 | uint32 pa;\r | |
1551 | \r | |
1552 | if (dms_enb) pa = dms (va, dms_ump ^ MAP_LNT, RD);\r | |
1553 | else pa = va;\r | |
1554 | return ReadPW (pa);\r | |
1555 | }\r | |
1556 | \r | |
1557 | uint16 ReadIO (uint32 va, uint32 map)\r | |
1558 | {\r | |
1559 | uint32 pa;\r | |
1560 | \r | |
1561 | if (dms_enb) pa = dms_io (va, map);\r | |
1562 | else pa = va;\r | |
1563 | return M[pa];\r | |
1564 | }\r | |
1565 | \r | |
1566 | uint16 ReadPW (uint32 pa)\r | |
1567 | {\r | |
1568 | if (pa <= 1) return ABREG[pa];\r | |
1569 | return M[pa];\r | |
1570 | }\r | |
1571 | \r | |
1572 | uint16 ReadTAB (uint32 addr)\r | |
1573 | {\r | |
1574 | if (addr == 0) return saved_AR;\r | |
1575 | else if (addr == 1) return saved_BR;\r | |
1576 | else return ReadIO (addr, dms_ump);\r | |
1577 | }\r | |
1578 | \r | |
1579 | /* Memory protection test for writes\r | |
1580 | \r | |
1581 | From Dave Bryan: The problem is that memory writes aren't being checked for\r | |
1582 | an MP violation if DMS is enabled, i.e., if DMS is enabled, and the page is\r | |
1583 | writable, then whether the target is below the MP fence is not checked. [The\r | |
1584 | simulator must] do MP check on all writes after DMS translation and violation\r | |
1585 | checks are done (so, to pass, the page must be writable AND the target must\r | |
1586 | be above the MP fence).\r | |
1587 | */\r | |
1588 | \r | |
1589 | #define MP_TEST(x) (CTL (PRO) && ((x) > 1) && ((x) < mp_fence))\r | |
1590 | \r | |
1591 | void WriteB (uint32 va, uint32 dat)\r | |
1592 | {\r | |
1593 | uint32 pa, t;\r | |
1594 | \r | |
1595 | if (dms_enb) pa = dms (va >> 1, dms_ump, WR);\r | |
1596 | else pa = va >> 1;\r | |
1597 | if (MP_TEST (va >> 1)) ABORT (ABORT_PRO);\r | |
1598 | if (MEM_ADDR_OK (pa)) {\r | |
1599 | t = ReadPW (pa);\r | |
1600 | if (va & 1) t = (t & 0177400) | (dat & 0377);\r | |
1601 | else t = (t & 0377) | ((dat & 0377) << 8);\r | |
1602 | WritePW (pa, t);\r | |
1603 | }\r | |
1604 | return;\r | |
1605 | }\r | |
1606 | \r | |
1607 | void WriteBA (uint32 va, uint32 dat)\r | |
1608 | {\r | |
1609 | uint32 pa, t;\r | |
1610 | \r | |
1611 | if (dms_enb) {\r | |
1612 | dms_viol (va >> 1, MVI_WPR); /* viol if prot */\r | |
1613 | pa = dms (va >> 1, dms_ump ^ MAP_LNT, WR);\r | |
1614 | }\r | |
1615 | else pa = va >> 1;\r | |
1616 | if (MP_TEST (va >> 1)) ABORT (ABORT_PRO);\r | |
1617 | if (MEM_ADDR_OK (pa)) {\r | |
1618 | t = ReadPW (pa);\r | |
1619 | if (va & 1) t = (t & 0177400) | (dat & 0377);\r | |
1620 | else t = (t & 0377) | ((dat & 0377) << 8);\r | |
1621 | WritePW (pa, t);\r | |
1622 | }\r | |
1623 | return;\r | |
1624 | }\r | |
1625 | \r | |
1626 | void WriteW (uint32 va, uint32 dat)\r | |
1627 | {\r | |
1628 | uint32 pa;\r | |
1629 | \r | |
1630 | if (dms_enb) pa = dms (va, dms_ump, WR);\r | |
1631 | else pa = va;\r | |
1632 | if (MP_TEST (va)) ABORT (ABORT_PRO);\r | |
1633 | if (MEM_ADDR_OK (pa)) WritePW (pa, dat);\r | |
1634 | return;\r | |
1635 | }\r | |
1636 | \r | |
1637 | void WriteWA (uint32 va, uint32 dat)\r | |
1638 | {\r | |
1639 | int32 pa;\r | |
1640 | \r | |
1641 | if (dms_enb) {\r | |
1642 | dms_viol (va, MVI_WPR); /* viol if prot */\r | |
1643 | pa = dms (va, dms_ump ^ MAP_LNT, WR);\r | |
1644 | }\r | |
1645 | else pa = va;\r | |
1646 | if (MP_TEST (va)) ABORT (ABORT_PRO);\r | |
1647 | if (MEM_ADDR_OK (pa)) WritePW (pa, dat);\r | |
1648 | return;\r | |
1649 | }\r | |
1650 | \r | |
1651 | void WriteIO (uint32 va, uint32 dat, uint32 map)\r | |
1652 | {\r | |
1653 | uint32 pa;\r | |
1654 | \r | |
1655 | if (dms_enb) pa = dms_io (va, map);\r | |
1656 | else pa = va;\r | |
1657 | if (MEM_ADDR_OK (pa)) M[pa] = dat & DMASK;\r | |
1658 | return;\r | |
1659 | }\r | |
1660 | \r | |
1661 | void WritePW (uint32 pa, uint32 dat)\r | |
1662 | {\r | |
1663 | if (pa <= 1) ABREG[pa] = dat & DMASK;\r | |
1664 | else M[pa] = dat & DMASK;\r | |
1665 | return;\r | |
1666 | }\r | |
1667 | \r | |
1668 | /* DMS relocation for CPU access */\r | |
1669 | \r | |
1670 | uint32 dms (uint32 va, uint32 map, uint32 prot)\r | |
1671 | {\r | |
1672 | uint32 pgn, mpr;\r | |
1673 | \r | |
1674 | if (va <= 1) return va; /* A, B */\r | |
1675 | pgn = VA_GETPAG (va); /* get page num */\r | |
1676 | if (pgn == 0) { /* base page? */\r | |
1677 | uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */\r | |
1678 | if ((dms_sr & MST_FLT)? /* check unmapped */\r | |
1679 | (va >= dms_fence): /* 1B10: >= fence */\r | |
1680 | (va < dms_fence)) { /* 0B10: < fence */\r | |
1681 | if (prot == WR) dms_viol (va, MVI_BPG); /* if W, viol */\r | |
1682 | return va; /* no mapping */\r | |
1683 | }\r | |
1684 | }\r | |
1685 | mpr = dms_map[map + pgn]; /* get map reg */\r | |
1686 | if (mpr & prot) dms_viol (va, prot); /* prot violation? */\r | |
1687 | return (MAP_GETPAG (mpr) | VA_GETOFF (va));\r | |
1688 | }\r | |
1689 | \r | |
1690 | /* DMS relocation for IO access */\r | |
1691 | \r | |
1692 | uint32 dms_io (uint32 va, uint32 map)\r | |
1693 | {\r | |
1694 | uint32 pgn, mpr;\r | |
1695 | \r | |
1696 | if (va <= 1) return va; /* A, B */\r | |
1697 | pgn = VA_GETPAG (va); /* get page num */\r | |
1698 | if (pgn == 0) { /* base page? */\r | |
1699 | uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */\r | |
1700 | if ((dms_sr & MST_FLT)? /* check unmapped */\r | |
1701 | (va >= dms_fence): /* 1B10: >= fence */\r | |
1702 | (va < dms_fence)) { /* 0B10: < fence */\r | |
1703 | return va; /* no mapping */\r | |
1704 | }\r | |
1705 | }\r | |
1706 | mpr = dms_map[map + pgn]; /* get map reg */\r | |
1707 | return (MAP_GETPAG (mpr) | VA_GETOFF (va));\r | |
1708 | }\r | |
1709 | \r | |
1710 | /* DMS relocation for console access */\r | |
1711 | \r | |
1712 | uint32 dms_cons (uint32 va, int32 sw)\r | |
1713 | {\r | |
1714 | uint32 map_sel;\r | |
1715 | \r | |
1716 | if ((dms_enb == 0) || /* DMS off? */\r | |
1717 | (sw & (SWMASK ('N') | SIM_SW_REST))) /* no mapping rqst or save/rest? */\r | |
1718 | return va; /* use physical address */\r | |
1719 | else if (sw & SWMASK ('S')) map_sel = SMAP;\r | |
1720 | else if (sw & SWMASK ('U')) map_sel = UMAP;\r | |
1721 | else if (sw & SWMASK ('P')) map_sel = PAMAP;\r | |
1722 | else if (sw & SWMASK ('Q')) map_sel = PBMAP;\r | |
1723 | else map_sel = dms_ump; /* dflt to log addr, cur map */\r | |
1724 | if (va >= VASIZE) return MEMSIZE; /* virtual, must be 15b */\r | |
1725 | else if (dms_enb) return dms_io (va, map_sel); /* DMS on? go thru map */\r | |
1726 | else return va; /* else return virtual */\r | |
1727 | }\r | |
1728 | \r | |
1729 | /* Mem protect and DMS validation for jumps */\r | |
1730 | \r | |
1731 | void mp_dms_jmp (uint32 va)\r | |
1732 | {\r | |
1733 | uint32 pgn = VA_GETPAG (va); /* get page num */\r | |
1734 | \r | |
1735 | if ((pgn == 0) && (va > 1)) { /* base page? */\r | |
1736 | uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */\r | |
1737 | if ((dms_sr & MST_FLT)? /* check unmapped */\r | |
1738 | (va >= dms_fence): /* 1B10: >= fence */\r | |
1739 | (va < dms_fence)) { /* 0B10: < fence */\r | |
1740 | dms_viol (va, MVI_BPG); /* if W, viol */\r | |
1741 | return; /* PRO not set */\r | |
1742 | }\r | |
1743 | }\r | |
1744 | if (CTL (PRO) && (va < mp_fence)) ABORT (ABORT_PRO); /* base page MPR */\r | |
1745 | return;\r | |
1746 | }\r | |
1747 | \r | |
1748 | /* DMS read and write maps */\r | |
1749 | \r | |
1750 | uint16 dms_rmap (uint32 mapi)\r | |
1751 | {\r | |
1752 | mapi = mapi & MAP_MASK;\r | |
1753 | return (dms_map[mapi] & ~MAP_MBZ);\r | |
1754 | }\r | |
1755 | \r | |
1756 | void dms_wmap (uint32 mapi, uint32 dat)\r | |
1757 | {\r | |
1758 | mapi = mapi & MAP_MASK;\r | |
1759 | dms_map[mapi] = (uint16) (dat & ~MAP_MBZ);\r | |
1760 | return;\r | |
1761 | }\r | |
1762 | \r | |
1763 | /* DMS violation */\r | |
1764 | \r | |
1765 | void dms_viol (uint32 va, uint32 st)\r | |
1766 | {\r | |
1767 | dms_vr = st | VA_GETPAG (va) |\r | |
1768 | ((st & (MVI_RPR | MVI_WPR))? MVI_MEB: 0) | /* set MEB */\r | |
1769 | (dms_enb? MVI_MEM: 0) | /* set MEM */\r | |
1770 | (dms_ump? MVI_UMP: 0); /* set UMAP */\r | |
1771 | if (CTL (PRO)) { /* protected? */\r | |
1772 | mp_mevff = 1; /* signal dms */\r | |
1773 | ABORT (ABORT_PRO); /* abort */\r | |
1774 | }\r | |
1775 | return;\r | |
1776 | }\r | |
1777 | \r | |
1778 | /* DMS update status */\r | |
1779 | \r | |
1780 | uint32 dms_upd_sr (void)\r | |
1781 | {\r | |
1782 | dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO);\r | |
1783 | if (dms_enb) dms_sr = dms_sr | MST_ENB;\r | |
1784 | if (dms_ump) dms_sr = dms_sr | MST_UMP;\r | |
1785 | if (CTL (PRO)) dms_sr = dms_sr | MST_PRO;\r | |
1786 | return dms_sr;\r | |
1787 | }\r | |
1788 | \r | |
1789 | /* Device 0 (CPU) I/O routine\r | |
1790 | \r | |
1791 | NOTE: LIx/MIx reads floating I/O bus (0 on all machines).\r | |
1792 | \r | |
1793 | NOTE: CLC 0 issues CRS to all devices, not CLC. While most cards react\r | |
1794 | identically to CRS and CLC, some do not, e.g., the 12566B when used as an\r | |
1795 | I/O diagnostic target. PRESET also issues CRS (with POPIO).\r | |
1796 | \r | |
1797 | From Dave Bryan: RTE uses the undocumented instruction "SFS 0,C" to both test\r | |
1798 | and turn off the interrupt system. This is confirmed in the "RTE-6/VM\r | |
1799 | Technical Specifications" manual (HP 92084-90015), section 2.3.1 "Process\r | |
1800 | the Interrupt", subsection "A.1 $CIC":\r | |
1801 | \r | |
1802 | "Test to see if the interrupt system is on or off. This is done with the\r | |
1803 | SFS 0,C instruction. In either case, turn it off (the ,C does it)."\r | |
1804 | \r | |
1805 | ...and in section 5.8, "Parity Error Detection":\r | |
1806 | \r | |
1807 | "Because parity error interrupts can occur even when the interrupt system\r | |
1808 | is off, the code at $CIC must be able to save the complete system status.\r | |
1809 | The major hole in being able to save the complete state is in saving the\r | |
1810 | interrupt system state. In order to do this in both the 21MX and the 21XE\r | |
1811 | the instruction 103300 was used to both test the interrupt system and\r | |
1812 | turn it off."\r | |
1813 | */\r | |
1814 | \r | |
1815 | int32 cpuio (int32 inst, int32 IR, int32 dat)\r | |
1816 | {\r | |
1817 | int i;\r | |
1818 | \r | |
1819 | switch (inst) { /* case on opcode */\r | |
1820 | \r | |
1821 | case ioFLG: /* flag */\r | |
1822 | ion = (IR & I_HC)? 0: 1; /* interrupts off/on */\r | |
1823 | return dat;\r | |
1824 | \r | |
1825 | case ioSFC: /* skip flag clear */\r | |
1826 | if (!ion) PC = (PC + 1) & VAMASK;\r | |
1827 | break;\r | |
1828 | \r | |
1829 | case ioSFS: /* skip flag set */\r | |
1830 | if (ion) PC = (PC + 1) & VAMASK;\r | |
1831 | break;\r | |
1832 | \r | |
1833 | case ioLIX: /* load */\r | |
1834 | dat = 0; /* returns 0 */\r | |
1835 | break;\r | |
1836 | \r | |
1837 | case ioCTL: /* control */\r | |
1838 | if (IR & I_CTL) /* CLC 0 sends CRS */\r | |
1839 | for (i = 0; i <= I_DEVMASK; i++) /* to all devices */\r | |
1840 | devdisp (i, ioCRS, I_CTL + i, 0); /* IR -> "CLC i" for convenience */\r | |
1841 | break;\r | |
1842 | \r | |
1843 | default:\r | |
1844 | break;\r | |
1845 | }\r | |
1846 | \r | |
1847 | if (IR & I_HC) ion = 0; /* HC option */\r | |
1848 | return dat;\r | |
1849 | }\r | |
1850 | \r | |
1851 | /* Device 1 (overflow/S-register) I/O routine\r | |
1852 | \r | |
1853 | NOTE: The S register is read-only on the 2115/2116. It is read/write on\r | |
1854 | the 2114, 2100, and 1000.\r | |
1855 | */\r | |
1856 | \r | |
1857 | int32 ovfio (int32 inst, int32 IR, int32 dat)\r | |
1858 | {\r | |
1859 | switch (inst) { /* case on opcode */\r | |
1860 | \r | |
1861 | case ioFLG: /* flag */\r | |
1862 | O = (IR & I_HC)? 0: 1; /* clear/set overflow */\r | |
1863 | return dat;\r | |
1864 | \r | |
1865 | case ioSFC: /* skip flag clear */\r | |
1866 | if (!O) PC = (PC + 1) & VAMASK;\r | |
1867 | break; /* can clear flag */\r | |
1868 | \r | |
1869 | case ioSFS: /* skip flag set */\r | |
1870 | if (O) PC = (PC + 1) & VAMASK;\r | |
1871 | break; /* can clear flag */\r | |
1872 | \r | |
1873 | case ioMIX: /* merge */\r | |
1874 | dat = dat | SR;\r | |
1875 | break;\r | |
1876 | \r | |
1877 | case ioLIX: /* load */\r | |
1878 | dat = SR;\r | |
1879 | break;\r | |
1880 | \r | |
1881 | case ioOTX: /* output */\r | |
1882 | if ((UNIT_CPU_MODEL != UNIT_2116) &&\r | |
1883 | (UNIT_CPU_MODEL != UNIT_2115))\r | |
1884 | SR = dat;\r | |
1885 | break;\r | |
1886 | \r | |
1887 | default:\r | |
1888 | break;\r | |
1889 | }\r | |
1890 | \r | |
1891 | if (IR & I_HC) O = 0; /* HC option */\r | |
1892 | return dat;\r | |
1893 | }\r | |
1894 | \r | |
1895 | /* Device 4 (power fail) I/O routine */\r | |
1896 | \r | |
1897 | int32 pwrio (int32 inst, int32 IR, int32 dat)\r | |
1898 | {\r | |
1899 | switch (inst) { /* case on opcode */\r | |
1900 | \r | |
1901 | case ioMIX: /* merge */\r | |
1902 | dat = dat | intaddr;\r | |
1903 | break;\r | |
1904 | \r | |
1905 | case ioLIX: /* load */\r | |
1906 | dat = intaddr;\r | |
1907 | break;\r | |
1908 | \r | |
1909 | default:\r | |
1910 | break;\r | |
1911 | }\r | |
1912 | \r | |
1913 | return dat;\r | |
1914 | }\r | |
1915 | \r | |
1916 | /* Device 5 (memory protect) I/O routine\r | |
1917 | \r | |
1918 | From Dave Bryan: Examination of the schematics for the MP card in the\r | |
1919 | engineering documentation shows that the SFS and SFC I/O backplane signals\r | |
1920 | gate the output of the MEVFF onto the SKF line unconditionally.\r | |
1921 | \r | |
1922 | The MEVFF records memory expansion (a.k.a. dynamic mapping) violations. It\r | |
1923 | is set when an DM violation is encountered. It is cleared on POPIO, STC 5,\r | |
1924 | and -HLT * IOGSP * INTPT. The latter occurs when an interrupt causes\r | |
1925 | execution of a non-halt I/O instruction in the interrupt trap cell.\r | |
1926 | */\r | |
1927 | \r | |
1928 | int32 proio (int32 inst, int32 IR, int32 dat)\r | |
1929 | {\r | |
1930 | switch (inst) { /* case on opcode */\r | |
1931 | \r | |
1932 | case ioSFC: /* skip flag clear */\r | |
1933 | if (!mp_mevff) PC = (PC + 1) & VAMASK; /* skip if mem prot */\r | |
1934 | break;\r | |
1935 | \r | |
1936 | case ioSFS: /* skip flag set */\r | |
1937 | if (mp_mevff) PC = (PC + 1) & VAMASK; /* skip if DMS */\r | |
1938 | break;\r | |
1939 | \r | |
1940 | case ioMIX: /* merge */\r | |
1941 | dat = dat | mp_viol;\r | |
1942 | break;\r | |
1943 | \r | |
1944 | case ioLIX: /* load */\r | |
1945 | dat = mp_viol;\r | |
1946 | break;\r | |
1947 | \r | |
1948 | case ioOTX: /* output */\r | |
1949 | mp_fence = dat & VAMASK;\r | |
1950 | if (cpu_unit.flags & UNIT_2100) iop_sp = mp_fence;\r | |
1951 | break;\r | |
1952 | \r | |
1953 | case ioCRS: /* control reset */\r | |
1954 | case ioCTL: /* control clear/set */\r | |
1955 | if ((IR & I_CTL) == 0) { /* STC */\r | |
1956 | setCTL (PRO);\r | |
1957 | dms_vr = 0;\r | |
1958 | mp_evrff = 1; /* allow mp_viol upd */\r | |
1959 | mp_mevff = 0; /* clear DMS flag */\r | |
1960 | }\r | |
1961 | break;\r | |
1962 | \r | |
1963 | default:\r | |
1964 | break;\r | |
1965 | }\r | |
1966 | \r | |
1967 | if (IR & I_HC) { clrFLG (PRO); } /* HC option */\r | |
1968 | return dat;\r | |
1969 | }\r | |
1970 | \r | |
1971 | /* Devices 2,3 (secondary DMA) I/O routine.\r | |
1972 | \r | |
1973 | Implements control word 2 (memory address) and control word 3 (word count).\r | |
1974 | \r | |
1975 | The 12607B (2114) supports 14-bit addresses and 13-bit word counts.\r | |
1976 | The 12578A (2115/6) supports 15-bit addresses and 14-bit word counts.\r | |
1977 | The 12895A (2100) and 12897B (1000) support 15-bit addresses and 16-bit word\r | |
1978 | counts.\r | |
1979 | \r | |
1980 | Note: because the I/O bus floats to zero on 211x computers, LIA/MIA (word\r | |
1981 | count) returns zeros in the unused bit locations, even though the word count\r | |
1982 | is a negative value.\r | |
1983 | */\r | |
1984 | \r | |
1985 | int32 dmsio (int32 inst, int32 IR, int32 dat)\r | |
1986 | {\r | |
1987 | int32 ch;\r | |
1988 | \r | |
1989 | ch = IR & 1; /* get channel num */\r | |
1990 | switch (inst) { /* case on opcode */\r | |
1991 | \r | |
1992 | case ioLIX: /* load remaining word count */\r | |
1993 | dat = 0;\r | |
1994 | \r | |
1995 | case ioMIX: /* merge */\r | |
1996 | if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */\r | |
1997 | dat = dat | (dmac[ch].cw3 & 0017777); /* only 13-bit count */\r | |
1998 | else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2115/2116? */\r | |
1999 | dat = dat | (dmac[ch].cw3 & 0037777); /* only 14-bit count */\r | |
2000 | else\r | |
2001 | dat = dat | dmac[ch].cw3; /* rest use full value */\r | |
2002 | break;\r | |
2003 | \r | |
2004 | case ioOTX: /* output */\r | |
2005 | if (CTL (DMALT0 + ch)) /* word count selected? */\r | |
2006 | dmac[ch].cw3 = dat; /* save count */\r | |
2007 | else /* memory address selected */\r | |
2008 | if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */\r | |
2009 | dmac[ch].cw2 = dat & 0137777; /* 14-bit address */\r | |
2010 | else\r | |
2011 | dmac[ch].cw2 = dat; /* full address stored */\r | |
2012 | break;\r | |
2013 | \r | |
2014 | case ioCRS: /* control reset */\r | |
2015 | case ioCTL: /* control clear/set */\r | |
2016 | if (IR & I_CTL) { clrCTL (DMALT0 + ch); } /* CLC */\r | |
2017 | else { setCTL (DMALT0 + ch); } /* STC */\r | |
2018 | break;\r | |
2019 | \r | |
2020 | default:\r | |
2021 | break;\r | |
2022 | }\r | |
2023 | \r | |
2024 | return dat;\r | |
2025 | }\r | |
2026 | \r | |
2027 | /* Devices 6,7 (primary DMA) I/O routine\r | |
2028 | \r | |
2029 | Implements control word 1 (device address) and DMA control.\r | |
2030 | \r | |
2031 | The 12607B (2114) stores only bits 2-0 of the select code and interprets them\r | |
2032 | as select codes 10-16 (SRQ17 is not decoded). The 12578A (2115/6), 12895A\r | |
2033 | (2100), and 12897B (1000) support the full 10-77 range of select codes.\r | |
2034 | \r | |
2035 | The 12578A supports byte-sized transfers by setting bit 14. Bit 14 is\r | |
2036 | ignored by all other DMA cards, which support word transfers only.\r | |
2037 | \r | |
2038 | NOTE: LIx/MIx reads floating S-bus (1 on 21MX, 0 on 211x/2100).\r | |
2039 | \r | |
2040 | NOTE: CRS clears control and command flip-flops, whereas CLC clears only\r | |
2041 | control.\r | |
2042 | */\r | |
2043 | \r | |
2044 | int32 dmpio (int32 inst, int32 IR, int32 dat)\r | |
2045 | {\r | |
2046 | int32 ch;\r | |
2047 | \r | |
2048 | ch = IR & 1; /* get channel number */\r | |
2049 | \r | |
2050 | switch (inst) { /* case on opcode */\r | |
2051 | \r | |
2052 | case ioFLG: /* flag */\r | |
2053 | if ((IR & I_HC) == 0) { /* set->abort */\r | |
2054 | setFLG (DMA0 + ch); /* set flag */\r | |
2055 | clrCMD (DMA0 + ch); /* clr cmd */\r | |
2056 | }\r | |
2057 | break;\r | |
2058 | \r | |
2059 | case ioSFC: /* skip flag clear */\r | |
2060 | if (FLG (DMA0 + ch) == 0) PC = (PC + 1) & VAMASK;\r | |
2061 | break;\r | |
2062 | \r | |
2063 | case ioSFS: /* skip flag set */\r | |
2064 | if (FLG (DMA0 + ch) != 0) PC = (PC + 1) & VAMASK;\r | |
2065 | break;\r | |
2066 | \r | |
2067 | case ioLIX: /* load */\r | |
2068 | dat = 0;\r | |
2069 | \r | |
2070 | case ioMIX: /* merge */\r | |
2071 | if (UNIT_CPU_TYPE == UNIT_TYPE_1000)\r | |
2072 | dat = DMASK;\r | |
2073 | break;\r | |
2074 | \r | |
2075 | case ioOTX: /* output */\r | |
2076 | if (UNIT_CPU_MODEL == UNIT_2114) /* 12607? */\r | |
2077 | dmac[ch].cw1 = (dat & 0137707) | 010; /* mask SC, convert to 10-17 */\r | |
2078 | else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 12578? */\r | |
2079 | dmac[ch].cw1 = dat; /* store full select code, flags */\r | |
2080 | else /* 12895, 12897 */\r | |
2081 | dmac[ch].cw1 = dat & ~DMA1_PB; /* clip byte-packing flag */\r | |
2082 | break;\r | |
2083 | \r | |
2084 | case ioCRS: /* control reset */\r | |
2085 | clrCMD (DMA0 + ch); /* clear command flip-flop */\r | |
2086 | \r | |
2087 | case ioCTL: /* control */\r | |
2088 | if (IR & I_CTL) { clrCTL (DMA0 + ch); } /* CLC: cmd unchgd */\r | |
2089 | else { /* STC */\r | |
2090 | if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* slow DMA card? */\r | |
2091 | dmac[ch].latency = 1; /* needs startup latency */\r | |
2092 | else\r | |
2093 | dmac[ch].latency = 0; /* DCPC starts immediately */\r | |
2094 | \r | |
2095 | dmac[ch].packer = 0; /* clear packing register */\r | |
2096 | setCTL (DMA0 + ch); /* set ctl, cmd */\r | |
2097 | setCMD (DMA0 + ch);\r | |
2098 | }\r | |
2099 | break;\r | |
2100 | \r | |
2101 | default:\r | |
2102 | break;\r | |
2103 | }\r | |
2104 | \r | |
2105 | if (IR & I_HC) { clrFLG (DMA0 + ch); } /* HC option */\r | |
2106 | return dat;\r | |
2107 | }\r | |
2108 | \r | |
2109 | /* DMA cycle routine\r | |
2110 | \r | |
2111 | The 12578A card supports byte-packing. If bit 14 in control word 1 is set,\r | |
2112 | each transfer will involve one read/write from memory and two output/input\r | |
2113 | operations in order to transfer sequential bytes to/from the device.\r | |
2114 | \r | |
2115 | The last cycle (word count reaches 0) logic is quite tricky.\r | |
2116 | Input cases:\r | |
2117 | - CLC requested: issue CLC\r | |
2118 | Output cases:\r | |
2119 | - neither STC nor CLC requested: issue CLF\r | |
2120 | - STC requested but not CLC: issue STC,C\r | |
2121 | - CLC requested but not STC: issue CLC,C\r | |
2122 | - STC and CLC both requested: issue STC,C and CLC,C, in that order\r | |
2123 | Either: issue EDT (pass DMA channel number and I/O flag)\r | |
2124 | */\r | |
2125 | \r | |
2126 | void dma_cycle (uint32 ch, uint32 map)\r | |
2127 | {\r | |
2128 | int32 temp, dev, MA;\r | |
2129 | int32 inp = dmac[ch].cw2 & DMA2_OI; /* input flag */\r | |
2130 | int32 byt = dmac[ch].cw1 & DMA1_PB; /* pack bytes flag */\r | |
2131 | \r | |
2132 | if (dmac[ch].latency) { /* start-up latency? */\r | |
2133 | dmac[ch].latency = dmac[ch].latency - 1; /* decrease it */\r | |
2134 | return; /* that's all this cycle */\r | |
2135 | }\r | |
2136 | \r | |
2137 | dev = dmac[ch].cw1 & I_DEVMASK; /* get device */\r | |
2138 | MA = dmac[ch].cw2 & VAMASK; /* get mem addr */\r | |
2139 | \r | |
2140 | if (inp) { /* input cycle? */\r | |
2141 | temp = devdisp (dev, ioLIX, dev, 0); /* do LIA dev */\r | |
2142 | \r | |
2143 | if (byt) { /* byte packing? */\r | |
2144 | if (dmac[ch].packer & DMA_OE) { /* second byte? */\r | |
2145 | temp = (dmac[ch].packer << 8) | /* merge stored byte */\r | |
2146 | (temp & DMASK8);\r | |
2147 | WriteIO (MA, temp, map); /* store word data */\r | |
2148 | }\r | |
2149 | else /* first byte */\r | |
2150 | dmac[ch].packer = (temp & DMASK8); /* save it */\r | |
2151 | \r | |
2152 | dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */\r | |
2153 | }\r | |
2154 | else /* no byte packing */\r | |
2155 | WriteIO (MA, temp, map); /* store word data */\r | |
2156 | }\r | |
2157 | else { /* output cycle */\r | |
2158 | if (byt) { /* byte packing? */\r | |
2159 | if (dmac[ch].packer & DMA_OE) /* second byte? */\r | |
2160 | temp = dmac[ch].packer & DMASK8; /* retrieve it */\r | |
2161 | \r | |
2162 | else { /* first byte */\r | |
2163 | dmac[ch].packer = ReadIO (MA, map); /* read word data */\r | |
2164 | temp = (dmac[ch].packer >> 8) & DMASK8; /* get high byte */\r | |
2165 | }\r | |
2166 | \r | |
2167 | dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */\r | |
2168 | }\r | |
2169 | else /* no byte packing */\r | |
2170 | temp = ReadIO (MA, map); /* read word data */\r | |
2171 | \r | |
2172 | devdisp (dev, ioOTX, dev, temp); /* do OTA dev */\r | |
2173 | }\r | |
2174 | \r | |
2175 | if ((dmac[ch].packer & DMA_OE) == 0) { /* new byte or no packing? */\r | |
2176 | dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | /* increment address */\r | |
2177 | ((dmac[ch].cw2 + 1) & VAMASK);\r | |
2178 | dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* increment word count */\r | |
2179 | }\r | |
2180 | \r | |
2181 | if (dmac[ch].cw3) { /* more to do? */\r | |
2182 | if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */\r | |
2183 | devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */\r | |
2184 | else devdisp (dev, ioFLG, I_HC + dev, 0); /* else CLF dev */\r | |
2185 | }\r | |
2186 | else {\r | |
2187 | if (inp) { /* last cycle, input? */\r | |
2188 | if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */\r | |
2189 | devdisp (dev, ioCTL, I_CTL + dev, 0); /* yes */\r | |
2190 | } /* end input */\r | |
2191 | else { /* output */\r | |
2192 | if ((dmac[ch].cw1 & (DMA1_STC | DMA1_CLC)) == 0)\r | |
2193 | devdisp (dev, ioFLG, I_HC + dev, 0); /* clear flag */\r | |
2194 | if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */\r | |
2195 | devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */\r | |
2196 | if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */\r | |
2197 | devdisp (dev, ioCTL, I_HC + I_CTL + dev, 0); /* yes */\r | |
2198 | } /* end output */\r | |
2199 | setFLG (DMA0 + ch); /* set DMA flg */\r | |
2200 | clrCMD (DMA0 + ch); /* clr DMA cmd */\r | |
2201 | devdisp (dev, ioEDT, dev, inp | ch); /* do EDT */\r | |
2202 | }\r | |
2203 | return;\r | |
2204 | }\r | |
2205 | \r | |
2206 | /* Unimplemented device routine\r | |
2207 | \r | |
2208 | NOTE: For SC < 10, LIx/MIx reads floating S-bus (-1 on 21MX, 0 on 211x/2100).\r | |
2209 | For SC >= 10, LIx/MIx reads floating I/O bus (0 on all machines).\r | |
2210 | */\r | |
2211 | \r | |
2212 | int32 nulio (int32 inst, int32 IR, int32 dat)\r | |
2213 | {\r | |
2214 | int32 devd;\r | |
2215 | \r | |
2216 | devd = IR & I_DEVMASK; /* get device no */\r | |
2217 | switch (inst) { /* case on opcode */\r | |
2218 | \r | |
2219 | case ioSFC: /* skip flag clear */\r | |
2220 | PC = (PC + 1) & VAMASK;\r | |
2221 | break;\r | |
2222 | \r | |
2223 | case ioLIX: /* load */\r | |
2224 | dat = 0;\r | |
2225 | \r | |
2226 | case ioMIX: /* merge */\r | |
2227 | if ((devd < VARDEV) && (UNIT_CPU_TYPE == UNIT_TYPE_1000))\r | |
2228 | dat = DMASK;\r | |
2229 | break;\r | |
2230 | \r | |
2231 | default:\r | |
2232 | break;\r | |
2233 | }\r | |
2234 | \r | |
2235 | return (stop_dev << IOT_V_REASON) | dat;\r | |
2236 | }\r | |
2237 | \r | |
2238 | /* Reset routines */\r | |
2239 | \r | |
2240 | t_stat cpu_reset (DEVICE *dptr)\r | |
2241 | {\r | |
2242 | E = 0;\r | |
2243 | O = 0;\r | |
2244 | ion = ion_defer = 0;\r | |
2245 | clrCMD (PWR);\r | |
2246 | clrCTL (PWR);\r | |
2247 | clrFLG (PWR);\r | |
2248 | clrFBF (PWR);\r | |
2249 | dev_srq[0] = dev_srq[0] & ~M_FXDEV;\r | |
2250 | dms_enb = dms_ump = 0; /* init DMS */\r | |
2251 | dms_sr = 0;\r | |
2252 | dms_vr = 0;\r | |
2253 | pcq_r = find_reg ("PCQ", NULL, dptr);\r | |
2254 | sim_brk_types = ALL_BKPTS;\r | |
2255 | sim_brk_dflt = SWMASK ('N'); /* type is nomap as DMS is off */\r | |
2256 | \r | |
2257 | if (M == NULL) { /* initial call? */\r | |
2258 | M = calloc (PASIZE, sizeof (uint16)); /* alloc mem */\r | |
2259 | \r | |
2260 | if (M == NULL) /* alloc fail? */\r | |
2261 | return SCPE_MEM;\r | |
2262 | else { /* do one-time init */\r | |
2263 | MEMSIZE = 32768; /* set initial memory size */\r | |
2264 | cpu_set_model (NULL, UNIT_2116, NULL, NULL); /* set initial CPU model */\r | |
2265 | SR = 001000; /* select PTR boot ROM at SC 10 */\r | |
2266 | cpu_boot (0, NULL); /* install loader for 2116 */\r | |
2267 | cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (was enabled) */\r | |
2268 | SR = 0; /* clear S */\r | |
2269 | sim_vm_post = &hp_post_cmd; /* set cmd post proc */\r | |
2270 | }\r | |
2271 | }\r | |
2272 | \r | |
2273 | if (pcq_r) pcq_r->qptr = 0;\r | |
2274 | else return SCPE_IERR;\r | |
2275 | return SCPE_OK;\r | |
2276 | }\r | |
2277 | \r | |
2278 | t_stat mp_reset (DEVICE *dptr)\r | |
2279 | {\r | |
2280 | clrCTL (PRO);\r | |
2281 | clrFLG (PRO);\r | |
2282 | clrFBF (PRO);\r | |
2283 | mp_fence = 0; /* init mprot */\r | |
2284 | mp_viol = 0;\r | |
2285 | mp_mevff = 0;\r | |
2286 | mp_evrff = 1;\r | |
2287 | return SCPE_OK;\r | |
2288 | }\r | |
2289 | \r | |
2290 | t_stat dma0_reset (DEVICE *tptr)\r | |
2291 | {\r | |
2292 | if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */\r | |
2293 | hp_enbdis_pair (&dma0_dev, &dma1_dev); /* make pair cons */\r | |
2294 | clrCMD (DMA0);\r | |
2295 | clrCTL (DMA0);\r | |
2296 | setFLG (DMA0);\r | |
2297 | clrSRQ (DMA0);\r | |
2298 | clrCTL (DMALT0);\r | |
2299 | dmac[0].latency = dmac[0].packer = 0;\r | |
2300 | if (sim_switches & SWMASK ('P')) /* power up? */\r | |
2301 | dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0;\r | |
2302 | return SCPE_OK;\r | |
2303 | }\r | |
2304 | \r | |
2305 | t_stat dma1_reset (DEVICE *tptr)\r | |
2306 | {\r | |
2307 | if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */\r | |
2308 | hp_enbdis_pair (&dma1_dev, &dma0_dev); /* make pair cons */\r | |
2309 | clrCMD (DMA1);\r | |
2310 | clrCTL (DMA1);\r | |
2311 | setFLG (DMA1);\r | |
2312 | clrSRQ (DMA1);\r | |
2313 | clrCTL (DMALT1);\r | |
2314 | dmac[1].latency = dmac[1].packer = 0;\r | |
2315 | if (sim_switches & SWMASK ('P')) /* power up? */\r | |
2316 | dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0;\r | |
2317 | return SCPE_OK;\r | |
2318 | }\r | |
2319 | \r | |
2320 | /* Memory examine */\r | |
2321 | \r | |
2322 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r | |
2323 | {\r | |
2324 | int32 d;\r | |
2325 | \r | |
2326 | if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */\r | |
2327 | return SCPE_NOFNC; /* command not allowed */\r | |
2328 | addr = dms_cons (addr, sw);\r | |
2329 | if (addr >= MEMSIZE) return SCPE_NXM;\r | |
2330 | if (!(sw & SIM_SW_REST) && (addr == 0)) d = saved_AR;\r | |
2331 | else if (!(sw & SIM_SW_REST) && (addr == 1)) d = saved_BR;\r | |
2332 | else d = M[addr];\r | |
2333 | if (vptr != NULL) *vptr = d & DMASK;\r | |
2334 | return SCPE_OK;\r | |
2335 | }\r | |
2336 | \r | |
2337 | /* Memory deposit */\r | |
2338 | \r | |
2339 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r | |
2340 | {\r | |
2341 | if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */\r | |
2342 | return SCPE_NOFNC; /* command not allowed */\r | |
2343 | addr = dms_cons (addr, sw);\r | |
2344 | if (addr >= MEMSIZE) return SCPE_NXM;\r | |
2345 | if (!(sw & SIM_SW_REST) && (addr == 0)) saved_AR = val & DMASK;\r | |
2346 | else if (!(sw & SIM_SW_REST) && (addr == 1)) saved_BR = val & DMASK;\r | |
2347 | else M[addr] = val & DMASK;\r | |
2348 | return SCPE_OK;\r | |
2349 | }\r | |
2350 | \r | |
2351 | /* Set device number */\r | |
2352 | \r | |
2353 | t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc)\r | |
2354 | {\r | |
2355 | DEVICE *dptr = (DEVICE *) desc;\r | |
2356 | DIB *dibp;\r | |
2357 | int32 i, newdev;\r | |
2358 | t_stat r;\r | |
2359 | \r | |
2360 | if (cptr == NULL) return SCPE_ARG;\r | |
2361 | if ((desc == NULL) || (num > 1)) return SCPE_IERR;\r | |
2362 | dibp = (DIB *) dptr->ctxt;\r | |
2363 | if (dibp == NULL) return SCPE_IERR;\r | |
2364 | newdev = get_uint (cptr, 8, I_DEVMASK - num, &r);\r | |
2365 | if (r != SCPE_OK) return r;\r | |
2366 | if (newdev < VARDEV) return SCPE_ARG;\r | |
2367 | for (i = 0; i <= num; i++, dibp++) dibp->devno = newdev + i;\r | |
2368 | return SCPE_OK;\r | |
2369 | }\r | |
2370 | \r | |
2371 | /* Show device number */\r | |
2372 | \r | |
2373 | t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc)\r | |
2374 | {\r | |
2375 | DEVICE *dptr = (DEVICE *) desc;\r | |
2376 | DIB *dibp;\r | |
2377 | int32 i;\r | |
2378 | \r | |
2379 | if ((desc == NULL) || (num > 1)) return SCPE_IERR;\r | |
2380 | dibp = (DIB *) dptr->ctxt;\r | |
2381 | if (dibp == NULL) return SCPE_IERR;\r | |
2382 | fprintf (st, "devno=%o", dibp->devno);\r | |
2383 | for (i = 1; i <= num; i++) fprintf (st, "/%o", dibp->devno + i);\r | |
2384 | return SCPE_OK;\r | |
2385 | }\r | |
2386 | \r | |
2387 | /* Make a pair of devices consistent */\r | |
2388 | \r | |
2389 | void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp)\r | |
2390 | {\r | |
2391 | if (ccp->flags & DEV_DIS) dcp->flags = dcp->flags | DEV_DIS;\r | |
2392 | else dcp->flags = dcp->flags & ~DEV_DIS;\r | |
2393 | return;\r | |
2394 | }\r | |
2395 | \r | |
2396 | /* VM command post-processor\r | |
2397 | \r | |
2398 | Update T register to contents of memory addressed by M register\r | |
2399 | if M register has changed. */\r | |
2400 | \r | |
2401 | void hp_post_cmd (t_bool from_scp)\r | |
2402 | {\r | |
2403 | if (MR != saved_MR) { /* M changed since last update? */\r | |
2404 | saved_MR = MR;\r | |
2405 | TR = ReadTAB (MR); /* sync T with new M */\r | |
2406 | }\r | |
2407 | return;\r | |
2408 | }\r | |
2409 | \r | |
2410 | /* Test for device conflict */\r | |
2411 | \r | |
2412 | t_bool dev_conflict (void)\r | |
2413 | {\r | |
2414 | DEVICE *dptr;\r | |
2415 | DIB *dibp;\r | |
2416 | uint32 i, j, k;\r | |
2417 | t_bool is_conflict = FALSE;\r | |
2418 | uint32 conflicts[I_DEVMASK + 1] = { 0 };\r | |
2419 | \r | |
2420 | for (i = 0; dptr = sim_devices[i]; i++) {\r | |
2421 | dibp = (DIB *) dptr->ctxt;\r | |
2422 | if (dibp && !(dptr->flags & DEV_DIS))\r | |
2423 | if (++conflicts[dibp->devno] > 1)\r | |
2424 | is_conflict = TRUE;\r | |
2425 | }\r | |
2426 | \r | |
2427 | if (is_conflict) {\r | |
2428 | sim_ttcmd();\r | |
2429 | for (i = 0; i <= I_DEVMASK; i++) {\r | |
2430 | if (conflicts[i] > 1) {\r | |
2431 | k = conflicts[i];\r | |
2432 | printf ("Select code %o conflict:", i);\r | |
2433 | if (sim_log) fprintf (sim_log, "Select code %o conflict:", i);\r | |
2434 | for (j = 0; dptr = sim_devices[j]; j++) {\r | |
2435 | dibp = (DIB *) dptr->ctxt;\r | |
2436 | if (dibp && !(dptr->flags & DEV_DIS) && (i == dibp->devno)) {\r | |
2437 | if (k < conflicts[i]) {\r | |
2438 | printf (" and");\r | |
2439 | if (sim_log) fputs (" and", sim_log);\r | |
2440 | }\r | |
2441 | printf (" %s", sim_dname (dptr));\r | |
2442 | if (sim_log) fprintf (sim_log, " %s", sim_dname (dptr));\r | |
2443 | k = k - 1;\r | |
2444 | if (k == 0) {\r | |
2445 | putchar ('\n');\r | |
2446 | if (sim_log) fputc ('\n', sim_log);\r | |
2447 | break;\r | |
2448 | }\r | |
2449 | }\r | |
2450 | }\r | |
2451 | }\r | |
2452 | }\r | |
2453 | }\r | |
2454 | return is_conflict;\r | |
2455 | }\r | |
2456 | \r | |
2457 | /* Change CPU memory size.\r | |
2458 | \r | |
2459 | On a 21xx, move the current loader to the top of the new memory size. Then\r | |
2460 | clear "non-existent memory" so that reads return zero, per spec.\r | |
2461 | \r | |
2462 | Validation:\r | |
2463 | - New size <= maximum size for current CPU.\r | |
2464 | - New size a positive multiple of 4K (progamming error if not).\r | |
2465 | - If new size < old size, truncation accepted.\r | |
2466 | */\r | |
2467 | \r | |
2468 | t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc)\r | |
2469 | {\r | |
2470 | int32 mc = 0;\r | |
2471 | uint32 i;\r | |
2472 | uint32 model = CPU_MODEL_INDEX; /* current CPU model index */\r | |
2473 | uint32 old_size = MEMSIZE; /* current memory size */\r | |
2474 | \r | |
2475 | if ((uint32) new_size > cpu_features[model].maxmem)\r | |
2476 | return SCPE_NOFNC; /* mem size unsupported */\r | |
2477 | \r | |
2478 | if ((new_size <= 0) || (new_size > PASIZE) || ((new_size & 07777) != 0))\r | |
2479 | return SCPE_NXM; /* invalid size (prog err) */\r | |
2480 | \r | |
2481 | if (!(sim_switches & SWMASK ('F'))) { /* force truncation? */\r | |
2482 | for (i = new_size; i < MEMSIZE; i++) mc = mc | M[i];\r | |
2483 | if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))\r | |
2484 | return SCPE_INCOMP;\r | |
2485 | }\r | |
2486 | \r | |
2487 | if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx CPU? */\r | |
2488 | cpu_set_ldr (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */\r | |
2489 | MEMSIZE = new_size; /* set new memory size */\r | |
2490 | fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */\r | |
2491 | }\r | |
2492 | else /* loader unsupported */\r | |
2493 | fwanxm = MEMSIZE = new_size; /* set new memory size */\r | |
2494 | \r | |
2495 | for (i = fwanxm; i < old_size; i++) M[i] = 0; /* zero non-existent memory */\r | |
2496 | return SCPE_OK;\r | |
2497 | }\r | |
2498 | \r | |
2499 | /* Change CPU models.\r | |
2500 | \r | |
2501 | For convenience, MP and DMA are typically enabled if available; they may be\r | |
2502 | disabled subsequently if desired. Note that the 2114 supports only one DMA\r | |
2503 | channel (channel 0). All other models support two channels.\r | |
2504 | \r | |
2505 | Validation:\r | |
2506 | - Sets standard equipment and convenience features.\r | |
2507 | - Changes DMA device name to DCPC if 1000 is selected.\r | |
2508 | - Enforces maximum memory allowed (doesn't change otherwise).\r | |
2509 | - Disables loader on 21xx machines.\r | |
2510 | */\r | |
2511 | \r | |
2512 | t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc)\r | |
2513 | {\r | |
2514 | uint32 old_family = UNIT_CPU_FAMILY; /* current CPU type */\r | |
2515 | uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */\r | |
2516 | uint32 new_index = new_model >> UNIT_V_CPU; /* new CPU model index */\r | |
2517 | uint32 new_memsize;\r | |
2518 | t_stat result;\r | |
2519 | \r | |
2520 | cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS | /* set typical features */\r | |
2521 | cpu_features[new_index].typ & UNIT_OPTS; /* mask pseudo-opts */\r | |
2522 | \r | |
2523 | \r | |
2524 | if (cpu_features[new_index].typ & UNIT_MP) /* MP in typ config? */\r | |
2525 | mp_dev.flags = mp_dev.flags & ~DEV_DIS; /* enable it */\r | |
2526 | else\r | |
2527 | mp_dev.flags = mp_dev.flags | DEV_DIS; /* disable it */\r | |
2528 | \r | |
2529 | if (cpu_features[new_index].opt & UNIT_MP) /* MP an option? */\r | |
2530 | mp_dev.flags = mp_dev.flags | DEV_DISABLE; /* make it alterable */\r | |
2531 | else\r | |
2532 | mp_dev.flags = mp_dev.flags & ~DEV_DISABLE; /* make it unalterable */\r | |
2533 | \r | |
2534 | \r | |
2535 | if (cpu_features[new_index].typ & UNIT_DMA) { /* DMA in typ config? */\r | |
2536 | dma0_dev.flags = dma0_dev.flags & ~DEV_DIS; /* enable DMA channel 0 */\r | |
2537 | \r | |
2538 | if (new_model == UNIT_2114) /* 2114 has only one channel */\r | |
2539 | dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */\r | |
2540 | else /* all others have two channels */\r | |
2541 | dma1_dev.flags = dma1_dev.flags & ~DEV_DIS; /* enable it */\r | |
2542 | }\r | |
2543 | else {\r | |
2544 | dma0_dev.flags = dma0_dev.flags | DEV_DIS; /* disable channel 0 */\r | |
2545 | dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */\r | |
2546 | }\r | |
2547 | \r | |
2548 | if (cpu_features[new_index].opt & UNIT_DMA) { /* DMA an option? */\r | |
2549 | dma0_dev.flags = dma0_dev.flags | DEV_DISABLE; /* make it alterable */\r | |
2550 | \r | |
2551 | if (new_model == UNIT_2114) /* 2114 has only one channel */\r | |
2552 | dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */\r | |
2553 | else /* all others have two channels */\r | |
2554 | dma1_dev.flags = dma1_dev.flags | DEV_DISABLE; /* make it alterable */\r | |
2555 | }\r | |
2556 | else {\r | |
2557 | dma0_dev.flags = dma0_dev.flags & ~DEV_DISABLE; /* make it unalterable */\r | |
2558 | dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */\r | |
2559 | }\r | |
2560 | \r | |
2561 | \r | |
2562 | if ((old_family == UNIT_FAMILY_1000) && /* if current family is 1000 */\r | |
2563 | (new_family == UNIT_FAMILY_21XX)) { /* and new family is 21xx */\r | |
2564 | deassign_device (&dma0_dev); /* delete DCPC names */\r | |
2565 | deassign_device (&dma1_dev);\r | |
2566 | }\r | |
2567 | else if ((old_family == UNIT_FAMILY_21XX) && /* if current family is 21xx */\r | |
2568 | (new_family == UNIT_FAMILY_1000)) { /* and new family is 1000 */\r | |
2569 | assign_device (&dma0_dev, "DCPC0"); /* change DMA device name */\r | |
2570 | assign_device (&dma1_dev, "DCPC1"); /* to DCPC for familiarity */\r | |
2571 | }\r | |
2572 | \r | |
2573 | if ((MEMSIZE == 0) || /* current mem size not set? */\r | |
2574 | (MEMSIZE > cpu_features[new_index].maxmem)) /* current mem size too large? */\r | |
2575 | new_memsize = cpu_features[new_index].maxmem; /* set it to max supported */\r | |
2576 | else\r | |
2577 | new_memsize = MEMSIZE; /* or leave it unchanged */\r | |
2578 | \r | |
2579 | result = cpu_set_size (uptr, new_memsize, NULL, NULL); /* set memory size */\r | |
2580 | \r | |
2581 | if (result == SCPE_OK) /* memory change OK? */\r | |
2582 | if (new_family == UNIT_FAMILY_21XX) /* 21xx CPU? */\r | |
2583 | fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */\r | |
2584 | else\r | |
2585 | fwanxm = MEMSIZE; /* loader reserved only for 21xx */\r | |
2586 | \r | |
2587 | return result;\r | |
2588 | }\r | |
2589 | \r | |
2590 | /* Display the CPU model and optional loader status.\r | |
2591 | \r | |
2592 | Loader status is displayed for 21xx models and suppressed for 1000 models.\r | |
2593 | */\r | |
2594 | \r | |
2595 | t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
2596 | {\r | |
2597 | fputs ((char *) desc, st); /* write model name */\r | |
2598 | \r | |
2599 | if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* valid only for 21xx */\r | |
2600 | if (fwanxm < MEMSIZE) /* loader area non-existent? */\r | |
2601 | fputs (", loader disabled", st); /* yes, so access disabled */\r | |
2602 | else\r | |
2603 | fputs (", loader enabled", st); /* no, so access enabled */\r | |
2604 | return SCPE_OK;\r | |
2605 | }\r | |
2606 | \r | |
2607 | /* Set a CPU option.\r | |
2608 | \r | |
2609 | Validation:\r | |
2610 | - Checks that the current CPU model supports the option selected.\r | |
2611 | - If CPU is 2100, ensures that FP/FFP and IOP are mutually exclusive.\r | |
2612 | - If CPU is 2100, ensures that FP is enabled if FFP enabled\r | |
2613 | (FP is required for FFP installation).\r | |
2614 | */\r | |
2615 | \r | |
2616 | t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc)\r | |
2617 | {\r | |
2618 | uint32 model = CPU_MODEL_INDEX; /* current CPU model index */\r | |
2619 | \r | |
2620 | if ((cpu_features[model].opt & option) == 0) /* option supported? */\r | |
2621 | return SCPE_NOFNC; /* no */\r | |
2622 | \r | |
2623 | if (UNIT_CPU_TYPE == UNIT_TYPE_2100) {\r | |
2624 | if ((option == UNIT_FP) || (option == UNIT_FFP)) /* 2100 IOP and FP/FFP options */\r | |
2625 | uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */\r | |
2626 | else if (option == UNIT_IOP)\r | |
2627 | uptr->flags = uptr->flags & ~(UNIT_FP | UNIT_FFP);\r | |
2628 | \r | |
2629 | if (option == UNIT_FFP) /* 2100 FFP option requires FP */\r | |
2630 | uptr->flags = uptr->flags | UNIT_FP;\r | |
2631 | }\r | |
2632 | \r | |
2633 | return SCPE_OK;\r | |
2634 | }\r | |
2635 | \r | |
2636 | /* Clear a CPU option.\r | |
2637 | \r | |
2638 | Validation:\r | |
2639 | - Checks that the current CPU model supports the option selected.\r | |
2640 | - Clears flag from unit structure (we are processing MTAB_XTD entries).\r | |
2641 | - If CPU is 2100, ensures that FFP is disabled if FP disabled\r | |
2642 | (FP is required for FFP installation).\r | |
2643 | */\r | |
2644 | \r | |
2645 | t_bool cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc)\r | |
2646 | {\r | |
2647 | uint32 model = CPU_MODEL_INDEX; /* current CPU model index */\r | |
2648 | \r | |
2649 | if ((cpu_features[model].opt & option) == 0) /* option supported? */\r | |
2650 | return SCPE_NOFNC; /* no */\r | |
2651 | \r | |
2652 | uptr->flags = uptr->flags & ~option; /* disable option */\r | |
2653 | \r | |
2654 | if ((UNIT_CPU_TYPE == UNIT_TYPE_2100) && /* disabling 2100 FP? */\r | |
2655 | (option == UNIT_FP))\r | |
2656 | uptr->flags = uptr->flags & ~UNIT_FFP; /* yes, so disable FFP too */\r | |
2657 | \r | |
2658 | return SCPE_OK;\r | |
2659 | }\r | |
2660 | \r | |
2661 | /* 21xx loader enable/disable function.\r | |
2662 | \r | |
2663 | The 21xx CPUs store their initial binary loaders in the last 64 words of\r | |
2664 | available memory. This memory is protected by a LOADER ENABLE switch on the\r | |
2665 | front panel. When the switch is off (disabled), main memory effectively ends\r | |
2666 | 64 locations earlier, i.e., the loader area is treated as non-existent.\r | |
2667 | Because these are core machines, the loader is retained when system power is\r | |
2668 | off.\r | |
2669 | \r | |
2670 | 1000 CPUs do not have a protected loader feature. Instead, loaders are\r | |
2671 | stored in PROMs and are copied into main memory for execution by the IBL\r | |
2672 | switch.\r | |
2673 | \r | |
2674 | Under simulation, we keep both a total configured memory size (MEMSIZE) and a\r | |
2675 | current configured memory size (fwanxm = "first word address of non-existent\r | |
2676 | memory). When the two are equal, the loader is enabled. When the current\r | |
2677 | size is less than the total size, the loader is disabled.\r | |
2678 | \r | |
2679 | Disabling the loader copies the last 64 words to a shadow array, zeros the\r | |
2680 | corresponding memory, and decreases the last word of addressable memory by\r | |
2681 | 64. Enabling the loader reverses this process.\r | |
2682 | \r | |
2683 | Disabling may be done manually by user command or automatically when a halt\r | |
2684 | instruction is executed. Enabling occurs only by user command. This differs\r | |
2685 | slightly from actual machine operation, which additionally disables the\r | |
2686 | loader when a manual halt is performed. We do not do this to allow\r | |
2687 | breakpoints within and single-stepping through the loaders.\r | |
2688 | */\r | |
2689 | \r | |
2690 | t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc)\r | |
2691 | {\r | |
2692 | static uint16 loader[IBL_LNT];\r | |
2693 | int32 i;\r | |
2694 | t_bool is_enabled = (fwanxm == MEMSIZE);\r | |
2695 | \r | |
2696 | if ((UNIT_CPU_FAMILY != UNIT_FAMILY_21XX) || /* valid only for 21xx */\r | |
2697 | (MEMSIZE == 0)) /* and for initialized memory */\r | |
2698 | return SCPE_NOFNC;\r | |
2699 | \r | |
2700 | if (is_enabled && (enable == 0)) { /* disable loader? */\r | |
2701 | fwanxm = MEMSIZE - IBL_LNT; /* decrease available memory */\r | |
2702 | for (i = 0; i < IBL_LNT; i++) { /* copy loader */\r | |
2703 | loader[i] = M[fwanxm + i]; /* from memory */\r | |
2704 | M[fwanxm + i] = 0; /* and zero location */\r | |
2705 | }\r | |
2706 | }\r | |
2707 | \r | |
2708 | else if ((!is_enabled) && (enable == 1)) { /* enable loader? */\r | |
2709 | for (i = 0; i < IBL_LNT; i++) /* copy loader */\r | |
2710 | M[fwanxm + i] = loader[i]; /* to memory */\r | |
2711 | fwanxm = MEMSIZE; /* increase available memory */\r | |
2712 | }\r | |
2713 | \r | |
2714 | return SCPE_OK;\r | |
2715 | }\r | |
2716 | \r | |
2717 | /* IBL routine (CPU boot) */\r | |
2718 | \r | |
2719 | t_stat cpu_boot (int32 unitno, DEVICE *dptr)\r | |
2720 | {\r | |
2721 | extern const uint16 ptr_rom[IBL_LNT], dq_rom[IBL_LNT];\r | |
2722 | extern const uint16 ms_rom[IBL_LNT], ds_rom[IBL_LNT];\r | |
2723 | int32 dev = (SR >> IBL_V_DEV) & I_DEVMASK;\r | |
2724 | int32 sel = (SR >> IBL_V_SEL) & IBL_M_SEL;\r | |
2725 | \r | |
2726 | if (dev < 010) return SCPE_NOFNC;\r | |
2727 | switch (sel) {\r | |
2728 | \r | |
2729 | case 0: /* PTR boot */\r | |
2730 | ibl_copy (ptr_rom, dev);\r | |
2731 | break;\r | |
2732 | \r | |
2733 | case 1: /* DP/DQ boot */\r | |
2734 | ibl_copy (dq_rom, dev);\r | |
2735 | break;\r | |
2736 | \r | |
2737 | case 2: /* MS boot */\r | |
2738 | ibl_copy (ms_rom, dev);\r | |
2739 | break;\r | |
2740 | \r | |
2741 | case 3: /* DS boot */\r | |
2742 | ibl_copy (ds_rom,dev);\r | |
2743 | break;\r | |
2744 | }\r | |
2745 | \r | |
2746 | return SCPE_OK;\r | |
2747 | }\r | |
2748 | \r | |
2749 | /* IBL boot ROM copy\r | |
2750 | \r | |
2751 | - Use memory size to set the initial PC and base of the boot area\r | |
2752 | - Copy boot ROM to memory, updating I/O instructions\r | |
2753 | - Place 2's complement of boot base in last location\r | |
2754 | \r | |
2755 | Notes:\r | |
2756 | - SR settings are done by the caller\r | |
2757 | - Boot ROM's must be assembled with a device code of 10 (10 and 11 for\r | |
2758 | devices requiring two codes)\r | |
2759 | */\r | |
2760 | \r | |
2761 | t_stat ibl_copy (const uint16 pboot[IBL_LNT], int32 dev)\r | |
2762 | {\r | |
2763 | int32 i;\r | |
2764 | uint16 wd;\r | |
2765 | \r | |
2766 | cpu_set_ldr (NULL, TRUE, NULL, NULL); /* enable loader (ignore errors) */\r | |
2767 | \r | |
2768 | if (dev < 010) return SCPE_ARG; /* valid device? */\r | |
2769 | PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */\r | |
2770 | for (i = 0; i < IBL_LNT; i++) { /* copy bootstrap */\r | |
2771 | wd = pboot[i]; /* get word */\r | |
2772 | if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */\r | |
2773 | ((wd & I_DEVMASK) >= 010) && /* dev >= 10? */\r | |
2774 | (I_GETIOOP (wd) != ioHLT)) /* not a HALT? */\r | |
2775 | M[PC + i] = (wd + (dev - 010)) & DMASK; /* change dev code */\r | |
2776 | else M[PC + i] = wd; /* leave unchanged */\r | |
2777 | }\r | |
2778 | M[PC + IBL_DPC] = (M[PC + IBL_DPC] + (dev - 010)) & DMASK; /* patch DMA ctrl */\r | |
2779 | M[PC + IBL_END] = (~PC + 1) & DMASK; /* fill in start of boot */\r | |
2780 | return SCPE_OK;\r | |
2781 | }\r |