Commit | Line | Data |
---|---|---|
196ba1fc PH |
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 |