| 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 |