| 1 | /* nova_cpu.c: NOVA 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 Nova central processor\r |
| 27 | \r |
| 28 | 04-Jul-07 BKR DEV_SET/CLR macros now used,\r |
| 29 | support for non-existant devices added\r |
| 30 | CPU bootstrap code warning: high-speed devices may not boot properly,\r |
| 31 | execution history facility added,\r |
| 32 | documented Nova 3 secret LDB/STB/SAVN behavior,\r |
| 33 | added support for secret Nova 3 LDB/STB/SAVN substitute actions,\r |
| 34 | 'ind_max' changed from 16 to 65536 for better unmapped system compatibility,\r |
| 35 | INT_TRAP added for Nova 3, 4 trap instruction handling,\r |
| 36 | 28-Apr-07 RMS Removed clock initialization\r |
| 37 | 06-Feb-06 RMS Fixed bug in DIVS (found by Mark Hittinger)\r |
| 38 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r |
| 39 | 25-Aug-05 RMS Fixed DIVS case 2^31 / - 1\r |
| 40 | 14-Jan-04 RMS Fixed device enable/disable support (found by Bruce Ray)\r |
| 41 | 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev Kit conflict\r |
| 42 | 03-Oct-02 RMS Added DIB infrastructure\r |
| 43 | 30-Dec-01 RMS Added old PC queue\r |
| 44 | 07-Dec-01 RMS Revised to use breakpoint package\r |
| 45 | 30-Nov-01 RMS Added extended SET/SHOW support\r |
| 46 | 10-Aug-01 RMS Removed register in declarations\r |
| 47 | 17-Jul-01 RMS Moved function prototype\r |
| 48 | 26-Apr-01 RMS Added device enable/disable support\r |
| 49 | 05-Mar-01 RMS Added clock calibration\r |
| 50 | 22-Dec-00 RMS Added Bruce Ray's second terminal\r |
| 51 | 15-Dec-00 RMS Added Charles Owen's CPU bootstrap\r |
| 52 | 08-Dec-00 RMS Changes from Bruce Ray\r |
| 53 | -- fixed trap test to include Nova 3\r |
| 54 | -- fixed DIV and DIVS divide by 0\r |
| 55 | -- fixed RETN to set SP from FP\r |
| 56 | -- fixed IORST to preserve carry\r |
| 57 | -- added "secret" Nova 4 PSHN/SAVEN instructions\r |
| 58 | -- added plotter support\r |
| 59 | 15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions\r |
| 60 | 14-Apr-98 RMS Changed t_addr to unsigned\r |
| 61 | 15-Sep-97 RMS Added read and write breakpoints\r |
| 62 | \r |
| 63 | The register state for the NOVA CPU is:\r |
| 64 | \r |
| 65 | AC[0:3]<0:15> general registers\r |
| 66 | C carry flag\r |
| 67 | PC<0:14> program counter\r |
| 68 | \r |
| 69 | The NOVA has three instruction formats: memory reference, I/O transfer,\r |
| 70 | and operate. The memory reference format is:\r |
| 71 | \r |
| 72 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r |
| 73 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 74 | | 0| op | AC |in| mode| displacement | memory reference\r |
| 75 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 76 | \r |
| 77 | <0:4> mnemonic action\r |
| 78 | \r |
| 79 | 00000 JMP PC = MA\r |
| 80 | 00001 JMS AC3 = PC, PC = MA\r |
| 81 | 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0\r |
| 82 | 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0\r |
| 83 | 001'n LDA ACn = M[MA]\r |
| 84 | 010'n STA M[MA] = ACn\r |
| 85 | \r |
| 86 | <5:7> mode action\r |
| 87 | \r |
| 88 | 000 page zero direct MA = zext (IR<8:15>)\r |
| 89 | 001 PC relative direct MA = PC + sext (IR<8:15>)\r |
| 90 | 010 AC2 relative direct MA = AC2 + sext (IR<8:15>)\r |
| 91 | 011 AC3 relative direct MA = AC3 + sext (IR<8:15>)\r |
| 92 | 100 page zero indirect MA = M[zext (IR<8:15>)]\r |
| 93 | 101 PC relative indirect MA = M[PC + sext (IR<8:15>)]\r |
| 94 | 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)]\r |
| 95 | 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)]\r |
| 96 | \r |
| 97 | Memory reference instructions can access an address space of 32K words.\r |
| 98 | An instruction can directly reference the first 256 words of memory\r |
| 99 | (called page zero), as well as 256 words relative to the PC, AC2, or\r |
| 100 | AC3; it can indirectly access all 32K words. If an indirect address\r |
| 101 | is in locations 00020-00027, the indirect address is incremented and\r |
| 102 | rewritten to memory before use; if in 00030-00037, decremented and\r |
| 103 | rewritten.\r |
| 104 | \r |
| 105 | The I/O transfer format is:\r |
| 106 | \r |
| 107 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r |
| 108 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 109 | | 0 1 1| AC | opcode |pulse| device | I/O transfer\r |
| 110 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 111 | \r |
| 112 | The IOT instruction sends the opcode, pulse, and specified AC to the\r |
| 113 | specified I/O device. The device may accept data, provide data,\r |
| 114 | initiate or cancel operations, or skip on status.\r |
| 115 | \r |
| 116 | The operate format is:\r |
| 117 | \r |
| 118 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\r |
| 119 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 120 | | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate\r |
| 121 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r |
| 122 | \______/ \___/ \___/ | | | |\r |
| 123 | | | | | | | +--- reverse skip sense\r |
| 124 | | | | | | +--- skip if C == 0\r |
| 125 | | | | | +--- skip if result == 0\r |
| 126 | | | | +--- don't load result\r |
| 127 | | | +--- carry in (load as is,\r |
| 128 | | | set to Zero,\r |
| 129 | | | set to One,\r |
| 130 | | | load Complement)\r |
| 131 | | +--- shift (none,\r |
| 132 | | left one,\r |
| 133 | | right one,\r |
| 134 | | byte swap)\r |
| 135 | +--- operation (complement,\r |
| 136 | negate,\r |
| 137 | move,\r |
| 138 | increment,\r |
| 139 | add complement,\r |
| 140 | subtract,\r |
| 141 | add,\r |
| 142 | and)\r |
| 143 | \r |
| 144 | The operate instruction can be microprogrammed to perform operations\r |
| 145 | on the source and destination AC's and the Carry flag.\r |
| 146 | \r |
| 147 | Some notes from Bruce Ray:\r |
| 148 | \r |
| 149 | 1. DG uses the value of the autoindex location -before- the\r |
| 150 | modification to determine if additional indirect address\r |
| 151 | levels are to be performed. Most DG emulators conform to\r |
| 152 | this standard, but some vendor machines (i.e. Point 4 Mark 8)\r |
| 153 | do not.\r |
| 154 | \r |
| 155 | 2. Infinite indirect references may occur on unmapped systems\r |
| 156 | and can "hang" the hardware. Some DG diagnostics perform\r |
| 157 | 10,000s of references during a single instruction.\r |
| 158 | \r |
| 159 | 3. Nova 3 adds the following instructions to the standard Nova\r |
| 160 | instruction set:\r |
| 161 | \r |
| 162 | trap instructions\r |
| 163 | stack push/pop instructions\r |
| 164 | save/return instructions\r |
| 165 | stack register manipulation instructions\r |
| 166 | unsigned MUL/DIV\r |
| 167 | \r |
| 168 | 4. Nova 4 adds the following instructions to the Nova 3 instruction\r |
| 169 | set:\r |
| 170 | \r |
| 171 | signed MUL/DIV\r |
| 172 | load/store byte\r |
| 173 | secret (undocumented) stack instructions [PSHN, SAVN]\r |
| 174 | \r |
| 175 | 5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the\r |
| 176 | same instruction code values on all machines.\r |
| 177 | \r |
| 178 | 6. Undocumented Nova 3 behaviour for LDB, STB and SAVN has been\r |
| 179 | added to appropriate code.\r |
| 180 | \r |
| 181 | 7. Most 3rd party vendors had a user-controlled method to increase the\r |
| 182 | logical address space from 32 KW to 64 KW. This capability came at\r |
| 183 | the expense of disabling multi-level indirect addressing when the 64KW\r |
| 184 | mode is in effect, and keeping DG multi-level indirect compatibility\r |
| 185 | when 64KW mode is inactive. The most common implementation was to use\r |
| 186 | an "NIOP <ac>,CPU" instruction to control whether 32 KW or 64 KW\r |
| 187 | addressing mode was wanted, and <ac> bit 15 (the least-significant bit\r |
| 188 | of an accumulator) determined which mode was set:\r |
| 189 | 0 = 32 KW (DG compatible), 1 = 64 KW.\r |
| 190 | \r |
| 191 | This feature has been implemented in our Nova emulation for all to enjoy.\r |
| 192 | \r |
| 193 | \r |
| 194 | This routine is the instruction decode routine for the NOVA.\r |
| 195 | It is called from the simulator control program to execute\r |
| 196 | instructions in simulated memory, starting at the simulated PC.\r |
| 197 | It runs until 'reason' is set non-zero.\r |
| 198 | \r |
| 199 | General notes:\r |
| 200 | \r |
| 201 | 1. Reasons to stop. The simulator can be stopped by:\r |
| 202 | \r |
| 203 | HALT instruction\r |
| 204 | breakpoint encountered\r |
| 205 | infinite indirection loop\r |
| 206 | unknown I/O device and STOP_DEV flag set\r |
| 207 | I/O error in I/O simulator\r |
| 208 | \r |
| 209 | 2. Interrupts. Interrupts are maintained by four parallel variables:\r |
| 210 | \r |
| 211 | dev_done device done flags\r |
| 212 | dev_disable device interrupt disable flags\r |
| 213 | dev_busy device busy flags\r |
| 214 | int_req interrupt requests\r |
| 215 | \r |
| 216 | In addition, int_req contains the interrupt enable and ION pending\r |
| 217 | flags. If ION and ION pending are set, and at least one interrupt\r |
| 218 | request is pending, then an interrupt occurs. Note that the 16b PIO\r |
| 219 | mask must be mapped to the simulator's device bit mapping.\r |
| 220 | \r |
| 221 | 3. Non-existent memory. On the NOVA, reads to non-existent memory\r |
| 222 | return zero, and writes are ignored. In the simulator, the\r |
| 223 | largest possible memory is instantiated and initialized to zero.\r |
| 224 | Thus, only writes need be checked against actual memory size.\r |
| 225 | \r |
| 226 | 4. Adding I/O devices. These modules must be modified:\r |
| 227 | \r |
| 228 | nova_defs.h add interrupt request definition\r |
| 229 | nova_sys.c add sim_devices entry\r |
| 230 | */\r |
| 231 | \r |
| 232 | #include "nova_defs.h"\r |
| 233 | \r |
| 234 | \r |
| 235 | #define PCQ_SIZE 64 /* must be 2**n */\r |
| 236 | #define PCQ_MASK (PCQ_SIZE - 1)\r |
| 237 | #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC\r |
| 238 | \r |
| 239 | \r |
| 240 | #define INCA(x) (((x) + 1) & AMASK)\r |
| 241 | #define DECA(x) (((x) - 1) & AMASK)\r |
| 242 | #define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x))\r |
| 243 | #define STK_CHECK(x,y) if (((x) & 0377) < (y)) int_req = int_req | INT_STK\r |
| 244 | #define IND_STEP(x) M[x] & A_IND; /* return next level indicator */ \\r |
| 245 | if ( ((x) <= AUTO_TOP) && ((x) >= AUTO_INC) ) \\r |
| 246 | if ( (x) < AUTO_DEC ) \\r |
| 247 | M[x] = (M[x] + 1) & DMASK; \\r |
| 248 | else \\r |
| 249 | M[x] = (M[x] - 1) & DMASK; \\r |
| 250 | x = M[x] & AMASK\r |
| 251 | \r |
| 252 | #define INCREMENT_PC PC = (PC + 1) & AMASK /* increment PC */\r |
| 253 | \r |
| 254 | #define UNIT_V_MDV (UNIT_V_UF + 0) /* MDV present */\r |
| 255 | #define UNIT_V_STK (UNIT_V_UF + 1) /* stack instr */\r |
| 256 | #define UNIT_V_BYT (UNIT_V_UF + 2) /* byte instr */\r |
| 257 | #define UNIT_V_64KW (UNIT_V_UF + 3) /* 64KW mem support */\r |
| 258 | #define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */\r |
| 259 | #define UNIT_MDV (1 << UNIT_V_MDV)\r |
| 260 | #define UNIT_STK (1 << UNIT_V_STK)\r |
| 261 | #define UNIT_BYT (1 << UNIT_V_BYT)\r |
| 262 | #define UNIT_64KW (1 << UNIT_V_64KW)\r |
| 263 | #define UNIT_MSIZE (1 << UNIT_V_MSIZE)\r |
| 264 | #define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT | UNIT_64KW)\r |
| 265 | #define UNIT_NOVA3 (UNIT_MDV | UNIT_STK)\r |
| 266 | #define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT)\r |
| 267 | #define UNIT_KERONIX (UNIT_MDV | UNIT_64KW)\r |
| 268 | \r |
| 269 | #define MODE_64K (cpu_unit.flags & UNIT_64KW)\r |
| 270 | #define MODE_64K_ACTIVE ((cpu_unit.flags & UNIT_64KW) && (0xFFFF == AMASK))\r |
| 271 | \r |
| 272 | \r |
| 273 | typedef struct\r |
| 274 | {\r |
| 275 | int32 pc;\r |
| 276 | int16 ir;\r |
| 277 | int16 ac0 ;\r |
| 278 | int16 ac1 ;\r |
| 279 | int16 ac2 ;\r |
| 280 | int16 ac3 ;\r |
| 281 | int16 carry ;\r |
| 282 | int16 sp ;\r |
| 283 | int16 fp ;\r |
| 284 | int32 devDone ;\r |
| 285 | int32 devBusy ;\r |
| 286 | int32 devDisable ;\r |
| 287 | int32 devIntr ;\r |
| 288 | } Hist_entry ;\r |
| 289 | \r |
| 290 | \r |
| 291 | uint16 M[MAXMEMSIZE] = { 0 }; /* memory */\r |
| 292 | int32 AC[4] = { 0 }; /* accumulators */\r |
| 293 | int32 C = 0; /* carry flag */\r |
| 294 | int32 saved_PC = 0; /* program counter */\r |
| 295 | int32 SP = 0; /* stack pointer */\r |
| 296 | int32 FP = 0; /* frame pointer */\r |
| 297 | int32 SR = 0; /* switch register */\r |
| 298 | int32 dev_done = 0; /* device done flags */\r |
| 299 | int32 dev_busy = 0; /* device busy flags */\r |
| 300 | int32 dev_disable = 0; /* int disable flags */\r |
| 301 | int32 int_req = 0; /* interrupt requests */\r |
| 302 | int32 pimask = 0; /* priority int mask */\r |
| 303 | int32 pwr_low = 0; /* power fail flag */\r |
| 304 | int32 ind_max = 65536; /* iadr nest limit */\r |
| 305 | int32 stop_dev = 0; /* stop on ill dev */\r |
| 306 | uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r |
| 307 | int32 pcq_p = 0; /* PC queue ptr */\r |
| 308 | REG *pcq_r = NULL; /* PC queue reg ptr */\r |
| 309 | struct ndev dev_table[64]; /* dispatch table */\r |
| 310 | int32 AMASK = 077777 ; /* current memory address mask */\r |
| 311 | /* (default to 32KW) */\r |
| 312 | static int32 hist_p = 0 ; /* history pointer */\r |
| 313 | static int32 hist_cnt = 0 ; /* history count */\r |
| 314 | static Hist_entry * hist = NULL ; /* instruction history */\r |
| 315 | \r |
| 316 | \r |
| 317 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r |
| 318 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r |
| 319 | t_stat cpu_reset (DEVICE *dptr);\r |
| 320 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 321 | t_stat cpu_boot (int32 unitno, DEVICE *dptr);\r |
| 322 | t_stat build_devtab (void);\r |
| 323 | \r |
| 324 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) ;\r |
| 325 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) ;\r |
| 326 | static int hist_save( int32 pc, int32 our_ir ) ;\r |
| 327 | char * devBitNames( int32 flags, char * ptr, char * sepStr ) ;\r |
| 328 | \r |
| 329 | void mask_out (int32 mask);\r |
| 330 | \r |
| 331 | \r |
| 332 | extern int32 sim_interval;\r |
| 333 | extern int32 sim_int_char;\r |
| 334 | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r |
| 335 | extern DEVICE * sim_devices[];\r |
| 336 | extern t_stat fprint_sym(FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw);\r |
| 337 | \r |
| 338 | \r |
| 339 | \r |
| 340 | /* CPU data structures\r |
| 341 | \r |
| 342 | cpu_dev CPU device descriptor\r |
| 343 | cpu_unit CPU unit descriptor\r |
| 344 | cpu_reg CPU register list\r |
| 345 | cpu_mod CPU modifiers list\r |
| 346 | */\r |
| 347 | \r |
| 348 | UNIT cpu_unit = {\r |
| 349 | UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_MDV, DFTMEMSIZE /* MAXMEMSIZE */ )\r |
| 350 | };\r |
| 351 | \r |
| 352 | REG cpu_reg[] = {\r |
| 353 | { ORDATA (PC, saved_PC, 15) },\r |
| 354 | { ORDATA (AC0, AC[0], 16) },\r |
| 355 | { ORDATA (AC1, AC[1], 16) },\r |
| 356 | { ORDATA (AC2, AC[2], 16) },\r |
| 357 | { ORDATA (AC3, AC[3], 16) },\r |
| 358 | { FLDATA (C, C, 16) },\r |
| 359 | { ORDATA (SP, SP, 16) },\r |
| 360 | { ORDATA (FP, FP, 16) },\r |
| 361 | { ORDATA (SR, SR, 16) },\r |
| 362 | { ORDATA (PI, pimask, 16) },\r |
| 363 | { FLDATA (ION, int_req, INT_V_ION) },\r |
| 364 | { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) },\r |
| 365 | { FLDATA (STKOVF, int_req, INT_V_STK) },\r |
| 366 | { FLDATA (PWR, pwr_low, 0) },\r |
| 367 | { ORDATA (INT, int_req, INT_V_ION+1), REG_RO },\r |
| 368 | { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO },\r |
| 369 | { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO },\r |
| 370 | { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO },\r |
| 371 | { FLDATA (STOP_DEV, stop_dev, 0) },\r |
| 372 | { DRDATA (INDMAX, ind_max, 32), REG_NZ + PV_LEFT },\r |
| 373 | { ORDATA (AMASK, AMASK, 16) },\r |
| 374 | { DRDATA (MEMSIZE, cpu_unit.capac, 32), REG_NZ + PV_LEFT },\r |
| 375 | { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC },\r |
| 376 | { ORDATA (PCQP, pcq_p, 6), REG_HRO },\r |
| 377 | { ORDATA (WRU, sim_int_char, 8) },\r |
| 378 | { NULL }\r |
| 379 | };\r |
| 380 | \r |
| 381 | MTAB cpu_mod[] = {\r |
| 382 | { UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL },\r |
| 383 | { UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL },\r |
| 384 | { UNIT_IOPT, UNIT_KERONIX, "KERONIX", "KERONIX", NULL },\r |
| 385 | { UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL },\r |
| 386 | { UNIT_IOPT, UNIT_64KW, "EXT64KW", "EXT64KW", NULL },\r |
| 387 | { UNIT_IOPT, 0, "none", "NONE", NULL },\r |
| 388 | { UNIT_MSIZE, ( 4 * 1024), NULL, "4K", &cpu_set_size },\r |
| 389 | { UNIT_MSIZE, ( 8 * 1024), NULL, "8K", &cpu_set_size },\r |
| 390 | { UNIT_MSIZE, (12 * 1024), NULL, "12K", &cpu_set_size },\r |
| 391 | { UNIT_MSIZE, (16 * 1024), NULL, "16K", &cpu_set_size },\r |
| 392 | { UNIT_MSIZE, (20 * 1024), NULL, "20K", &cpu_set_size },\r |
| 393 | { UNIT_MSIZE, (24 * 1024), NULL, "24K", &cpu_set_size },\r |
| 394 | { UNIT_MSIZE, (28 * 1024), NULL, "28K", &cpu_set_size },\r |
| 395 | { UNIT_MSIZE, (32 * 1024), NULL, "32K", &cpu_set_size },\r |
| 396 | \r |
| 397 | { UNIT_MSIZE, (36 * 1024), NULL, "36K", &cpu_set_size },\r |
| 398 | { UNIT_MSIZE, (40 * 1024), NULL, "40K", &cpu_set_size },\r |
| 399 | { UNIT_MSIZE, (44 * 1024), NULL, "44K", &cpu_set_size },\r |
| 400 | { UNIT_MSIZE, (48 * 1024), NULL, "48K", &cpu_set_size },\r |
| 401 | { UNIT_MSIZE, (52 * 1024), NULL, "52K", &cpu_set_size },\r |
| 402 | { UNIT_MSIZE, (56 * 1024), NULL, "56K", &cpu_set_size },\r |
| 403 | { UNIT_MSIZE, (60 * 1024), NULL, "60K", &cpu_set_size },\r |
| 404 | { UNIT_MSIZE, (64 * 1024), NULL, "64K", &cpu_set_size },\r |
| 405 | { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",\r |
| 406 | &hist_set, &hist_show },\r |
| 407 | \r |
| 408 | { 0 }\r |
| 409 | };\r |
| 410 | \r |
| 411 | DEVICE cpu_dev = {\r |
| 412 | "CPU", &cpu_unit, cpu_reg, cpu_mod,\r |
| 413 | 1, 8, 16 /* = 64 KW, 15 = 32KW */, 1, 8, 16,\r |
| 414 | &cpu_ex, &cpu_dep, &cpu_reset,\r |
| 415 | NULL, NULL, NULL\r |
| 416 | };\r |
| 417 | \r |
| 418 | t_stat sim_instr (void)\r |
| 419 | {\r |
| 420 | int32 PC, IR, i;\r |
| 421 | t_stat reason;\r |
| 422 | \r |
| 423 | /* Restore register state */\r |
| 424 | \r |
| 425 | if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */\r |
| 426 | PC = saved_PC & AMASK; /* load local PC */\r |
| 427 | C = C & CBIT;\r |
| 428 | mask_out (pimask); /* reset int system */\r |
| 429 | reason = 0;\r |
| 430 | \r |
| 431 | /* Main instruction fetch/decode loop */\r |
| 432 | \r |
| 433 | while (reason == 0) { /* loop until halted */\r |
| 434 | \r |
| 435 | if (sim_interval <= 0) { /* check clock queue */\r |
| 436 | if ( (reason = sim_process_event ()) ) break;\r |
| 437 | }\r |
| 438 | \r |
| 439 | if (int_req > INT_PENDING) { /* interrupt or exception? */\r |
| 440 | int32 MA, indf;\r |
| 441 | \r |
| 442 | if (int_req & INT_TRAP) { /* trap instruction? */\r |
| 443 | int_req = int_req & ~INT_TRAP ; /* clear */\r |
| 444 | PCQ_ENTRY; /* save old PC */\r |
| 445 | M[TRP_SAV] = (PC - 1) & AMASK;\r |
| 446 | MA = TRP_JMP; /* jmp @47 */\r |
| 447 | }\r |
| 448 | else {\r |
| 449 | int_req = int_req & ~INT_ION; /* intr off */\r |
| 450 | PCQ_ENTRY; /* save old PC */\r |
| 451 | M[INT_SAV] = PC;\r |
| 452 | if (int_req & INT_STK) { /* stack overflow? */\r |
| 453 | int_req = int_req & ~INT_STK; /* clear */\r |
| 454 | MA = STK_JMP; /* jmp @3 */\r |
| 455 | }\r |
| 456 | else\r |
| 457 | MA = INT_JMP; /* intr: jmp @1 */\r |
| 458 | }\r |
| 459 | if ( MODE_64K_ACTIVE ) {\r |
| 460 | indf = IND_STEP (MA);\r |
| 461 | }\r |
| 462 | else\r |
| 463 | {\r |
| 464 | for (i = 0, indf = 1; indf && (i < ind_max); i++) {\r |
| 465 | indf = IND_STEP (MA); /* indirect loop */\r |
| 466 | }\r |
| 467 | if (i >= ind_max) {\r |
| 468 | reason = STOP_IND_INT;\r |
| 469 | break;\r |
| 470 | }\r |
| 471 | }\r |
| 472 | PC = MA;\r |
| 473 | } /* end interrupt */\r |
| 474 | \r |
| 475 | if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */\r |
| 476 | reason = STOP_IBKPT; /* stop simulation */\r |
| 477 | break;\r |
| 478 | }\r |
| 479 | \r |
| 480 | IR = M[PC]; /* fetch instr */\r |
| 481 | if ( hist_cnt )\r |
| 482 | {\r |
| 483 | hist_save( PC, IR ) ; /* PC, int_req unchanged */\r |
| 484 | }\r |
| 485 | \r |
| 486 | INCREMENT_PC ;\r |
| 487 | int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */\r |
| 488 | sim_interval = sim_interval - 1;\r |
| 489 | \r |
| 490 | /* Operate instruction */\r |
| 491 | \r |
| 492 | if (IR & I_OPR) { /* operate? */\r |
| 493 | int32 src, srcAC, dstAC;\r |
| 494 | \r |
| 495 | srcAC = I_GETSRC (IR); /* get reg decodes */\r |
| 496 | dstAC = I_GETDST (IR);\r |
| 497 | switch (I_GETCRY (IR)) { /* decode carry */\r |
| 498 | case 0: /* load */\r |
| 499 | src = AC[srcAC] | C;\r |
| 500 | break;\r |
| 501 | case 1: /* clear */\r |
| 502 | src = AC[srcAC];\r |
| 503 | break;\r |
| 504 | case 2: /* set */\r |
| 505 | src = AC[srcAC] | CBIT;\r |
| 506 | break;\r |
| 507 | case 3: /* complement */\r |
| 508 | src = AC[srcAC] | (C ^ CBIT);\r |
| 509 | break;\r |
| 510 | } /* end switch carry */\r |
| 511 | \r |
| 512 | switch (I_GETALU (IR)) { /* decode ALU */\r |
| 513 | case 0: /* COM */\r |
| 514 | src = src ^ DMASK;\r |
| 515 | break;\r |
| 516 | case 1: /* NEG */\r |
| 517 | src = ((src ^ DMASK) + 1) & CDMASK;\r |
| 518 | break;\r |
| 519 | case 2: /* MOV */\r |
| 520 | break;\r |
| 521 | case 3: /* INC */\r |
| 522 | src = (src + 1) & CDMASK;\r |
| 523 | break;\r |
| 524 | case 4: /* ADC */\r |
| 525 | src = ((src ^ DMASK) + AC[dstAC]) & CDMASK;\r |
| 526 | break;\r |
| 527 | case 5: /* SUB */\r |
| 528 | src = ((src ^ DMASK) + AC[dstAC] + 1) & CDMASK;\r |
| 529 | break;\r |
| 530 | case 6: /* ADD */\r |
| 531 | src = (src + AC[dstAC]) & CDMASK;\r |
| 532 | break;\r |
| 533 | case 7: /* AND */\r |
| 534 | src = src & (AC[dstAC] | CBIT);\r |
| 535 | break;\r |
| 536 | } /* end switch oper */\r |
| 537 | \r |
| 538 | switch (I_GETSHF (IR)) { /* decode shift */\r |
| 539 | case 0: /* nop */\r |
| 540 | break;\r |
| 541 | case 1: /* L */\r |
| 542 | src = ((src << 1) | (src >> 16)) & CDMASK;\r |
| 543 | break;\r |
| 544 | case 2: /* R */\r |
| 545 | src = ((src >> 1) | (src << 16)) & CDMASK;\r |
| 546 | break;\r |
| 547 | case 3: /* S */\r |
| 548 | src = ((src & 0377) << 8) | ((src >> 8) & 0377) |\r |
| 549 | (src & CBIT);\r |
| 550 | break;\r |
| 551 | } /* end switch shift */\r |
| 552 | \r |
| 553 | switch (I_GETSKP (IR)) { /* decode skip */\r |
| 554 | case 0: /* nop */\r |
| 555 | if ((IR & I_NLD) && (cpu_unit.flags & UNIT_STK)) {\r |
| 556 | int_req = int_req | INT_TRAP ; /* Nova 3 or 4 trap */\r |
| 557 | continue ;\r |
| 558 | }\r |
| 559 | break;\r |
| 560 | case 1: /* SKP */\r |
| 561 | INCREMENT_PC ;\r |
| 562 | break;\r |
| 563 | case 2: /* SZC */\r |
| 564 | if (src < CBIT) INCREMENT_PC ;\r |
| 565 | break;\r |
| 566 | case 3: /* SNC */\r |
| 567 | if (src >= CBIT) INCREMENT_PC ;\r |
| 568 | break;\r |
| 569 | case 4: /* SZR */\r |
| 570 | if ((src & DMASK) == 0) INCREMENT_PC ;\r |
| 571 | break;\r |
| 572 | case 5: /* SNR */\r |
| 573 | if ((src & DMASK) != 0) INCREMENT_PC ;\r |
| 574 | break;\r |
| 575 | case 6: /* SEZ */\r |
| 576 | if (src <= CBIT) INCREMENT_PC ;\r |
| 577 | break;\r |
| 578 | case 7: /* SBN */\r |
| 579 | if (src > CBIT) INCREMENT_PC ;\r |
| 580 | break;\r |
| 581 | } /* end switch skip */\r |
| 582 | if ((IR & I_NLD) == 0) { /* load? */\r |
| 583 | AC[dstAC] = src & DMASK;\r |
| 584 | C = src & CBIT;\r |
| 585 | } /* end if load */\r |
| 586 | } /* end if operate */\r |
| 587 | \r |
| 588 | /* Memory reference instructions */\r |
| 589 | \r |
| 590 | else if (IR < 060000) { /* mem ref? */\r |
| 591 | int32 src, MA, indf;\r |
| 592 | \r |
| 593 | MA = I_GETDISP (IR); /* get disp */\r |
| 594 | switch (I_GETMODE (IR)) { /* decode mode */\r |
| 595 | case 0: /* page zero */\r |
| 596 | break;\r |
| 597 | case 1: /* PC relative */\r |
| 598 | if (MA & DISPSIGN) MA = 0177400 | MA;\r |
| 599 | MA = (MA + PC - 1) & AMASK;\r |
| 600 | break;\r |
| 601 | case 2: /* AC2 relative */\r |
| 602 | if (MA & DISPSIGN) MA = 0177400 | MA;\r |
| 603 | MA = (MA + AC[2]) & AMASK;\r |
| 604 | break;\r |
| 605 | case 3: /* AC3 relative */\r |
| 606 | if (MA & DISPSIGN) MA = 0177400 | MA;\r |
| 607 | MA = (MA + AC[3]) & AMASK;\r |
| 608 | break;\r |
| 609 | } /* end switch mode */\r |
| 610 | \r |
| 611 | if ( (indf = IR & I_IND) ) { /* indirect? */\r |
| 612 | if ( MODE_64K_ACTIVE ) { /* 64k mode? */\r |
| 613 | indf = IND_STEP (MA);\r |
| 614 | }\r |
| 615 | else /* compat mode */\r |
| 616 | {\r |
| 617 | for (i = 0; indf && (i < ind_max); i++) { /* count */\r |
| 618 | indf = IND_STEP (MA); /* resolve indirect */\r |
| 619 | }\r |
| 620 | if (i >= ind_max) { /* too many? */\r |
| 621 | reason = STOP_IND;\r |
| 622 | break;\r |
| 623 | }\r |
| 624 | }\r |
| 625 | }\r |
| 626 | \r |
| 627 | switch (I_GETOPAC (IR)) { /* decode op + AC */\r |
| 628 | case 001: /* JSR */\r |
| 629 | AC[3] = PC;\r |
| 630 | case 000: /* JMP */\r |
| 631 | PCQ_ENTRY;\r |
| 632 | PC = MA;\r |
| 633 | break;\r |
| 634 | case 002: /* ISZ */\r |
| 635 | src = (M[MA] + 1) & DMASK;\r |
| 636 | if (MEM_ADDR_OK(MA)) M[MA] = src;\r |
| 637 | if (src == 0) INCREMENT_PC ;\r |
| 638 | break;\r |
| 639 | case 003: /* DSZ */\r |
| 640 | src = (M[MA] - 1) & DMASK;\r |
| 641 | if (MEM_ADDR_OK(MA)) M[MA] = src;\r |
| 642 | if (src == 0) INCREMENT_PC ;\r |
| 643 | break;\r |
| 644 | case 004: /* LDA 0 */\r |
| 645 | AC[0] = M[MA];\r |
| 646 | break;\r |
| 647 | case 005: /* LDA 1 */\r |
| 648 | AC[1] = M[MA];\r |
| 649 | break;\r |
| 650 | case 006: /* LDA 2 */\r |
| 651 | AC[2] = M[MA];\r |
| 652 | break;\r |
| 653 | case 007: /* LDA 3 */\r |
| 654 | AC[3] = M[MA];\r |
| 655 | break;\r |
| 656 | case 010: /* STA 0 */\r |
| 657 | if (MEM_ADDR_OK(MA)) M[MA] = AC[0];\r |
| 658 | break;\r |
| 659 | case 011: /* STA 1 */\r |
| 660 | if (MEM_ADDR_OK(MA)) M[MA] = AC[1];\r |
| 661 | break;\r |
| 662 | case 012: /* STA 2 */\r |
| 663 | if (MEM_ADDR_OK(MA)) M[MA] = AC[2];\r |
| 664 | break;\r |
| 665 | case 013: /* STA 3 */\r |
| 666 | if (MEM_ADDR_OK(MA)) M[MA] = AC[3];\r |
| 667 | break;\r |
| 668 | } /* end switch */\r |
| 669 | } /* end mem ref */\r |
| 670 | \r |
| 671 | /* IOT instruction */\r |
| 672 | \r |
| 673 | else { /* IOT */\r |
| 674 | int32 dstAC, pulse, code, device, iodata;\r |
| 675 | \r |
| 676 | dstAC = I_GETDST (IR); /* decode fields */\r |
| 677 | code = I_GETIOT (IR);\r |
| 678 | pulse = I_GETPULSE (IR);\r |
| 679 | device = I_GETDEV (IR);\r |
| 680 | if (code == ioSKP) { /* IO skip? */\r |
| 681 | switch (pulse) { /* decode IR<8:9> */\r |
| 682 | \r |
| 683 | case 0: /* skip if busy */\r |
| 684 | if ((device == DEV_CPU)? (int_req & INT_ION) != 0:\r |
| 685 | (dev_busy & dev_table[device].mask) != 0)\r |
| 686 | INCREMENT_PC ;\r |
| 687 | break;\r |
| 688 | \r |
| 689 | case 1: /* skip if not busy */\r |
| 690 | if ((device == DEV_CPU)? (int_req & INT_ION) == 0:\r |
| 691 | (dev_busy & dev_table[device].mask) == 0)\r |
| 692 | INCREMENT_PC ;\r |
| 693 | break;\r |
| 694 | \r |
| 695 | case 2: /* skip if done */\r |
| 696 | if ((device == DEV_CPU)? pwr_low != 0:\r |
| 697 | (dev_done & dev_table[device].mask) != 0)\r |
| 698 | INCREMENT_PC ;\r |
| 699 | break;\r |
| 700 | \r |
| 701 | case 3: /* skip if not done */\r |
| 702 | if ((device == DEV_CPU)? pwr_low == 0:\r |
| 703 | (dev_done & dev_table[device].mask) == 0)\r |
| 704 | INCREMENT_PC ;\r |
| 705 | break;\r |
| 706 | } /* end switch */\r |
| 707 | } /* end IO skip */\r |
| 708 | \r |
| 709 | /* Hmm, this means a Nova 3 _must_ have DEV_MDV enabled - not true in DG land */\r |
| 710 | \r |
| 711 | else if (device == DEV_MDV) {\r |
| 712 | switch (code) { /* case on opcode */\r |
| 713 | \r |
| 714 | case ioNIO: /* frame ptr */\r |
| 715 | if (cpu_unit.flags & UNIT_STK) {\r |
| 716 | if (pulse == iopN) FP = AC[dstAC] & AMASK ;\r |
| 717 | if (pulse == iopC) AC[dstAC] = FP & AMASK ;\r |
| 718 | }\r |
| 719 | break;\r |
| 720 | \r |
| 721 | case ioDIA: /* load byte */\r |
| 722 | if (cpu_unit.flags & UNIT_BYT)\r |
| 723 | {\r |
| 724 | AC[dstAC] = (M[AC[pulse] >> 1] >> ((AC[pulse] & 1)? 0: 8)) & 0377 ;\r |
| 725 | }\r |
| 726 | else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */\r |
| 727 | {\r |
| 728 | SP = INCA (SP);\r |
| 729 | if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r |
| 730 | SP = INCA (SP);\r |
| 731 | if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r |
| 732 | SP = INCA (SP);\r |
| 733 | if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r |
| 734 | SP = INCA (SP);\r |
| 735 | if (MEM_ADDR_OK (SP)) M[SP] = FP;\r |
| 736 | SP = INCA (SP);\r |
| 737 | if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r |
| 738 | AC[3] = FP = SP & AMASK; \r |
| 739 | STK_CHECK (SP, 5);\r |
| 740 | }\r |
| 741 | else\r |
| 742 | {\r |
| 743 | AC[dstAC] = 0;\r |
| 744 | }\r |
| 745 | break;\r |
| 746 | \r |
| 747 | case ioDOA: /* stack ptr */\r |
| 748 | if (cpu_unit.flags & UNIT_STK) {\r |
| 749 | if (pulse == iopN) SP = AC[dstAC] & AMASK;\r |
| 750 | if (pulse == iopC) AC[dstAC] = SP & AMASK;\r |
| 751 | }\r |
| 752 | break;\r |
| 753 | \r |
| 754 | case ioDIB: /* push, pop */\r |
| 755 | if (cpu_unit.flags & UNIT_STK) {\r |
| 756 | if (pulse == iopN) { /* push (PSHA) */\r |
| 757 | SP = INCA (SP);\r |
| 758 | if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r |
| 759 | STK_CHECK (SP, 1);\r |
| 760 | }\r |
| 761 | if ((pulse == iopS) && /* Nova 4 pshn (PSHN) */\r |
| 762 | (cpu_unit.flags & UNIT_BYT)) {\r |
| 763 | SP = INCA (SP);\r |
| 764 | if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r |
| 765 | if ( (SP & 0xFFFF) > (M[042] & 0xFFFF) )\r |
| 766 | {\r |
| 767 | int_req = int_req | INT_STK ;\r |
| 768 | }\r |
| 769 | }\r |
| 770 | if (pulse == iopC) { /* pop (POPA) */\r |
| 771 | AC[dstAC] = M[SP];\r |
| 772 | SP = DECA (SP);\r |
| 773 | }\r |
| 774 | }\r |
| 775 | break;\r |
| 776 | \r |
| 777 | case ioDOB: /* store byte */\r |
| 778 | if (cpu_unit.flags & UNIT_BYT)\r |
| 779 | {\r |
| 780 | int32 MA, val;\r |
| 781 | MA = AC[pulse] >> 1;\r |
| 782 | val = AC[dstAC] & 0377;\r |
| 783 | if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)?\r |
| 784 | ((M[MA] & ~0377) | val)\r |
| 785 | : ((M[MA] & 0377) | (val << 8));\r |
| 786 | }\r |
| 787 | else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */\r |
| 788 | {\r |
| 789 | SP = INCA (SP);\r |
| 790 | if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r |
| 791 | SP = INCA (SP);\r |
| 792 | if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r |
| 793 | SP = INCA (SP);\r |
| 794 | if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r |
| 795 | SP = INCA (SP);\r |
| 796 | if (MEM_ADDR_OK (SP)) M[SP] = FP;\r |
| 797 | SP = INCA (SP);\r |
| 798 | if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r |
| 799 | AC[3] = FP = SP & AMASK; \r |
| 800 | STK_CHECK (SP, 5);\r |
| 801 | }\r |
| 802 | break;\r |
| 803 | \r |
| 804 | case ioDIC: /* save, return */\r |
| 805 | if (cpu_unit.flags & UNIT_STK) {\r |
| 806 | if (pulse == iopN) { /* save */\r |
| 807 | SP = INCA (SP);\r |
| 808 | if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r |
| 809 | SP = INCA (SP);\r |
| 810 | if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r |
| 811 | SP = INCA (SP);\r |
| 812 | if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r |
| 813 | SP = INCA (SP);\r |
| 814 | if (MEM_ADDR_OK (SP)) M[SP] = FP;\r |
| 815 | SP = INCA (SP);\r |
| 816 | if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r |
| 817 | AC[3] = FP = SP & AMASK; \r |
| 818 | STK_CHECK (SP, 5);\r |
| 819 | }\r |
| 820 | else if (pulse == iopC) { /* retn */\r |
| 821 | PCQ_ENTRY;\r |
| 822 | SP = FP & AMASK;\r |
| 823 | C = (M[SP] << 1) & CBIT;\r |
| 824 | PC = M[SP] & AMASK;\r |
| 825 | SP = DECA (SP);\r |
| 826 | AC[3] = M[SP];\r |
| 827 | SP = DECA (SP);\r |
| 828 | AC[2] = M[SP];\r |
| 829 | SP = DECA (SP);\r |
| 830 | AC[1] = M[SP];\r |
| 831 | SP = DECA (SP);\r |
| 832 | AC[0] = M[SP];\r |
| 833 | SP = DECA (SP);\r |
| 834 | FP = AC[3] & AMASK;\r |
| 835 | }\r |
| 836 | else if ((pulse == iopS) && /* Nova 4 SAVN */\r |
| 837 | (cpu_unit.flags & UNIT_BYT)) {\r |
| 838 | int32 frameSz = M[PC] ;\r |
| 839 | PC = INCA (PC) ;\r |
| 840 | SP = INCA (SP);\r |
| 841 | if (MEM_ADDR_OK (SP)) M[SP] = AC[0];\r |
| 842 | SP = INCA (SP);\r |
| 843 | if (MEM_ADDR_OK (SP)) M[SP] = AC[1];\r |
| 844 | SP = INCA (SP);\r |
| 845 | if (MEM_ADDR_OK (SP)) M[SP] = AC[2];\r |
| 846 | SP = INCA (SP);\r |
| 847 | if (MEM_ADDR_OK (SP)) M[SP] = FP;\r |
| 848 | SP = INCA (SP);\r |
| 849 | if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK);\r |
| 850 | AC[3] = FP = SP & AMASK ;\r |
| 851 | SP = (SP + frameSz) & AMASK ;\r |
| 852 | if (SP > M[042])\r |
| 853 | {\r |
| 854 | int_req = int_req | INT_STK;\r |
| 855 | }\r |
| 856 | }\r |
| 857 | }\r |
| 858 | break;\r |
| 859 | \r |
| 860 | case ioDOC:\r |
| 861 | if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV))\r |
| 862 | { /* Nova, Nova3 or Nova 4 */\r |
| 863 | uint32 mddata, uAC0, uAC1, uAC2;\r |
| 864 | \r |
| 865 | uAC0 = (uint32) AC[0];\r |
| 866 | uAC1 = (uint32) AC[1];\r |
| 867 | uAC2 = (uint32) AC[2];\r |
| 868 | if (pulse == iopP)\r |
| 869 | { /* mul */\r |
| 870 | mddata = (uAC1 * uAC2) + uAC0;\r |
| 871 | AC[0] = (mddata >> 16) & DMASK;\r |
| 872 | AC[1] = mddata & DMASK;\r |
| 873 | }\r |
| 874 | if (pulse == iopS)\r |
| 875 | { /* div */\r |
| 876 | if ((uAC0 >= uAC2) || (uAC2 == 0))\r |
| 877 | {\r |
| 878 | C = CBIT;\r |
| 879 | }\r |
| 880 | else\r |
| 881 | {\r |
| 882 | C = 0;\r |
| 883 | mddata = (uAC0 << 16) | uAC1;\r |
| 884 | AC[1] = mddata / uAC2;\r |
| 885 | AC[0] = mddata % uAC2;\r |
| 886 | }\r |
| 887 | }\r |
| 888 | }\r |
| 889 | else if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT) /* assuming UNIT_BYT = Nova 4 */)\r |
| 890 | {\r |
| 891 | int32 mddata;\r |
| 892 | if (pulse == iopC)\r |
| 893 | { /* muls */\r |
| 894 | mddata = (SEXT (AC[1]) * SEXT (AC[2])) + SEXT (AC[0]);\r |
| 895 | AC[0] = (mddata >> 16) & DMASK;\r |
| 896 | AC[1] = mddata & DMASK;\r |
| 897 | }\r |
| 898 | else if (pulse == iopN)\r |
| 899 | { /* divs */\r |
| 900 | if ((AC[2] == 0) || /* overflow? */\r |
| 901 | ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777)))\r |
| 902 | {\r |
| 903 | C = CBIT;\r |
| 904 | }\r |
| 905 | else\r |
| 906 | {\r |
| 907 | mddata = (SEXT (AC[0]) << 16) | AC[1];\r |
| 908 | AC[1] = mddata / SEXT (AC[2]);\r |
| 909 | AC[0] = mddata % SEXT (AC[2]);\r |
| 910 | if ((AC[1] > 077777) || (AC[1] < -0100000))\r |
| 911 | {\r |
| 912 | C = CBIT;\r |
| 913 | }\r |
| 914 | else\r |
| 915 | {\r |
| 916 | C = 0;\r |
| 917 | }\r |
| 918 | AC[0] = AC[0] & DMASK;\r |
| 919 | }\r |
| 920 | }\r |
| 921 | }\r |
| 922 | else if ((dstAC == 3) && (cpu_unit.flags & UNIT_STK)) /* if Nova 3 this is really a PSHA... 2007-Jun-01, BKR */\r |
| 923 | {\r |
| 924 | SP = INCA (SP);\r |
| 925 | if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];\r |
| 926 | STK_CHECK (SP, 1);\r |
| 927 | }\r |
| 928 | break;\r |
| 929 | } /* end case code */\r |
| 930 | } /* end if mul/div */\r |
| 931 | \r |
| 932 | else if (device == DEV_CPU) { /* CPU control */\r |
| 933 | switch (code) { /* decode IR<5:7> */\r |
| 934 | \r |
| 935 | case ioNIO: /* NIOP <x> CPU ? */\r |
| 936 | if ( pulse == iopP )\r |
| 937 | if ( MODE_64K )\r |
| 938 | {\r |
| 939 | /* Keronix/Point4/SCI/INI/IDP (and others) */\r |
| 940 | /* 64 KW memory extension: */\r |
| 941 | /* NIOP - set memory mode (32/64 KW) per AC: */\r |
| 942 | /* B15: 0 = 32 KW, 1 = 64 KW mode */\r |
| 943 | AMASK = (AC[dstAC] & 0x0001) ? 0177777 : 077777 ;\r |
| 944 | }\r |
| 945 | break ;\r |
| 946 | \r |
| 947 | case ioDIA: /* read switches */\r |
| 948 | AC[dstAC] = SR;\r |
| 949 | break;\r |
| 950 | \r |
| 951 | case ioDIB: /* int ack */\r |
| 952 | AC[dstAC] = 0;\r |
| 953 | DEV_UPDATE_INTR ;\r |
| 954 | iodata = int_req & (-int_req);\r |
| 955 | for (i = DEV_LOW; i <= DEV_HIGH; i++) {\r |
| 956 | if (iodata & dev_table[i].mask) {\r |
| 957 | AC[dstAC] = i;\r |
| 958 | break;\r |
| 959 | }\r |
| 960 | }\r |
| 961 | break;\r |
| 962 | \r |
| 963 | case ioDOB: /* mask out */\r |
| 964 | mask_out (pimask = AC[dstAC]);\r |
| 965 | break;\r |
| 966 | \r |
| 967 | case ioDIC: /* io reset */\r |
| 968 | reset_all (0); /* reset devices */\r |
| 969 | mask_out( 0 ) ; /* clear all device masks */\r |
| 970 | AMASK = 077777 ; /* reset memory mode */\r |
| 971 | break;\r |
| 972 | \r |
| 973 | case ioDOC: /* halt */\r |
| 974 | reason = STOP_HALT;\r |
| 975 | break;\r |
| 976 | } /* end switch code */\r |
| 977 | \r |
| 978 | switch (pulse) { /* decode IR<8:9> */\r |
| 979 | \r |
| 980 | case iopS: /* ion */\r |
| 981 | int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;\r |
| 982 | break;\r |
| 983 | \r |
| 984 | case iopC: /* iof */\r |
| 985 | int_req = int_req & ~INT_ION;\r |
| 986 | break;\r |
| 987 | } /* end switch pulse */\r |
| 988 | } /* end CPU control */\r |
| 989 | \r |
| 990 | else if (dev_table[device].routine) { /* normal device */\r |
| 991 | iodata = dev_table[device].routine (pulse, code, AC[dstAC]);\r |
| 992 | reason = iodata >> IOT_V_REASON;\r |
| 993 | if (code & 1) AC[dstAC] = iodata & 0177777;\r |
| 994 | }\r |
| 995 | \r |
| 996 | /* bkr, 2007-May-30\r |
| 997 | * if device does not exist certain I/O instructions will still\r |
| 998 | * return data: DIA/B/C will return idle data bus value and\r |
| 999 | * SKPBZ/SKPDZ will sense zero value (and will therefore skip).\r |
| 1000 | *\r |
| 1001 | * Perform these non-supported device functions only if 'stop_dev'\r |
| 1002 | * is zero (i.e. I/O access trap is not in effect).\r |
| 1003 | */\r |
| 1004 | else if ( stop_dev == 0 )\r |
| 1005 | {\r |
| 1006 | switch (code) /* decode IR<5:7> */\r |
| 1007 | {\r |
| 1008 | case ioDIA:\r |
| 1009 | case ioDIB:\r |
| 1010 | case ioDIC:\r |
| 1011 | AC[dstAC] = 0 ; /* idle I/O bus data */\r |
| 1012 | break;\r |
| 1013 | \r |
| 1014 | case ioSKP:\r |
| 1015 | /* (This should have been caught in previous CPU skip code) */\r |
| 1016 | if ( (pulse == 1 /* SKPBZ */) || (pulse == 3 /* SKPDZ */) )\r |
| 1017 | {\r |
| 1018 | INCREMENT_PC ;\r |
| 1019 | }\r |
| 1020 | } /* end of 'switch' */\r |
| 1021 | } /* end of handling non-existant device */\r |
| 1022 | else reason = stop_dev;\r |
| 1023 | } /* end if IOT */\r |
| 1024 | } /* end while */\r |
| 1025 | \r |
| 1026 | /* Simulation halted */\r |
| 1027 | \r |
| 1028 | saved_PC = PC;\r |
| 1029 | pcq_r->qptr = pcq_p; /* update pc q ptr */\r |
| 1030 | return ( reason ) ;\r |
| 1031 | }\r |
| 1032 | \r |
| 1033 | /* New priority mask out */\r |
| 1034 | \r |
| 1035 | void mask_out (int32 newmask)\r |
| 1036 | {\r |
| 1037 | int32 i;\r |
| 1038 | \r |
| 1039 | dev_disable = 0;\r |
| 1040 | for (i = DEV_LOW; i <= DEV_HIGH; i++) {\r |
| 1041 | if (newmask & dev_table[i].pi)\r |
| 1042 | dev_disable = dev_disable | dev_table[i].mask;\r |
| 1043 | }\r |
| 1044 | DEV_UPDATE_INTR ;\r |
| 1045 | return;\r |
| 1046 | }\r |
| 1047 | \r |
| 1048 | /* Reset routine */\r |
| 1049 | \r |
| 1050 | t_stat cpu_reset (DEVICE *dptr)\r |
| 1051 | {\r |
| 1052 | int_req = int_req & ~(INT_ION | INT_STK | INT_TRAP);\r |
| 1053 | pimask = 0;\r |
| 1054 | dev_disable = 0;\r |
| 1055 | pwr_low = 0;\r |
| 1056 | AMASK = 077777 ; /* 32KW mode */\r |
| 1057 | pcq_r = find_reg ("PCQ", NULL, dptr);\r |
| 1058 | if (pcq_r) pcq_r->qptr = 0;\r |
| 1059 | else return SCPE_IERR;\r |
| 1060 | sim_brk_types = sim_brk_dflt = SWMASK ('E');\r |
| 1061 | return SCPE_OK;\r |
| 1062 | }\r |
| 1063 | \r |
| 1064 | /* Memory examine */\r |
| 1065 | \r |
| 1066 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r |
| 1067 | {\r |
| 1068 | if (addr >= MEMSIZE) return SCPE_NXM;\r |
| 1069 | if (vptr != NULL) *vptr = M[addr] & DMASK;\r |
| 1070 | return SCPE_OK;\r |
| 1071 | }\r |
| 1072 | \r |
| 1073 | /* Memory deposit */\r |
| 1074 | \r |
| 1075 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r |
| 1076 | {\r |
| 1077 | if (addr >= MEMSIZE) return SCPE_NXM;\r |
| 1078 | M[addr] = val & DMASK;\r |
| 1079 | return SCPE_OK;\r |
| 1080 | }\r |
| 1081 | \r |
| 1082 | /* Alter memory size */\r |
| 1083 | \r |
| 1084 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 1085 | {\r |
| 1086 | int32 mc = 0;\r |
| 1087 | t_addr i;\r |
| 1088 | \r |
| 1089 | if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))\r |
| 1090 | return SCPE_ARG;\r |
| 1091 | for (i = val; i < MEMSIZE; i++) mc = mc | M[i];\r |
| 1092 | if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))\r |
| 1093 | return SCPE_OK;\r |
| 1094 | MEMSIZE = val;\r |
| 1095 | for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;\r |
| 1096 | return SCPE_OK;\r |
| 1097 | }\r |
| 1098 | \r |
| 1099 | /* Build dispatch table */\r |
| 1100 | \r |
| 1101 | t_stat build_devtab (void)\r |
| 1102 | {\r |
| 1103 | DEVICE *dptr;\r |
| 1104 | DIB *dibp;\r |
| 1105 | int32 i, dn;\r |
| 1106 | \r |
| 1107 | for (i = 0; i < 64; i++) { /* clr dev_table */\r |
| 1108 | dev_table[i].mask = 0;\r |
| 1109 | dev_table[i].pi = 0;\r |
| 1110 | dev_table[i].routine = NULL;\r |
| 1111 | }\r |
| 1112 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */\r |
| 1113 | if (!(dptr->flags & DEV_DIS) && /* enabled and */\r |
| 1114 | ( (dibp = (DIB *) dptr->ctxt)) ) { /* defined DIB? */\r |
| 1115 | dn = dibp->dnum; /* get dev num */\r |
| 1116 | dev_table[dn].mask = dibp->mask; /* copy entries */\r |
| 1117 | dev_table[dn].pi = dibp->pi;\r |
| 1118 | dev_table[dn].routine = dibp->routine;\r |
| 1119 | }\r |
| 1120 | }\r |
| 1121 | return SCPE_OK;\r |
| 1122 | }\r |
| 1123 | \r |
| 1124 | /* BKR notes:\r |
| 1125 | *\r |
| 1126 | * Data General APL (Automatic Program Load) boot code\r |
| 1127 | *\r |
| 1128 | * - This bootstrap code is called the "APL option" in DG documentation (Automatic\r |
| 1129 | * Program Load), and cost ~$400 USD (in 1970 - wow!) to load 32(10) words from\r |
| 1130 | * a PROM to main (core) memory location 0 - 32.\r |
| 1131 | * - This code is documented in various DG Nova programming manuals and was\r |
| 1132 | * quite static (i.e. no revisions or updates to code were made). \r |
| 1133 | * - switch register is used to determine device code and device type.\r |
| 1134 | * - lower 6-bits of switch register determines device code (0-63.).\r |
| 1135 | * - most significant bit determines if device is "low speed" or "high speed".\r |
| 1136 | * - "high speed" devices have effective boot program logic of:\r |
| 1137 | *\r |
| 1138 | * IORST\r |
| 1139 | * NIOS <device>\r |
| 1140 | * JMP .\r |
| 1141 | *\r |
| 1142 | * - "high speed" devices use data channel (DCH) to read first sector/record\r |
| 1143 | * of device into memory (usually starting at location 0), which then over-writes\r |
| 1144 | * the 'JMP .' instruction of boot code. This usually has a jump to some other\r |
| 1145 | * device and operating system specific boot code that was loaded from the device.\r |
| 1146 | * - "low speed" devices are assumed to be sequential character-oriented devices\r |
| 1147 | * (i.e. Teletype (r) reader, paper tape reader).\r |
| 1148 | * - "low speed" devices are assumed to start read operations with a 'S' pulse,\r |
| 1149 | * read data buffer with a DIA instruction and have standard DG I/O Busy/Done logic.\r |
| 1150 | * - "low speed" devices usually read in a more full-featured 'binary loader' with\r |
| 1151 | * the APL boot code:\r |
| 1152 | *\r |
| 1153 | * DG paper tape: 091-000004-xx, Binary Loader (BLDR.AB)\r |
| 1154 | *\r |
| 1155 | * - The Binary Loader was in turn used to load tapes in the usual DG 'absolute binary' format.\r |
| 1156 | */\r |
| 1157 | \r |
| 1158 | #define BOOT_START 00000\r |
| 1159 | #define BOOT_LEN (sizeof(boot_rom) / sizeof(int32))\r |
| 1160 | \r |
| 1161 | static const int32 boot_rom[] = {\r |
| 1162 | 0062677, /* IORST ;reset all I/O */\r |
| 1163 | 0060477, /* READS 0 ;read SR into AC0 */\r |
| 1164 | 0024026, /* LDA 1,C77 ;get dev mask */\r |
| 1165 | 0107400, /* AND 0,1 ;isolate dev code */\r |
| 1166 | 0124000, /* COM 1,1 ;- device code - 1 */\r |
| 1167 | 0010014, /* LOOP: ISZ OP1 ;device code to all */\r |
| 1168 | 0010030, /* ISZ OP2 ;I/O instructions */\r |
| 1169 | 0010032, /* ISZ OP3 */\r |
| 1170 | 0125404, /* INC 1,1,SZR ;done? */\r |
| 1171 | 0000005, /* JMP LOOP ;no, increment again */\r |
| 1172 | 0030016, /* LDA 2,C377 ;place JMP 377 into */\r |
| 1173 | 0050377, /* STA 2,377 ;location 377 */\r |
| 1174 | 0060077, /* OP1: 060077 ;start device (NIOS 0) */\r |
| 1175 | 0101102, /* MOVL 0,0,SZC ;test switch 0, low speed? */\r |
| 1176 | 0000377, /* C377: JMP 377 ;no - jmp 377 & wait */\r |
| 1177 | 0004030, /* LOOP2: JSR GET+1 ;get a frame */\r |
| 1178 | 0101065, /* MOVC 0,0,SNR ;is it non-zero? */\r |
| 1179 | 0000017, /* JMP LOOP2 ;no, ignore */\r |
| 1180 | 0004027, /* LOOP4: JSR GET ;yes, get full word */\r |
| 1181 | 0046026, /* STA 1,@C77 ;store starting at 100 */\r |
| 1182 | /* ;2's complement of word ct */\r |
| 1183 | 0010100, /* ISZ 100 ;done? */\r |
| 1184 | 0000022, /* JMP LOOP4 ;no, get another */\r |
| 1185 | 0000077, /* C77: JMP 77 ;yes location ctr and */\r |
| 1186 | /* ;jmp to last word */\r |
| 1187 | 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */\r |
| 1188 | /* OP2: */\r |
| 1189 | 0063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */\r |
| 1190 | 0000030, /* JMP LOOP3 ;no -- wait */\r |
| 1191 | 0060477, /* OP3: 060477 ;y -- read in ac0 (DIAS 0,0) */\r |
| 1192 | 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */\r |
| 1193 | 0000030, /* JMP LOOP3 ;no go back after it */\r |
| 1194 | 0125300, /* MOVS 1,1 ;yes swap them */\r |
| 1195 | 0001400, /* JMP 0,3 ;rtn with full word */\r |
| 1196 | 0000000 /* 0 ;padding */\r |
| 1197 | };\r |
| 1198 | \r |
| 1199 | t_stat cpu_boot (int32 unitno, DEVICE *dptr)\r |
| 1200 | {\r |
| 1201 | int32 i;\r |
| 1202 | \r |
| 1203 | for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];\r |
| 1204 | saved_PC = BOOT_START;\r |
| 1205 | return SCPE_OK;\r |
| 1206 | }\r |
| 1207 | \r |
| 1208 | /* 1-to-1 map for I/O devices */\r |
| 1209 | \r |
| 1210 | int32 MapAddr (int32 map, int32 addr)\r |
| 1211 | {\r |
| 1212 | return addr;\r |
| 1213 | }\r |
| 1214 | \r |
| 1215 | /* History subsystem\r |
| 1216 | \r |
| 1217 | global routines\r |
| 1218 | \r |
| 1219 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc, void ** HistCookie, sizeof(usrHistInfo) ) ;\r |
| 1220 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc, void * HistCookie ) ;\r |
| 1221 | int hist_save( int32 next_pc, int32 our_ir, void * usrHistInfo )\r |
| 1222 | \r |
| 1223 | local user struct:\r |
| 1224 | \r |
| 1225 | usrHistInfo\r |
| 1226 | \r |
| 1227 | local user routines:\r |
| 1228 | \r |
| 1229 | int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ;\r |
| 1230 | int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ;\r |
| 1231 | \r |
| 1232 | typedef struct\r |
| 1233 | {\r |
| 1234 | int hMax ; // total # entries in queue (0 = inactive)\r |
| 1235 | int hCount ; // current entry\r |
| 1236 | void * hPtr ; // pointer to save area\r |
| 1237 | int hSize ; // size of each user save area (not used by global routines?)\r |
| 1238 | } Hist_info ;\r |
| 1239 | */\r |
| 1240 | \r |
| 1241 | /* generalized CPU execution trace */\r |
| 1242 | \r |
| 1243 | #define HIST_IR_INVALID -1\r |
| 1244 | #define HIST_MIN 0 /* 0 == deactivate history feature, else size of queue */\r |
| 1245 | #define HIST_MAX 1000000 /* completely arbitrary max size value */\r |
| 1246 | \r |
| 1247 | /* save history entry (proposed local routine) */\r |
| 1248 | \r |
| 1249 | static int hist_save( int32 pc, int32 our_ir )\r |
| 1250 | {\r |
| 1251 | Hist_entry * hist_ptr ;\r |
| 1252 | \r |
| 1253 | if ( hist )\r |
| 1254 | if ( hist_cnt )\r |
| 1255 | {\r |
| 1256 | hist_p = (hist_p + 1) ; /* next entry */\r |
| 1257 | if ( hist_p >= hist_cnt )\r |
| 1258 | {\r |
| 1259 | hist_p = 0 ;\r |
| 1260 | }\r |
| 1261 | hist_ptr = &hist[ hist_p ] ;\r |
| 1262 | \r |
| 1263 | /* (machine-specific stuff) */\r |
| 1264 | \r |
| 1265 | hist_ptr->pc = pc ;\r |
| 1266 | hist_ptr->ir = our_ir ;\r |
| 1267 | hist_ptr->ac0 = AC[ 0 ] ;\r |
| 1268 | hist_ptr->ac1 = AC[ 1 ] ;\r |
| 1269 | hist_ptr->ac2 = AC[ 2 ] ;\r |
| 1270 | hist_ptr->ac3 = AC[ 3 ] ;\r |
| 1271 | hist_ptr->carry = C ;\r |
| 1272 | hist_ptr->fp = FP ;\r |
| 1273 | hist_ptr->sp = SP ;\r |
| 1274 | hist_ptr->devBusy = dev_busy ;\r |
| 1275 | hist_ptr->devDone = dev_done ;\r |
| 1276 | hist_ptr->devDisable = dev_disable ;\r |
| 1277 | hist_ptr->devIntr = int_req ;\r |
| 1278 | /* how 'bout state and AMASK? */\r |
| 1279 | return ( hist_p ) ;\r |
| 1280 | }\r |
| 1281 | return ( -1 ) ;\r |
| 1282 | } /* end of 'hist_save' */\r |
| 1283 | \r |
| 1284 | /* setup history save area (proposed global routine) */\r |
| 1285 | \r |
| 1286 | t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc )\r |
| 1287 | {\r |
| 1288 | int32 i, lnt ;\r |
| 1289 | t_stat r ;\r |
| 1290 | \r |
| 1291 | if ( cptr == NULL )\r |
| 1292 | {\r |
| 1293 | for (i = 0 ; i < hist_cnt ; ++i )\r |
| 1294 | {\r |
| 1295 | hist[i].pc = 0 ;\r |
| 1296 | hist[i].ir = HIST_IR_INVALID ;\r |
| 1297 | }\r |
| 1298 | hist_p = 0 ;\r |
| 1299 | return ( SCPE_OK ) ;\r |
| 1300 | }\r |
| 1301 | lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r) ;\r |
| 1302 | if ( (r != SCPE_OK) || (lnt && (lnt < HIST_MIN)) )\r |
| 1303 | {\r |
| 1304 | return ( SCPE_ARG ) ;\r |
| 1305 | }\r |
| 1306 | hist_p = 0;\r |
| 1307 | if ( hist_cnt )\r |
| 1308 | {\r |
| 1309 | free( hist ) ;\r |
| 1310 | hist_cnt = 0 ;\r |
| 1311 | hist = NULL ;\r |
| 1312 | }\r |
| 1313 | if ( lnt )\r |
| 1314 | {\r |
| 1315 | hist = (Hist_entry *) calloc( lnt, sizeof(Hist_entry) ) ;\r |
| 1316 | if ( hist == NULL )\r |
| 1317 | {\r |
| 1318 | return ( SCPE_MEM ) ;\r |
| 1319 | }\r |
| 1320 | hist_cnt = lnt ;\r |
| 1321 | }\r |
| 1322 | return ( SCPE_OK ) ;\r |
| 1323 | } /* end of 'hist_set' */\r |
| 1324 | \r |
| 1325 | \r |
| 1326 | int hist_fprintf( FILE * fp, int itemNum, Hist_entry * hptr )\r |
| 1327 | {\r |
| 1328 | t_value sim_eval ;\r |
| 1329 | \r |
| 1330 | if ( hptr )\r |
| 1331 | {\r |
| 1332 | if ( itemNum == 0 )\r |
| 1333 | {\r |
| 1334 | fprintf( fp, "\n\n" ) ;\r |
| 1335 | }\r |
| 1336 | fprintf( fp, "%05o / %06o %06o %06o %06o %06o %o ",\r |
| 1337 | (hptr->pc & 0x7FFF),\r |
| 1338 | (hptr->ir & 0xFFFF),\r |
| 1339 | (hptr->ac0 & 0xFFFF),\r |
| 1340 | (hptr->ac1 & 0xFFFF),\r |
| 1341 | (hptr->ac2 & 0xFFFF),\r |
| 1342 | (hptr->ac3 & 0xFFFF),\r |
| 1343 | ((hptr->carry) ? 1 : 0)\r |
| 1344 | ) ;\r |
| 1345 | if ( cpu_unit.flags & UNIT_STK /* Nova 3 or Nova 4 */ ) \r |
| 1346 | {\r |
| 1347 | fprintf( fp, "%06o %06o ", SP, FP ) ;\r |
| 1348 | }\r |
| 1349 | \r |
| 1350 | sim_eval = (hptr->ir & 0xFFFF) ;\r |
| 1351 | if ( (fprint_sym(fp, (hptr->pc & AMASK), &sim_eval, &cpu_unit, SWMASK ('M'))) > 0 )\r |
| 1352 | {\r |
| 1353 | fprintf( fp, "(undefined) %04o", (hptr->ir & 0xFFFF) ) ;\r |
| 1354 | }\r |
| 1355 | /*\r |
| 1356 | display ION flag value, pend value?\r |
| 1357 | display devBusy, devDone, devIntr info?\r |
| 1358 | */\r |
| 1359 | \r |
| 1360 | if ( 0 ) /* display INTRP codes? */\r |
| 1361 | {\r |
| 1362 | char tmp[ 500 ] ;\r |
| 1363 | \r |
| 1364 | devBitNames( hptr->devIntr, tmp, NULL ) ;\r |
| 1365 | fprintf( fp, " %s", tmp ) ;\r |
| 1366 | }\r |
| 1367 | \r |
| 1368 | fprintf( fp, "\n" ) ;\r |
| 1369 | }\r |
| 1370 | return ( 0 ) ;\r |
| 1371 | } /* end of 'hist_fprintf' */\r |
| 1372 | \r |
| 1373 | \r |
| 1374 | /* show execution history (proposed global routine) */\r |
| 1375 | \r |
| 1376 | t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc )\r |
| 1377 | {\r |
| 1378 | int32 k, di, lnt ;\r |
| 1379 | char * cptr = (char *) desc ;\r |
| 1380 | t_stat r ;\r |
| 1381 | Hist_entry * hptr ;\r |
| 1382 | \r |
| 1383 | \r |
| 1384 | if (hist_cnt == 0)\r |
| 1385 | {\r |
| 1386 | return ( SCPE_NOFNC ) ; /* enabled? */\r |
| 1387 | }\r |
| 1388 | if ( cptr )\r |
| 1389 | { /* number of entries specified */\r |
| 1390 | lnt = (int32) get_uint( cptr, 10, hist_cnt, &r ) ;\r |
| 1391 | if ( (r != SCPE_OK) || (lnt == 0) )\r |
| 1392 | {\r |
| 1393 | return ( SCPE_ARG ) ;\r |
| 1394 | }\r |
| 1395 | }\r |
| 1396 | else\r |
| 1397 | {\r |
| 1398 | lnt = hist_cnt ; /* display all entries */\r |
| 1399 | }\r |
| 1400 | di = hist_p - lnt; /* work forward */\r |
| 1401 | if ( di < 0 )\r |
| 1402 | {\r |
| 1403 | di = di + hist_cnt ;\r |
| 1404 | }\r |
| 1405 | \r |
| 1406 | for ( k = 0 ; k < lnt ; ++k )\r |
| 1407 | { /* print specified */\r |
| 1408 | hptr = &hist[ (++di) % hist_cnt] ; /* entry pointer */\r |
| 1409 | if ( hptr->ir != HIST_IR_INVALID ) /* valid entry? */\r |
| 1410 | {\r |
| 1411 | hist_fprintf( st, k, hptr ) ;\r |
| 1412 | } /* end else instruction */\r |
| 1413 | } /* end for */\r |
| 1414 | return SCPE_OK;\r |
| 1415 | } /* end of 'hist_show' */\r |
| 1416 | \r |
| 1417 | \r |
| 1418 | \r |
| 1419 | struct Dbits\r |
| 1420 | {\r |
| 1421 | int32 dBit ;\r |
| 1422 | int32 dInvertMask ;\r |
| 1423 | char * dName ;\r |
| 1424 | } devBits [] =\r |
| 1425 | \r |
| 1426 | {\r |
| 1427 | { INT_TRAP, 0, "TRAP" }, /* (in order of approximate DG interrupt mask priority) */\r |
| 1428 | { INT_ION, 0, "ION" },\r |
| 1429 | { INT_NO_ION_PENDING, 1, "IONPND" }, /* (invert this logic to provide cleaner display) */\r |
| 1430 | { INT_STK, 0, "STK" },\r |
| 1431 | { INT_PIT, 0, "PIT" },\r |
| 1432 | { INT_DKP, 0, "DKP" },\r |
| 1433 | { INT_DSK, 0, "DSK" },\r |
| 1434 | { INT_MTA, 0, "MTA" },\r |
| 1435 | { INT_LPT, 0, "LPT" },\r |
| 1436 | { INT_PTR, 0, "PTR" },\r |
| 1437 | { INT_PTP, 0, "PTP" },\r |
| 1438 | { INT_PLT, 0, "PLT" },\r |
| 1439 | { INT_CLK, 0, "CLK" },\r |
| 1440 | { INT_ALM, 0, "ALM" },\r |
| 1441 | { INT_QTY, 0, "QTY" },\r |
| 1442 | { INT_TTO1, 0, "TTO1" },\r |
| 1443 | { INT_TTI1, 0, "TTI1" },\r |
| 1444 | { INT_TTO, 0, "TTO" },\r |
| 1445 | { INT_TTI, 0, "TTI" },\r |
| 1446 | { 0, 0, NULL }\r |
| 1447 | } ;\r |
| 1448 | \r |
| 1449 | \r |
| 1450 | char * devBitNames( int32 flags, char * ptr, char * sepStr )\r |
| 1451 | {\r |
| 1452 | int a ;\r |
| 1453 | \r |
| 1454 | if ( ptr )\r |
| 1455 | {\r |
| 1456 | *ptr = 0 ;\r |
| 1457 | for ( a = 0 ; (devBits[a].dBit) ; ++a )\r |
| 1458 | if ( devBits[a].dBit & ((devBits[a].dInvertMask)? ~flags : flags) )\r |
| 1459 | {\r |
| 1460 | if ( *ptr )\r |
| 1461 | {\r |
| 1462 | strcat( ptr, (sepStr) ? sepStr : " " ) ;\r |
| 1463 | strcat( ptr, devBits[a].dName ) ;\r |
| 1464 | }\r |
| 1465 | else\r |
| 1466 | {\r |
| 1467 | strcpy( ptr, devBits[a].dName ) ;\r |
| 1468 | }\r |
| 1469 | }\r |
| 1470 | }\r |
| 1471 | return ( ptr ) ;\r |
| 1472 | } /* end of 'devBitNames' */\r |