| 1 | /* lgp_cpu.c: LGP CPU simulator\r |
| 2 | \r |
| 3 | Copyright (c) 2004-2005, 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 LGP-30 [LGP-21] CPU\r |
| 27 | \r |
| 28 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r |
| 29 | 04-Sep-05 RMS Fixed missing returns (found by Peter Schorn)\r |
| 30 | 04-Jan-05 RMS Modified VM pointer setup\r |
| 31 | \r |
| 32 | The system state for the LGP-30 [LGP-21] is:\r |
| 33 | \r |
| 34 | A<0:31> accumulator\r |
| 35 | C<0:11> counter (PC)\r |
| 36 | OVF overflow flag [LGP-21 only]\r |
| 37 | \r |
| 38 | The LGP-30 [LGP-21] has just one instruction format:\r |
| 39 | \r |
| 40 | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3\r |
| 41 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\r |
| 42 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r |
| 43 | |S| |opcode | | operand address | | \r |
| 44 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r |
| 45 | \r |
| 46 | LGP-30 instructions:\r |
| 47 | \r |
| 48 | <0,12:15> operation\r |
| 49 | \r |
| 50 | 0 stop\r |
| 51 | 1 A <- M[ea]\r |
| 52 | 2 M[ea]<addr> <- A<addr>\r |
| 53 | 3 M[ea]<addr> <- C + 1\r |
| 54 | 4 input\r |
| 55 | 5 A <- A / M[ea]\r |
| 56 | 6 A <- A * M[ea], low result\r |
| 57 | 7 A <- A * M[ea], high result\r |
| 58 | 8 output\r |
| 59 | 9 A <- A & M[ea]\r |
| 60 | A C <- ea\r |
| 61 | B C <- ea if A < 0\r |
| 62 | -B C <- ea if (A < 0) || T-switch set\r |
| 63 | C M[ea] <- A\r |
| 64 | D M[ea] <- A, A <- 0\r |
| 65 | E A <- A + M[ea]\r |
| 66 | F A <- A - M[ea]\r |
| 67 | \r |
| 68 | LGP-21 instructions:\r |
| 69 | \r |
| 70 | <0,12:15> operation\r |
| 71 | \r |
| 72 | 0 stop; sense and skip\r |
| 73 | -0 stop; sense overflow and skip\r |
| 74 | 1 A <- M[ea]\r |
| 75 | 2 M[ea]<addr> <- A<addr>\r |
| 76 | 3 M[ea]<addr> <- C + 1\r |
| 77 | 4 6b input\r |
| 78 | -4 4b input\r |
| 79 | 5 A <- A / M[ea]\r |
| 80 | 6 A <- A * M[ea], low result\r |
| 81 | 7 A <- A * M[ea], high result\r |
| 82 | 8 6b output\r |
| 83 | -8 4b output\r |
| 84 | 9 A <- A & M[ea]\r |
| 85 | A C <- ea\r |
| 86 | B C <- ea if A < 0\r |
| 87 | -B C <- ea if (A < 0) || T-switch set\r |
| 88 | C M[ea] <- A\r |
| 89 | D M[ea] <- A, A <- 0\r |
| 90 | E A <- A + M[ea]\r |
| 91 | F A <- A - M[ea]\r |
| 92 | \r |
| 93 | The LGP-30 [LGP-21] has 4096 32b words of memory. The low order\r |
| 94 | bit is always read and stored as 0. The LGP-30 uses a drum for\r |
| 95 | memory, with 64 tracks of 64 words. The LGP-21 uses a disk for\r |
| 96 | memory, with 32 tracks of 128 words.\r |
| 97 | \r |
| 98 | This routine is the instruction decode routine for the LGP-30\r |
| 99 | [LGP-21]. It is called from the simulator control program to\r |
| 100 | execute instructions in simulated memory, starting at the simulated\r |
| 101 | PC. It runs until 'reason' is set non-zero.\r |
| 102 | \r |
| 103 | General notes:\r |
| 104 | \r |
| 105 | 1. Reasons to stop. The simulator can be stopped by:\r |
| 106 | \r |
| 107 | STOP instruction\r |
| 108 | breakpoint encountered\r |
| 109 | overflow [LGP-30]\r |
| 110 | I/O error in I/O simulator\r |
| 111 | \r |
| 112 | 2. Interrupts. There are no interrupts.\r |
| 113 | \r |
| 114 | 3. Non-existent memory. All of memory always exists.\r |
| 115 | \r |
| 116 | 4. Adding I/O devices. The LGP-30 could not support additional\r |
| 117 | I/O devices. The LGP-21 could but none are known.\r |
| 118 | */\r |
| 119 | \r |
| 120 | #include "lgp_defs.h"\r |
| 121 | \r |
| 122 | #define PCQ_SIZE 64 /* must be 2**n */\r |
| 123 | #define PCQ_MASK (PCQ_SIZE - 1)\r |
| 124 | #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC - 1) & AMASK;\r |
| 125 | #define M16 0xFFFF\r |
| 126 | #define M32 0xFFFFFFFF\r |
| 127 | #define NEG(x) ((~(x) + 1) & DMASK)\r |
| 128 | #define ABS(x) (((x) & SIGN)? NEG (x): (x))\r |
| 129 | \r |
| 130 | uint32 M[MEMSIZE] = { 0 }; /* memory */\r |
| 131 | uint32 PC = 0; /* counter */\r |
| 132 | uint32 A = 0; /* accumulator */\r |
| 133 | uint32 IR = 0; /* instr register */\r |
| 134 | uint32 OVF = 0; /* overflow indicator */\r |
| 135 | uint32 t_switch = 0; /* transfer switch */\r |
| 136 | uint32 bp32 = 0; /* BP32 switch */\r |
| 137 | uint32 bp16 = 0; /* BP16 switch */\r |
| 138 | uint32 bp8 = 0; /* BP8 switch */\r |
| 139 | uint32 bp4 = 0; /* BP4 switch */\r |
| 140 | uint32 inp_strt = 0; /* input started */\r |
| 141 | uint32 inp_done = 0; /* input done */\r |
| 142 | uint32 out_strt = 0; /* output started */\r |
| 143 | uint32 out_done = 0; /* output done */\r |
| 144 | uint32 lgp21_sov = 0; /* LGP-21 sense pending */\r |
| 145 | int32 delay = 0;\r |
| 146 | int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r |
| 147 | int32 pcq_p = 0; /* PC queue ptr */\r |
| 148 | REG *pcq_r = NULL; /* PC queue reg ptr */\r |
| 149 | \r |
| 150 | extern int32 sim_interval;\r |
| 151 | extern int32 sim_int_char;\r |
| 152 | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r |
| 153 | extern int32 sim_step;\r |
| 154 | \r |
| 155 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r |
| 156 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r |
| 157 | t_stat cpu_reset (DEVICE *dptr);\r |
| 158 | t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 159 | t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc);\r |
| 160 | t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 161 | t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 162 | t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 163 | t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 164 | t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc);\r |
| 165 | t_stat cpu_one_inst (uint32 opc, uint32 ir);\r |
| 166 | uint32 Mul64 (uint32 a, uint32 b, uint32 *low);\r |
| 167 | t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q);\r |
| 168 | uint32 I_delay (uint32 opc, uint32 ea, uint32 op);\r |
| 169 | uint32 shift_in (uint32 a, uint32 dat, uint32 sh4);\r |
| 170 | \r |
| 171 | extern t_stat op_p (uint32 dev, uint32 ch);\r |
| 172 | extern t_stat op_i (uint32 dev, uint32 ch, uint32 sh4);\r |
| 173 | extern void lgp_vm_init (void);\r |
| 174 | \r |
| 175 | /* CPU data structures\r |
| 176 | \r |
| 177 | cpu_dev CPU device descriptor\r |
| 178 | cpu_unit CPU unit descriptor\r |
| 179 | cpu_reg CPU register list\r |
| 180 | cpu_mod CPU modifiers list\r |
| 181 | */\r |
| 182 | \r |
| 183 | UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_IN4B+UNIT_TTSS_D, MEMSIZE) };\r |
| 184 | \r |
| 185 | REG cpu_reg[] = {\r |
| 186 | { DRDATA (C, PC, 12), REG_VMAD },\r |
| 187 | { HRDATA (A, A, 32), REG_VMIO },\r |
| 188 | { HRDATA (IR, IR, 32), REG_VMIO },\r |
| 189 | { FLDATA (OVF, OVF, 0) },\r |
| 190 | { FLDATA (TSW, t_switch, 0) },\r |
| 191 | { FLDATA (BP32, bp32, 0) },\r |
| 192 | { FLDATA (BP16, bp16, 0) },\r |
| 193 | { FLDATA (BP8, bp8, 0) },\r |
| 194 | { FLDATA (BP4, bp4, 0) },\r |
| 195 | { FLDATA (INPST, inp_strt, 0) },\r |
| 196 | { FLDATA (INPDN, inp_done, 0) },\r |
| 197 | { FLDATA (OUTST, out_strt, 0) },\r |
| 198 | { FLDATA (OUTDN, out_done, 0) },\r |
| 199 | { DRDATA (DELAY, delay, 7) },\r |
| 200 | { BRDATA (CQ, pcq, 16, 12, PCQ_SIZE), REG_RO + REG_CIRC },\r |
| 201 | { HRDATA (CQP, pcq_p, 6), REG_HRO },\r |
| 202 | { HRDATA (WRU, sim_int_char, 8) },\r |
| 203 | { NULL }\r |
| 204 | };\r |
| 205 | \r |
| 206 | MTAB cpu_mod[] = {\r |
| 207 | { UNIT_LGP21, UNIT_LGP21, "LGP-21", "LGP21", &cpu_set_model, &cpu_show_model },\r |
| 208 | { UNIT_LGP21, 0, "LGP-30", "LGP30", &cpu_set_model, &cpu_show_model },\r |
| 209 | { UNIT_TTSS_D, UNIT_TTSS_D, 0, "TRACK" },\r |
| 210 | { UNIT_TTSS_D, 0, 0, "NORMAL" },\r |
| 211 | { UNIT_LGPH_D, UNIT_LGPH_D, 0, "LGPHEX" },\r |
| 212 | { UNIT_LGPH_D, 0, 0, "STANDARDHEX" },\r |
| 213 | { UNIT_MANI, UNIT_MANI, NULL, "MANUAL" },\r |
| 214 | { UNIT_MANI, 0, NULL, "TAPE" },\r |
| 215 | { UNIT_IN4B, UNIT_IN4B, NULL, "4B", &cpu_set_30opt },\r |
| 216 | { UNIT_IN4B, 0, NULL, "6B", &cpu_set_30opt },\r |
| 217 | { MTAB_XTD|MTAB_VDV, 0, NULL, "INPUT", &cpu_set_30opt_i },\r |
| 218 | { MTAB_XTD|MTAB_VDV, 0, NULL, "OUTPUT", &cpu_set_30opt_o },\r |
| 219 | { MTAB_XTD|MTAB_VDV, 0, NULL, "EXECUTE", &cpu_set_exec },\r |
| 220 | { MTAB_XTD|MTAB_VDV, 0, NULL, "FILL", &cpu_set_fill },\r |
| 221 | { 0 }\r |
| 222 | };\r |
| 223 | \r |
| 224 | DEVICE cpu_dev = {\r |
| 225 | "CPU", &cpu_unit, cpu_reg, cpu_mod,\r |
| 226 | 1, 10, 12, 1, 16, 32,\r |
| 227 | &cpu_ex, &cpu_dep, &cpu_reset,\r |
| 228 | NULL, NULL, NULL\r |
| 229 | };\r |
| 230 | \r |
| 231 | /* Timing tables */\r |
| 232 | \r |
| 233 | /* Optimization minima and maxima\r |
| 234 | Z B Y R I D N M P E U T H C A S */\r |
| 235 | \r |
| 236 | static const int32 min_30[16] = {\r |
| 237 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2\r |
| 238 | };\r |
| 239 | static const int32 max_30[16] = {\r |
| 240 | 7, 7, 7, 7, 7, 5, 8, 6, 7, 7, 0, 0, 7, 7, 7, 7\r |
| 241 | };\r |
| 242 | static const int32 min_21[16] = {\r |
| 243 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2\r |
| 244 | };\r |
| 245 | static const int32 max_21[16] = {\r |
| 246 | 0, 16, 16, 16, 0, 58, 81, 79, 0, 16, 0, 0, 16, 16, 16, 16\r |
| 247 | };\r |
| 248 | \r |
| 249 | static const uint32 log_to_phys_30[NSC_30] = { /* drum interlace chart */\r |
| 250 | 0, 57, 50, 43, 36, 29, 22, 15, 8 ,\r |
| 251 | 1, 58, 51, 44, 37, 30, 23, 16, 9 ,\r |
| 252 | 2, 59, 52, 45, 38, 31, 24, 17, 10,\r |
| 253 | 3, 60, 53, 46, 39, 32, 25, 18, 11,\r |
| 254 | 4, 61, 54, 47, 40, 33, 26, 19, 12,\r |
| 255 | 5, 62, 55, 48, 41, 32, 27, 20, 13,\r |
| 256 | 6, 63, 56, 49, 42, 33, 28, 21, 14,\r |
| 257 | 7\r |
| 258 | };\r |
| 259 | \r |
| 260 | static const uint32 log_to_phys_21[NSC_21] = { /* disk interlace chart */\r |
| 261 | 0, 64, 57, 121, 50, 114, 43, 107, 36, 100, 29, 93, 22, 86, 15, 79, 8, 72,\r |
| 262 | 1, 65, 58, 122, 51, 115, 44, 108, 37, 101, 30, 94, 23, 87, 16, 80, 9, 73,\r |
| 263 | 2, 66, 59, 123, 52, 116, 45, 109, 38, 102, 31, 95, 24, 88, 17, 81, 10, 74,\r |
| 264 | 3, 67, 60, 124, 53, 117, 46, 110, 39, 103, 32, 96, 25, 89, 18, 82, 11, 75,\r |
| 265 | 4, 68, 61, 125, 54, 118, 47, 111, 40, 104, 33, 97, 26, 90, 19, 83, 12, 76,\r |
| 266 | 5, 69, 62, 126, 55, 119, 48, 112, 41, 105, 34, 98, 27, 91, 20, 84, 12, 77,\r |
| 267 | 6, 70, 63, 127, 56, 120, 49, 113, 42, 106, 35, 99, 28, 92, 21, 85, 13, 78,\r |
| 268 | 7, 71\r |
| 269 | };\r |
| 270 | \r |
| 271 | t_stat sim_instr (void)\r |
| 272 | {\r |
| 273 | t_stat r = 0;\r |
| 274 | uint32 oPC;\r |
| 275 | \r |
| 276 | /* Restore register state */\r |
| 277 | \r |
| 278 | PC = PC & AMASK; /* mask PC */\r |
| 279 | sim_cancel_step (); /* defang SCP step */\r |
| 280 | if (lgp21_sov) { /* stop sense pending? */\r |
| 281 | lgp21_sov = 0;\r |
| 282 | if (!OVF) PC = (PC + 1) & AMASK; /* ovf off? skip */\r |
| 283 | else OVF = 0; /* on? reset */\r |
| 284 | }\r |
| 285 | \r |
| 286 | /* Main instruction fetch/decode loop */\r |
| 287 | \r |
| 288 | do {\r |
| 289 | if (sim_interval <= 0) { /* check clock queue */\r |
| 290 | if (r = sim_process_event ()) break;\r |
| 291 | }\r |
| 292 | \r |
| 293 | if (delay > 0) { /* delay to next instr */\r |
| 294 | delay = delay - 1; /* count down delay */\r |
| 295 | sim_interval = sim_interval - 1;\r |
| 296 | continue; /* skip execution */\r |
| 297 | }\r |
| 298 | \r |
| 299 | if (sim_brk_summ && /* breakpoint? */\r |
| 300 | sim_brk_test (PC, SWMASK ('E'))) {\r |
| 301 | r = STOP_IBKPT; /* stop simulation */\r |
| 302 | break;\r |
| 303 | }\r |
| 304 | \r |
| 305 | IR = Read (oPC = PC); /* get instruction */\r |
| 306 | PC = (PC + 1) & AMASK; /* increment PC */\r |
| 307 | sim_interval = sim_interval - 1;\r |
| 308 | \r |
| 309 | if (r = cpu_one_inst (oPC, IR)) { /* one instr; error? */\r |
| 310 | if (r == STOP_STALL) { /* stall? */\r |
| 311 | PC = oPC; /* back up PC */\r |
| 312 | delay = r = 0; /* no delay */\r |
| 313 | }\r |
| 314 | else break;\r |
| 315 | }\r |
| 316 | \r |
| 317 | if (sim_step && (--sim_step <= 0)) /* do step count */\r |
| 318 | r = SCPE_STOP;\r |
| 319 | \r |
| 320 | } while (r == 0); /* loop until halted */\r |
| 321 | pcq_r->qptr = pcq_p; /* update pc q ptr */\r |
| 322 | return r;\r |
| 323 | }\r |
| 324 | \r |
| 325 | /* Execute one instruction */\r |
| 326 | \r |
| 327 | t_stat cpu_one_inst (uint32 opc, uint32 ir)\r |
| 328 | {\r |
| 329 | uint32 ea, op, dat, res, dev, sh4, ch;\r |
| 330 | t_bool ovf_this_cycle = FALSE;\r |
| 331 | t_stat reason = 0;\r |
| 332 | \r |
| 333 | op = I_GETOP (ir); /* opcode */\r |
| 334 | ea = I_GETEA (ir); /* address */\r |
| 335 | switch (op) { /* case on opcode */\r |
| 336 | \r |
| 337 | /* Loads, stores, transfers instructions */\r |
| 338 | \r |
| 339 | case OP_B: /* bring */\r |
| 340 | A = Read (ea); /* A <- M[ea] */\r |
| 341 | delay = I_delay (opc, ea, op);\r |
| 342 | break;\r |
| 343 | \r |
| 344 | case OP_H: /* hold */\r |
| 345 | Write (ea, A); /* M[ea] <- A */\r |
| 346 | delay = I_delay (opc, ea, op);\r |
| 347 | break;\r |
| 348 | \r |
| 349 | case OP_C: /* clear */\r |
| 350 | Write (ea, A); /* M[ea] <- A */\r |
| 351 | A = 0; /* A <- 0 */\r |
| 352 | delay = I_delay (opc, ea, op);\r |
| 353 | break;\r |
| 354 | \r |
| 355 | case OP_Y: /* store address */\r |
| 356 | dat = Read (ea); /* get operand */\r |
| 357 | dat = (dat & ~I_EA) | (A & I_EA); /* merge address */\r |
| 358 | Write (ea, dat);\r |
| 359 | delay = I_delay (opc, ea, op);\r |
| 360 | break;\r |
| 361 | \r |
| 362 | case OP_R: /* return address */\r |
| 363 | dat = Read (ea); /* get operand */\r |
| 364 | dat = (dat & ~I_EA) | (((PC + 1) & AMASK) << I_V_EA);\r |
| 365 | Write (ea, dat);\r |
| 366 | delay = I_delay (opc, ea, op);\r |
| 367 | break;\r |
| 368 | \r |
| 369 | case OP_U: /* uncond transfer */\r |
| 370 | PCQ_ENTRY;\r |
| 371 | PC = ea; /* transfer */\r |
| 372 | delay = I_delay (opc, ea, op);\r |
| 373 | break;\r |
| 374 | \r |
| 375 | case OP_T: /* conditional transfer */\r |
| 376 | if ((A & SIGN) || /* A < 0 or */\r |
| 377 | ((ir & SIGN) && t_switch)) { /* -T and Tswitch set? */\r |
| 378 | PCQ_ENTRY;\r |
| 379 | PC = ea; /* transfer */\r |
| 380 | }\r |
| 381 | delay = I_delay (opc, ea, op);\r |
| 382 | break;\r |
| 383 | \r |
| 384 | /* Arithmetic and logical instructions */\r |
| 385 | \r |
| 386 | case OP_A: /* add */\r |
| 387 | dat = Read (ea); /* get operand */\r |
| 388 | res = (A + dat) & DMASK; /* add */\r |
| 389 | if ((~A ^ dat) & (dat ^ res) & SIGN) /* calc overflow */\r |
| 390 | ovf_this_cycle = TRUE;\r |
| 391 | A = res; /* save result */\r |
| 392 | delay = I_delay (opc, ea, op);\r |
| 393 | break;\r |
| 394 | \r |
| 395 | case OP_S: /* sub */\r |
| 396 | dat = Read (ea); /* get operand */\r |
| 397 | res = (A - dat) & DMASK; /* subtract */\r |
| 398 | if ((A ^ dat) & (~dat ^ res) & SIGN) /* calc overflow */\r |
| 399 | ovf_this_cycle = TRUE;\r |
| 400 | A = res;\r |
| 401 | delay = I_delay (opc, ea, op);\r |
| 402 | break;\r |
| 403 | \r |
| 404 | case OP_M: /* multiply high */\r |
| 405 | dat = Read (ea); /* get operand */\r |
| 406 | A = (Mul64 (A, dat, NULL) << 1) & DMASK; /* multiply */\r |
| 407 | delay = I_delay (opc, ea, op);\r |
| 408 | break;\r |
| 409 | \r |
| 410 | case OP_N: /* multiply low */\r |
| 411 | dat = Read (ea); /* get operand */\r |
| 412 | Mul64 (A, dat, &res); /* multiply */\r |
| 413 | A = res; /* keep low result */\r |
| 414 | delay = I_delay (opc, ea, op); /* total delay */\r |
| 415 | break;\r |
| 416 | \r |
| 417 | case OP_D: /* divide */\r |
| 418 | dat = Read (ea); /* get operand */\r |
| 419 | if (Div32 (A, dat, &A)) ovf_this_cycle = TRUE; /* divide; overflow? */\r |
| 420 | delay = I_delay (opc, ea, op);\r |
| 421 | break;\r |
| 422 | \r |
| 423 | case OP_E: /* extract */\r |
| 424 | dat = Read (ea); /* get operand */\r |
| 425 | A = A & dat; /* and */\r |
| 426 | delay = I_delay (opc, ea, op);\r |
| 427 | break;\r |
| 428 | \r |
| 429 | /* IO instructions */\r |
| 430 | \r |
| 431 | case OP_P: /* output */\r |
| 432 | if (Q_LGP21) { /* LGP-21 */\r |
| 433 | ch = A >> 26; /* char, 6b */\r |
| 434 | if (ir & SIGN) ch = (ch & 0x3C) | 2; /* 4b? convert */\r |
| 435 | dev = I_GETTK (ir); /* device select */\r |
| 436 | }\r |
| 437 | else { /* LGP-30 */\r |
| 438 | ch = I_GETTK (ir); /* char, always 6b */\r |
| 439 | dev = Q_OUTPT? DEV_PT: DEV_TT; /* device select */\r |
| 440 | }\r |
| 441 | reason = op_p (dev & DEV_MASK, ch); /* output */\r |
| 442 | delay = I_delay (sim_grtime (), ea, op); /* next instruction */\r |
| 443 | break;\r |
| 444 | \r |
| 445 | case OP_I: /* input */\r |
| 446 | if (Q_LGP21) { /* LGP-21 */\r |
| 447 | ch = 0; /* initial shift */\r |
| 448 | sh4 = ir & SIGN; /* 4b/6b select */\r |
| 449 | dev = I_GETTK (ir); /* device select */\r |
| 450 | }\r |
| 451 | else { /* LGP-30 */\r |
| 452 | ch = I_GETTK (ir); /* initial shift */\r |
| 453 | sh4 = Q_IN4B; /* 4b/6b select */\r |
| 454 | dev = Q_INPT? DEV_PT: DEV_TT; /* device select */\r |
| 455 | }\r |
| 456 | if (dev == DEV_SHIFT) /* shift? */\r |
| 457 | A = shift_in (A, 0, sh4); /* shift 4/6b */\r |
| 458 | else reason = op_i (dev & DEV_MASK, ch, sh4); /* input */\r |
| 459 | delay = I_delay (sim_grtime (), ea, op); /* next instruction */\r |
| 460 | break;\r |
| 461 | \r |
| 462 | case OP_Z:\r |
| 463 | if (Q_LGP21) { /* LGP-21 */\r |
| 464 | if (ea & 0xF80) { /* no stop? */\r |
| 465 | if (((ea & 0x800) && !bp32) || /* skip if any */\r |
| 466 | ((ea & 0x400) && !bp16) || /* selected switch */\r |
| 467 | ((ea & 0x200) && !bp8) || /* is off */\r |
| 468 | ((ea & 0x100) && !bp4) || /* or if */\r |
| 469 | ((ir & SIGN) && !OVF)) /* ovf sel and off */\r |
| 470 | PC = (PC + 1) & AMASK;\r |
| 471 | if (ir & SIGN) OVF = 0; /* -Z? clr overflow */\r |
| 472 | }\r |
| 473 | else { /* stop */\r |
| 474 | lgp21_sov = (ir & SIGN)? 1: 0; /* pending sense? */\r |
| 475 | reason = STOP_STOP; /* stop */\r |
| 476 | }\r |
| 477 | }\r |
| 478 | else { /* LGP-30 */\r |
| 479 | if (out_done) out_done = 0; /* P complete? */\r |
| 480 | else if (((ea & 0x800) && bp32) || /* bpt switch set? */\r |
| 481 | ((ea & 0x400) && bp16) ||\r |
| 482 | ((ea & 0x200) && bp8) ||\r |
| 483 | ((ea & 0x100) && bp4)) ; /* don't stop or stall */\r |
| 484 | else if (out_strt) reason = STOP_STALL; /* P pending? stall */\r |
| 485 | else reason = STOP_STOP; /* no, stop */\r |
| 486 | }\r |
| 487 | delay = I_delay (sim_grtime (), ea, op); /* next instruction */\r |
| 488 | break; /* end switch */\r |
| 489 | }\r |
| 490 | \r |
| 491 | if (ovf_this_cycle) {\r |
| 492 | if (Q_LGP21) OVF = 1; /* LGP-21? set OVF */\r |
| 493 | else reason = STOP_OVF; /* LGP-30? stop */\r |
| 494 | }\r |
| 495 | return reason;\r |
| 496 | }\r |
| 497 | \r |
| 498 | /* Support routines */\r |
| 499 | \r |
| 500 | uint32 Read (uint32 ea)\r |
| 501 | {\r |
| 502 | return M[ea] & MMASK;\r |
| 503 | }\r |
| 504 | \r |
| 505 | void Write (uint32 ea, uint32 dat)\r |
| 506 | {\r |
| 507 | M[ea] = dat & MMASK;\r |
| 508 | return;\r |
| 509 | }\r |
| 510 | \r |
| 511 | /* Input shift */\r |
| 512 | \r |
| 513 | uint32 shift_in (uint32 a, uint32 dat, uint32 sh4)\r |
| 514 | {\r |
| 515 | if (sh4) return (((a << 4) | (dat >> 2)) & DMASK);\r |
| 516 | return (((a << 6) | dat) & DMASK);\r |
| 517 | }\r |
| 518 | \r |
| 519 | /* 32b * 32b multiply, signed */\r |
| 520 | \r |
| 521 | uint32 Mul64 (uint32 a, uint32 b, uint32 *low)\r |
| 522 | {\r |
| 523 | uint32 sgn = a ^ b;\r |
| 524 | uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2;\r |
| 525 | \r |
| 526 | if ((a == 0) || (b == 0)) { /* zero argument? */\r |
| 527 | if (low) *low = 0;\r |
| 528 | return 0;\r |
| 529 | }\r |
| 530 | a = ABS (a);\r |
| 531 | b = ABS (b);\r |
| 532 | ah = (a >> 16) & M16; /* split operands */\r |
| 533 | bh = (b >> 16) & M16; /* into 16b chunks */\r |
| 534 | al = a & M16;\r |
| 535 | bl = b & M16;\r |
| 536 | rhi = ah * bh; /* high result */\r |
| 537 | rmid1 = ah * bl;\r |
| 538 | rmid2 = al * bh;\r |
| 539 | rlo = al * bl;\r |
| 540 | rhi = rhi + ((rmid1 >> 16) & M16) + ((rmid2 >> 16) & M16);\r |
| 541 | rmid1 = (rlo + (rmid1 << 16)) & M32; /* add mid1 to lo */\r |
| 542 | if (rmid1 < rlo) rhi = rhi + 1; /* carry? incr hi */\r |
| 543 | rmid2 = (rmid1 + (rmid2 << 16)) & M32; /* add mid2 to to */\r |
| 544 | if (rmid2 < rmid1) rhi = rhi + 1; /* carry? incr hi */\r |
| 545 | if (sgn & SIGN) { /* result negative? */\r |
| 546 | rmid2 = NEG (rmid2); /* negate */\r |
| 547 | rhi = (~rhi + (rmid2 == 0)) & M32;\r |
| 548 | }\r |
| 549 | if (low) *low = rmid2; /* low result */\r |
| 550 | return rhi & M32;\r |
| 551 | }\r |
| 552 | \r |
| 553 | /* 32b/32b divide (done as 32b'0/32b) */\r |
| 554 | \r |
| 555 | t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q)\r |
| 556 | {\r |
| 557 | uint32 sgn = dvd ^ dvr;\r |
| 558 | uint32 i, quo;\r |
| 559 | \r |
| 560 | dvd = ABS (dvd);\r |
| 561 | dvr = ABS (dvr);\r |
| 562 | if (dvd >= dvr) return TRUE;\r |
| 563 | for (i = quo = 0; i < 31; i++) { /* 31 iterations */\r |
| 564 | quo = quo << 1; /* shift quotient */\r |
| 565 | dvd = dvd << 1; /* shift dividend */\r |
| 566 | if (dvd >= dvr) { /* step work? */\r |
| 567 | dvd = (dvd - dvr) & M32; /* subtract dvr */\r |
| 568 | quo = quo + 1;\r |
| 569 | }\r |
| 570 | }\r |
| 571 | quo = (quo + 1) & MMASK; /* round low bit */\r |
| 572 | if (sgn & SIGN) quo = NEG (quo); /* result -? */\r |
| 573 | if (q) *q = quo; /* return quo */\r |
| 574 | return FALSE; /* no overflow */\r |
| 575 | }\r |
| 576 | \r |
| 577 | /* Rotational delay */\r |
| 578 | \r |
| 579 | uint32 I_delay (uint32 opc, uint32 ea, uint32 op)\r |
| 580 | {\r |
| 581 | uint32 tmin = Q_LGP21? min_21[op]: min_30[op];\r |
| 582 | uint32 tmax = Q_LGP21? max_21[op]: max_30[op];\r |
| 583 | uint32 nsc, curp, newp, oprp, pcdelta, opdelta;\r |
| 584 | \r |
| 585 | if (Q_LGP21) { /* LGP21 */\r |
| 586 | nsc = NSC_21; /* full rotation delay */\r |
| 587 | curp = log_to_phys_21[opc & SCMASK_21]; /* current phys pos */\r |
| 588 | newp = log_to_phys_21[PC & SCMASK_21]; /* new PC phys pos */\r |
| 589 | oprp = log_to_phys_21[ea & SCMASK_21]; /* ea phys pos */\r |
| 590 | pcdelta = (newp - curp + NSC_21) & SCMASK_21;\r |
| 591 | opdelta = (oprp - curp + NSC_21) & SCMASK_21;\r |
| 592 | }\r |
| 593 | else {\r |
| 594 | nsc = NSC_30;\r |
| 595 | curp = log_to_phys_30[opc & SCMASK_30];\r |
| 596 | newp = log_to_phys_30[PC & SCMASK_30];\r |
| 597 | oprp = log_to_phys_30[ea & SCMASK_30];\r |
| 598 | pcdelta = (newp - curp + NSC_30) & SCMASK_30;\r |
| 599 | opdelta = (oprp - curp + NSC_30) & SCMASK_30;\r |
| 600 | }\r |
| 601 | if (tmax == 0) { /* skip ea calc? */\r |
| 602 | if (pcdelta >= tmin) return pcdelta - 1; /* new PC >= min? */\r |
| 603 | return pcdelta + nsc - 1;\r |
| 604 | }\r |
| 605 | if ((opdelta >= tmin) && (opdelta <= tmax)) return pcdelta - 1;\r |
| 606 | return pcdelta + nsc - 1;\r |
| 607 | }\r |
| 608 | \r |
| 609 | /* Reset routine */\r |
| 610 | \r |
| 611 | t_stat cpu_reset (DEVICE *dptr)\r |
| 612 | {\r |
| 613 | OVF = 0;\r |
| 614 | inp_strt = 0;\r |
| 615 | inp_done = 0;\r |
| 616 | out_strt = 0;\r |
| 617 | out_done = 0;\r |
| 618 | lgp21_sov = 0;\r |
| 619 | delay = 0;\r |
| 620 | lgp_vm_init ();\r |
| 621 | pcq_r = find_reg ("CQ", NULL, dptr);\r |
| 622 | if (pcq_r) pcq_r->qptr = 0;\r |
| 623 | else return SCPE_IERR;\r |
| 624 | sim_brk_types = sim_brk_dflt = SWMASK ('E');\r |
| 625 | return SCPE_OK;\r |
| 626 | }\r |
| 627 | \r |
| 628 | /* Validate option, must be LGP30 */\r |
| 629 | \r |
| 630 | t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 631 | {\r |
| 632 | if (Q_LGP21) return SCPE_ARG;\r |
| 633 | return SCPE_OK;\r |
| 634 | }\r |
| 635 | \r |
| 636 | /* Validate input option, must be LGP30 */\r |
| 637 | \r |
| 638 | t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 639 | {\r |
| 640 | if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG;\r |
| 641 | if (strcmp (cptr, "TTI") == 0) uptr->flags = uptr->flags & ~UNIT_INPT;\r |
| 642 | else if (strcmp (cptr, "PTR") == 0) uptr->flags = uptr->flags | UNIT_INPT;\r |
| 643 | else return SCPE_ARG;\r |
| 644 | return SCPE_OK;\r |
| 645 | }\r |
| 646 | \r |
| 647 | /* Validate output option, must be LGP30 */\r |
| 648 | \r |
| 649 | t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 650 | {\r |
| 651 | if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG;\r |
| 652 | if (strcmp (cptr, "TTO") == 0) uptr->flags = uptr->flags & ~UNIT_OUTPT;\r |
| 653 | else if (strcmp (cptr, "PTP") == 0) uptr->flags = uptr->flags | UNIT_OUTPT;\r |
| 654 | else return SCPE_ARG;\r |
| 655 | return SCPE_OK;\r |
| 656 | }\r |
| 657 | \r |
| 658 | /* Set CPU to LGP21 or LPG30 */\r |
| 659 | \r |
| 660 | t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 661 | {\r |
| 662 | if (val) uptr->flags = uptr->flags & ~(UNIT_IN4B|UNIT_INPT|UNIT_OUTPT);\r |
| 663 | return reset_all (0);\r |
| 664 | }\r |
| 665 | \r |
| 666 | /* Show CPU type and all options */\r |
| 667 | \r |
| 668 | t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc)\r |
| 669 | {\r |
| 670 | fputs (Q_LGP21? "LGP-21": "LGP-30", st);\r |
| 671 | if (uptr->flags & UNIT_TTSS_D) fputs (", track/sector", st);\r |
| 672 | if (uptr->flags & UNIT_LGPH_D) fputs (", LGP hex", st);\r |
| 673 | fputs (Q_MANI? ", manual": ", tape", st);\r |
| 674 | if (!Q_LGP21) {\r |
| 675 | fputs (Q_IN4B? ", 4b": ", 6b", st);\r |
| 676 | fputs (Q_INPT? ", in=PTR": ", in=TTI", st);\r |
| 677 | fputs (Q_OUTPT? ", out=PTP": ", out=TTO", st);\r |
| 678 | }\r |
| 679 | return SCPE_OK;\r |
| 680 | }\r |
| 681 | \r |
| 682 | /* Memory examine */\r |
| 683 | \r |
| 684 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r |
| 685 | {\r |
| 686 | if (addr >= MEMSIZE) return SCPE_NXM;\r |
| 687 | if (vptr != NULL) *vptr = Read (addr);\r |
| 688 | return SCPE_OK;\r |
| 689 | }\r |
| 690 | \r |
| 691 | /* Memory deposit */\r |
| 692 | \r |
| 693 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r |
| 694 | {\r |
| 695 | if (addr >= MEMSIZE) return SCPE_NXM;\r |
| 696 | Write (addr, val);\r |
| 697 | return SCPE_OK;\r |
| 698 | }\r |
| 699 | \r |
| 700 | /* Execute */\r |
| 701 | \r |
| 702 | t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 703 | {\r |
| 704 | uint32 inst;\r |
| 705 | t_stat r;\r |
| 706 | \r |
| 707 | if (cptr) {\r |
| 708 | inst = get_uint (cptr, 16, DMASK, &r);\r |
| 709 | if (r != SCPE_OK) return r;\r |
| 710 | }\r |
| 711 | else inst = IR;\r |
| 712 | while ((r = cpu_one_inst (PC, inst)) == STOP_STALL) {\r |
| 713 | sim_interval = 0;\r |
| 714 | if (r = sim_process_event ()) return r;\r |
| 715 | }\r |
| 716 | return r;\r |
| 717 | }\r |
| 718 | \r |
| 719 | /* Fill */\r |
| 720 | \r |
| 721 | t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc)\r |
| 722 | {\r |
| 723 | uint32 inst;\r |
| 724 | t_stat r;\r |
| 725 | \r |
| 726 | if (cptr) {\r |
| 727 | inst = get_uint (cptr, 16, DMASK, &r);\r |
| 728 | if (r != SCPE_OK) return r;\r |
| 729 | IR = inst;\r |
| 730 | }\r |
| 731 | else IR = A;\r |
| 732 | return SCPE_OK;\r |
| 733 | }\r |