Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* sds_cpu.c: SDS 940 CPU simulator\r |
2 | \r | |
3 | Copyright (c) 2001-2007, 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 central processor\r | |
27 | rtc real time clock\r | |
28 | \r | |
29 | 28-Apr-07 RMS Removed clock initialization\r | |
30 | 29-Dec-06 RMS Fixed breakpoint variable declarations\r | |
31 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r | |
32 | 07-Nov-04 RMS Added instruction history\r | |
33 | 01-Mar-03 RMS Added SET/SHOW RTC FREQ support\r | |
34 | \r | |
35 | The system state for the SDS 940 is:\r | |
36 | \r | |
37 | A<0:23> A register\r | |
38 | B<0:23> B register\r | |
39 | X<0:23> X (index) register\r | |
40 | OV overflow indicator\r | |
41 | P<0:13> program counter\r | |
42 | nml_mode compatible (1) vs 940 (0) mode\r | |
43 | usr_mode user (1) vs monitor (0) mode\r | |
44 | RL1<0:23> user map low\r | |
45 | RL2<0:23> user map high\r | |
46 | RL4<12:23> monitor map high\r | |
47 | EM2<0:2> memory extension, block 2\r | |
48 | EM3<0:2> memory extension, block 3\r | |
49 | bpt breakpoint switches\r | |
50 | \r | |
51 | The SDS 940 has three instruction format -- memory reference, register change,\r | |
52 | and I/O. The memory reference format is:\r | |
53 | \r | |
54 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23\r | |
55 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
56 | | U| X| P| opcode |IN| address |\r | |
57 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
58 | \r | |
59 | U force user mode addressing (monitor mode only)\r | |
60 | X indexed\r | |
61 | P opcode is a programmed operator\r | |
62 | opcode opcode\r | |
63 | IN indirect addressing\r | |
64 | address virtual address\r | |
65 | \r | |
66 | Virtual addresses are 14b. Depending on the operating mode (normal, user,\r | |
67 | or monitor), virtual addresses are translated to 15b or 16b physical addresses.\r | |
68 | \r | |
69 | normal virtual [000000:017777] are unmapped\r | |
70 | EM2 and EM3 extend virtual [020000:037777] to 15b\r | |
71 | user RL1 and RL2 map virtual [000000:037777] to 16b\r | |
72 | monitor virtual [000000:017777] are unmapped\r | |
73 | EM2 extends virtual [020000:027777] to 15b\r | |
74 | RL4 maps virtual [030000:037777] to 16b\r | |
75 | \r | |
76 | The register change format is:\r | |
77 | \r | |
78 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23\r | |
79 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
80 | | 0| m| 0| opcode | microcoded register change instruction |\r | |
81 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
82 | \r | |
83 | The I/O format is:\r | |
84 | \r | |
85 | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23\r | |
86 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
87 | | 0|CH| 0| opcode |mode | I/O function |\r | |
88 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r | |
89 | \r | |
90 | This routine is the instruction decode routine for the SDS 940.\r | |
91 | It is called from the simulator control program to execute\r | |
92 | instructions in simulated memory, starting at the simulated PC.\r | |
93 | It runs until 'reason' is set non-zero.\r | |
94 | \r | |
95 | General notes:\r | |
96 | \r | |
97 | 1. Reasons to stop. The simulator can be stopped by:\r | |
98 | \r | |
99 | HALT instruction\r | |
100 | breakpoint encountered\r | |
101 | invalid instruction and stop_invins flag set\r | |
102 | invalid I/O device and stop_invdev flag set\r | |
103 | invalid I/O operation and stop_inviop flag set\r | |
104 | I/O error in I/O simulator\r | |
105 | indirect loop exceeding limit\r | |
106 | EXU loop exceeding limit\r | |
107 | mapping exception in interrupt or trap instruction\r | |
108 | \r | |
109 | 2. Interrupts. The interrupt structure consists of the following:\r | |
110 | \r | |
111 | int_req interrupt requests (low bit reserved)\r | |
112 | api_lvl active interrupt levels\r | |
113 | int_reqhi highest interrupt request\r | |
114 | api_lvlhi highest interrupt service (0 if none)\r | |
115 | ion interrupt enable\r | |
116 | ion_defer interrupt defer (one instruction)\r | |
117 | \r | |
118 | 3. Channels. The SDS 940 has a channel-based I/O structure. Each\r | |
119 | channel is represented by a set of registers. Channels test the\r | |
120 | I/O transfer requests from devices, which are kept in xfr_req.\r | |
121 | \r | |
122 | 4. Non-existent memory. On the SDS 940, reads to non-existent memory\r | |
123 | return zero, and writes are ignored. In the simulator, the\r | |
124 | largest possible memory is instantiated and initialized to zero.\r | |
125 | Thus, only writes need be checked against actual memory size.\r | |
126 | \r | |
127 | 5. Adding I/O devices. These modules must be modified:\r | |
128 | \r | |
129 | sds_defs.h add interrupt, transfer, and alert definitions\r | |
130 | sds_io.c add alert dispatches aldisp\r | |
131 | sds_sys.c add pointer to data structures to sim_devices\r | |
132 | */\r | |
133 | \r | |
134 | #include "sds_defs.h"\r | |
135 | \r | |
136 | #define PCQ_SIZE 64 /* must be 2**n */\r | |
137 | #define PCQ_MASK (PCQ_SIZE - 1)\r | |
138 | #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = pc\r | |
139 | #define UNIT_V_MSIZE (UNIT_V_GENIE + 1) /* dummy mask */\r | |
140 | #define UNIT_MSIZE (1 << UNIT_V_MSIZE)\r | |
141 | \r | |
142 | #define HIST_XCT 1 /* instruction */\r | |
143 | #define HIST_INT 2 /* interrupt cycle */\r | |
144 | #define HIST_TRP 3 /* trap cycle */\r | |
145 | #define HIST_MIN 64\r | |
146 | #define HIST_MAX 65536\r | |
147 | #define HIST_NOEA 0x40000000\r | |
148 | \r | |
149 | typedef struct {\r | |
150 | uint32 typ;\r | |
151 | uint32 pc;\r | |
152 | uint32 ir;\r | |
153 | uint32 a;\r | |
154 | uint32 b;\r | |
155 | uint32 x;\r | |
156 | uint32 ea;\r | |
157 | } InstHistory;\r | |
158 | \r | |
159 | uint32 M[MAXMEMSIZE] = { 0 }; /* memory */\r | |
160 | uint32 A, B, X; /* registers */\r | |
161 | uint32 P; /* program counter */\r | |
162 | uint32 OV; /* overflow */\r | |
163 | uint32 xfr_req = 0; /* xfr req */\r | |
164 | uint32 ion = 0; /* int enable */\r | |
165 | uint32 ion_defer = 0; /* int defer */\r | |
166 | uint32 int_req = 0; /* int requests */\r | |
167 | uint32 int_reqhi = 0; /* highest int request */\r | |
168 | uint32 api_lvl = 0; /* api active */\r | |
169 | uint32 api_lvlhi = 0; /* highest api active */\r | |
170 | t_bool chan_req; /* chan request */\r | |
171 | uint32 nml_mode = 1; /* normal mode */\r | |
172 | uint32 usr_mode = 0; /* user mode */\r | |
173 | uint32 mon_usr_trap = 0; /* mon-user trap */\r | |
174 | uint32 EM2 = 2, EM3 = 3; /* extension registers */\r | |
175 | uint32 RL1, RL2, RL4; /* relocation maps */\r | |
176 | uint32 bpt; /* breakpoint switches */\r | |
177 | uint32 alert; /* alert dispatch */\r | |
178 | uint32 em2_dyn, em3_dyn; /* extensions, dynamic */\r | |
179 | uint32 usr_map[8]; /* user map, dynamic */\r | |
180 | uint32 mon_map[8]; /* mon map, dynamic */\r | |
181 | int32 ind_lim = 32; /* indirect limit */\r | |
182 | int32 exu_lim = 32; /* EXU limit */\r | |
183 | int32 cpu_genie = 0; /* Genie flag */\r | |
184 | int32 cpu_astop = 0; /* address stop */\r | |
185 | int32 stop_invins = 1; /* stop inv inst */\r | |
186 | int32 stop_invdev = 1; /* stop inv dev */\r | |
187 | int32 stop_inviop = 1; /* stop inv io op */\r | |
188 | uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */\r | |
189 | int32 pcq_p = 0; /* PC queue ptr */\r | |
190 | REG *pcq_r = NULL; /* PC queue reg ptr */\r | |
191 | int32 hst_p = 0; /* history pointer */\r | |
192 | int32 hst_lnt = 0; /* history length */\r | |
193 | InstHistory *hst = NULL; /* instruction history */\r | |
194 | int32 rtc_pie = 0; /* rtc pulse ie */\r | |
195 | int32 rtc_tps = 60; /* rtc ticks/sec */\r | |
196 | \r | |
197 | extern int32 sim_int_char;\r | |
198 | extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */\r | |
199 | \r | |
200 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r | |
201 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r | |
202 | t_stat cpu_reset (DEVICE *dptr);\r | |
203 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
204 | t_stat cpu_set_type (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
205 | t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
206 | t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
207 | t_stat Ea (uint32 wd, uint32 *va);\r | |
208 | t_stat EaSh (uint32 wd, uint32 *va);\r | |
209 | t_stat Read (uint32 va, uint32 *dat);\r | |
210 | t_stat Write (uint32 va, uint32 dat);\r | |
211 | void set_dyn_map (void);\r | |
212 | uint32 api_findreq (void);\r | |
213 | void api_dismiss (void);\r | |
214 | uint32 Add24 (uint32 s1, uint32 s2, uint32 cin);\r | |
215 | uint32 AddM24 (uint32 s1, uint32 s2);\r | |
216 | void Mul48 (uint32 mplc, uint32 mplr);\r | |
217 | void Div48 (uint32 dvdh, uint32 dvdl, uint32 dvr);\r | |
218 | void RotR48 (uint32 sc);\r | |
219 | void ShfR48 (uint32 sc, uint32 sgn);\r | |
220 | t_stat one_inst (uint32 inst, uint32 pc, uint32 mode);\r | |
221 | void inst_hist (uint32 inst, uint32 pc, uint32 typ);\r | |
222 | t_stat rtc_inst (uint32 inst);\r | |
223 | t_stat rtc_svc (UNIT *uptr);\r | |
224 | t_stat rtc_reset (DEVICE *dptr);\r | |
225 | t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
226 | t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
227 | \r | |
228 | extern t_bool io_init (void);\r | |
229 | extern t_stat op_wyim (uint32 inst, uint32 *dat);\r | |
230 | extern t_stat op_miwy (uint32 inst, uint32 dat);\r | |
231 | extern t_stat op_pin (uint32 *dat);\r | |
232 | extern t_stat op_pot (uint32 dat);\r | |
233 | extern t_stat op_eomd (uint32 inst);\r | |
234 | extern t_stat op_sks (uint32 inst, uint32 *skp);\r | |
235 | \r | |
236 | /* CPU data structures\r | |
237 | \r | |
238 | cpu_dev CPU device descriptor\r | |
239 | cpu_unit CPU unit descriptor\r | |
240 | cpu_reg CPU register list\r | |
241 | cpu_mod CPU modifiers list\r | |
242 | */\r | |
243 | \r | |
244 | UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };\r | |
245 | \r | |
246 | REG cpu_reg[] = {\r | |
247 | { ORDATA (P, P, 14) },\r | |
248 | { ORDATA (A, A, 24) },\r | |
249 | { ORDATA (B, B, 24) },\r | |
250 | { ORDATA (X, X, 24) },\r | |
251 | { FLDATA (OV, OV, 0) },\r | |
252 | { ORDATA (EM2, EM2, 3) },\r | |
253 | { ORDATA (EM3, EM3, 3) },\r | |
254 | { ORDATA (RL1, RL1, 24) },\r | |
255 | { ORDATA (RL2, RL2, 24) },\r | |
256 | { ORDATA (RL4, RL4, 12) },\r | |
257 | { FLDATA (NML, nml_mode, 0) },\r | |
258 | { FLDATA (USR, usr_mode, 0) },\r | |
259 | { FLDATA (MONUSR, mon_usr_trap, 0) },\r | |
260 | { FLDATA (ION, ion, 0) },\r | |
261 | { FLDATA (INTDEF, ion_defer, 0) },\r | |
262 | { ORDATA (INTREQ, int_req, 32) },\r | |
263 | { ORDATA (APILVL, api_lvl, 32) },\r | |
264 | { DRDATA (INTRHI, int_reqhi, 5) },\r | |
265 | { DRDATA (APILHI, api_lvlhi, 5), REG_RO },\r | |
266 | { ORDATA (XFRREQ, xfr_req, 32) },\r | |
267 | { FLDATA (BPT1, bpt, 3) },\r | |
268 | { FLDATA (BPT2, bpt, 2) },\r | |
269 | { FLDATA (BPT3, bpt, 1) },\r | |
270 | { FLDATA (BPT4, bpt, 0) },\r | |
271 | { ORDATA (ALERT, alert, 6) },\r | |
272 | { FLDATA (STOP_INVINS, stop_invins, 0) },\r | |
273 | { FLDATA (STOP_INVDEV, stop_invdev, 0) },\r | |
274 | { FLDATA (STOP_INVIOP, stop_inviop, 0) },\r | |
275 | { DRDATA (INDLIM, ind_lim, 8), REG_NZ+PV_LEFT },\r | |
276 | { DRDATA (EXULIM, exu_lim, 8), REG_NZ+PV_LEFT },\r | |
277 | { BRDATA (PCQ, pcq, 8, 14, PCQ_SIZE), REG_RO+REG_CIRC },\r | |
278 | { ORDATA (PCQP, pcq_p, 6), REG_HRO },\r | |
279 | { ORDATA (WRU, sim_int_char, 8) },\r | |
280 | { NULL }\r | |
281 | };\r | |
282 | \r | |
283 | MTAB cpu_mod[] = {\r | |
284 | { UNIT_GENIE, 0, "standard peripherals", "SDS", &cpu_set_type },\r | |
285 | { UNIT_GENIE, UNIT_GENIE, "Genie peripherals", "GENIE", &cpu_set_type },\r | |
286 | { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size },\r | |
287 | { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },\r | |
288 | { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size },\r | |
289 | { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size },\r | |
290 | { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",\r | |
291 | &cpu_set_hist, &cpu_show_hist },\r | |
292 | { 0 }\r | |
293 | };\r | |
294 | \r | |
295 | DEVICE cpu_dev = {\r | |
296 | "CPU", &cpu_unit, cpu_reg, cpu_mod,\r | |
297 | 1, 8, 16, 1, 8, 24,\r | |
298 | &cpu_ex, &cpu_dep, &cpu_reset,\r | |
299 | NULL, NULL, NULL,\r | |
300 | NULL, 0\r | |
301 | };\r | |
302 | \r | |
303 | /* Clock data structures\r | |
304 | \r | |
305 | rtc_dev RTC device descriptor\r | |
306 | rtc_unit RTC unit\r | |
307 | rtc_reg RTC register list\r | |
308 | */\r | |
309 | \r | |
310 | UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), 16000 };\r | |
311 | \r | |
312 | REG rtc_reg[] = {\r | |
313 | { FLDATA (PIE, rtc_pie, 0) },\r | |
314 | { DRDATA (TIME, rtc_unit.wait, 24), REG_NZ + PV_LEFT },\r | |
315 | { DRDATA (TPS, rtc_tps, 8), PV_LEFT + REG_HRO },\r | |
316 | { NULL }\r | |
317 | };\r | |
318 | \r | |
319 | MTAB rtc_mod[] = {\r | |
320 | { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ",\r | |
321 | &rtc_set_freq, NULL, NULL },\r | |
322 | { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ",\r | |
323 | &rtc_set_freq, NULL, NULL },\r | |
324 | { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL,\r | |
325 | NULL, &rtc_show_freq, NULL },\r | |
326 | { 0 }\r | |
327 | };\r | |
328 | \r | |
329 | DEVICE rtc_dev = {\r | |
330 | "RTC", &rtc_unit, rtc_reg, rtc_mod,\r | |
331 | 1, 8, 8, 1, 8, 8,\r | |
332 | NULL, NULL, &rtc_reset,\r | |
333 | NULL, NULL, NULL\r | |
334 | };\r | |
335 | \r | |
336 | /* Interrupt tables */\r | |
337 | \r | |
338 | static const uint32 api_mask[32] = {\r | |
339 | 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, 0xFFFFFFF0,\r | |
340 | 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, 0xFFFFFF00,\r | |
341 | 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800, 0xFFFFF000,\r | |
342 | 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, 0xFFFF0000,\r | |
343 | 0xFFFE0000, 0xFFFC0000, 0xFFF80000, 0xFFF00000,\r | |
344 | 0xFFE00000, 0xFFC00000, 0xFF800000, 0xFF000000,\r | |
345 | 0xFE000000, 0xFC000000, 0xF8000000, 0xF0000000,\r | |
346 | 0xE0000000, 0xC0000000, 0x80000000, 0x00000000\r | |
347 | };\r | |
348 | \r | |
349 | static const uint32 int_vec[32] = {\r | |
350 | 0, 0, 0, 0,\r | |
351 | VEC_FORK, VEC_DRM, VEC_MUXCF,VEC_MUXCO,\r | |
352 | VEC_MUXT, VEC_MUXR, VEC_HEOR, VEC_HZWC,\r | |
353 | VEC_GEOR, VEC_GZWC, VEC_FEOR, VEC_FZWC,\r | |
354 | VEC_EEOR, VEC_EZWC, VEC_DEOR, VEC_DZWC,\r | |
355 | VEC_CEOR, VEC_CZWC, VEC_WEOR, VEC_YEOR,\r | |
356 | VEC_WZWC, VEC_YZWC, VEC_RTCP, VEC_RTCS,\r | |
357 | VEC_IPAR, VEC_CPAR, VEC_PWRF, VEC_PWRO\r | |
358 | };\r | |
359 | \r | |
360 | t_stat sim_instr (void)\r | |
361 | {\r | |
362 | extern int32 sim_interval;\r | |
363 | uint32 inst, tinst, pa, save_P, save_mode;\r | |
364 | t_stat reason, tr;\r | |
365 | \r | |
366 | /* Restore register state */\r | |
367 | \r | |
368 | if (io_init ()) return SCPE_STOP; /* init IO; conflict? */\r | |
369 | reason = 0;\r | |
370 | xfr_req = xfr_req & ~1; /* <0> reserved */\r | |
371 | int_req = int_req & ~1; /* <0> reserved */\r | |
372 | api_lvl = api_lvl & ~1; /* <0> reserved */\r | |
373 | set_dyn_map (); /* set up mapping */\r | |
374 | int_reqhi = api_findreq (); /* recalc int req */\r | |
375 | chan_req = chan_testact (); /* recalc chan act */\r | |
376 | \r | |
377 | /* Main instruction fetch/decode loop */\r | |
378 | \r | |
379 | while (reason == 0) { /* loop until halted */\r | |
380 | \r | |
381 | if (cpu_astop) { /* debug stop? */\r | |
382 | cpu_astop = 0;\r | |
383 | return SCPE_STOP;\r | |
384 | }\r | |
385 | \r | |
386 | if (sim_interval <= 0) { /* event queue? */\r | |
387 | if (reason = sim_process_event ()) break; /* process */\r | |
388 | int_reqhi = api_findreq (); /* recalc int req */\r | |
389 | chan_req = chan_testact (); /* recalc chan act */\r | |
390 | }\r | |
391 | \r | |
392 | if (chan_req) { /* channel request? */\r | |
393 | if (reason = chan_process ()) break; /* process */\r | |
394 | int_reqhi = api_findreq (); /* recalc int req */\r | |
395 | chan_req = chan_testact (); /* recalc chan act */\r | |
396 | }\r | |
397 | \r | |
398 | sim_interval = sim_interval - 1; /* count down */\r | |
399 | if (ion && !ion_defer && int_reqhi) { /* int request? */\r | |
400 | pa = int_vec[int_reqhi]; /* get vector */\r | |
401 | if (pa == 0) { /* bad value? */\r | |
402 | reason = STOP_ILLVEC;\r | |
403 | break;\r | |
404 | }\r | |
405 | tinst = ReadP (pa); /* get inst */\r | |
406 | save_mode = usr_mode; /* save mode */\r | |
407 | usr_mode = 0; /* switch to mon */\r | |
408 | if (hst_lnt) inst_hist (tinst, P, HIST_INT); /* record inst */\r | |
409 | if (pa != VEC_RTCP) { /* normal intr? */\r | |
410 | tr = one_inst (tinst, P, save_mode); /* exec intr inst */\r | |
411 | if (tr) { /* stop code? */\r | |
412 | usr_mode = save_mode; /* restore mode */\r | |
413 | reason = (tr > 0)? tr: STOP_MMINT;\r | |
414 | break;\r | |
415 | }\r | |
416 | api_lvl = api_lvl | (1u << int_reqhi); /* set level active */\r | |
417 | api_lvlhi = int_reqhi; /* elevate api */\r | |
418 | }\r | |
419 | else { /* clock intr */\r | |
420 | tr = rtc_inst (tinst); /* exec RTC inst */\r | |
421 | usr_mode = save_mode; /* restore mode */\r | |
422 | if (tr) { /* stop code? */\r | |
423 | reason = (tr > 0)? tr: STOP_MMINT;\r | |
424 | break;\r | |
425 | }\r | |
426 | int_req = int_req & ~INT_RTCP; /* clr clkp intr */\r | |
427 | }\r | |
428 | int_reqhi = api_findreq (); /* recalc int req */\r | |
429 | }\r | |
430 | else { /* normal instr */\r | |
431 | if (sim_brk_summ && sim_brk_test (P, SWMASK ('E'))) { /* breakpoint? */\r | |
432 | reason = STOP_IBKPT; /* stop simulation */\r | |
433 | break;\r | |
434 | }\r | |
435 | reason = Read (save_P = P, &inst); /* get instr */\r | |
436 | P = (P + 1) & VA_MASK; /* incr PC */\r | |
437 | if (reason == SCPE_OK) { /* fetch ok? */\r | |
438 | ion_defer = 0; /* clear ion */\r | |
439 | if (hst_lnt) inst_hist (inst, save_P, HIST_XCT);\r | |
440 | reason = one_inst (inst, save_P, usr_mode); /* exec inst */\r | |
441 | if (reason > 0) { /* stop code? */\r | |
442 | if (reason != STOP_HALT) P = save_P;\r | |
443 | if (reason == STOP_IONRDY) reason = 0;\r | |
444 | }\r | |
445 | } /* end if r == 0 */\r | |
446 | if (reason < 0) { /* mm (fet or ex)? */\r | |
447 | pa = -reason; /* get vector */\r | |
448 | reason = 0; /* defang */\r | |
449 | tinst = ReadP (pa); /* get inst */\r | |
450 | if (I_GETOP (tinst) != BRM) { /* not BRM? */\r | |
451 | reason = STOP_TRPINS; /* fatal err */\r | |
452 | break;\r | |
453 | }\r | |
454 | save_mode = usr_mode; /* save mode */\r | |
455 | usr_mode = 0; /* switch to mon */\r | |
456 | mon_usr_trap = 0;\r | |
457 | if (hst_lnt) inst_hist (tinst, save_P, HIST_TRP);\r | |
458 | tr = one_inst (tinst, save_P, save_mode); /* trap inst */\r | |
459 | if (tr) { /* stop code? */\r | |
460 | usr_mode = save_mode; /* restore mode */\r | |
461 | P = save_P; /* restore PC */\r | |
462 | reason = (tr > 0)? tr: STOP_MMTRP;\r | |
463 | break;\r | |
464 | }\r | |
465 | } /* end if reason */\r | |
466 | } /* end else int */\r | |
467 | } /* end while */\r | |
468 | \r | |
469 | /* Simulation halted */\r | |
470 | \r | |
471 | pcq_r->qptr = pcq_p; /* update pc q ptr */\r | |
472 | return reason;\r | |
473 | }\r | |
474 | \r | |
475 | /* Simulate one instruction */\r | |
476 | \r | |
477 | t_stat one_inst (uint32 inst, uint32 pc, uint32 mode)\r | |
478 | {\r | |
479 | uint32 op, shf_op, va, dat;\r | |
480 | uint32 old_A, old_B, old_X;\r | |
481 | int32 i, exu_cnt, sc;\r | |
482 | t_stat r;\r | |
483 | \r | |
484 | exu_cnt = 0; /* init EXU count */\r | |
485 | EXU_LOOP:\r | |
486 | op = I_GETOP (inst); /* get opcode */\r | |
487 | if (inst & I_POP) { /* POP? */\r | |
488 | dat = (EM3 << 18) | (EM2 << 15) | I_IND | pc; /* data to save */\r | |
489 | if (nml_mode) { /* normal mode? */\r | |
490 | dat = (OV << 23) | dat; /* ov in <0> */\r | |
491 | WriteP (0, dat);\r | |
492 | }\r | |
493 | else if (usr_mode) { /* user mode? */\r | |
494 | if (inst & I_USR) { /* SYSPOP? */\r | |
495 | dat = I_USR | (OV << 21) | dat; /* ov in <2> */\r | |
496 | WriteP (0, dat);\r | |
497 | usr_mode = 0; /* set mon mode */\r | |
498 | }\r | |
499 | else { /* normal POP */\r | |
500 | dat = (OV << 23) | dat; /* ov in <0> */\r | |
501 | if (r = Write (0, dat)) return r;\r | |
502 | }\r | |
503 | } \r | |
504 | else { /* mon mode */\r | |
505 | dat = (OV << 21) | dat; /* ov in <2> */\r | |
506 | WriteP (0, dat); /* store return */\r | |
507 | }\r | |
508 | PCQ_ENTRY; /* save PC */\r | |
509 | P = 0100 | op; /* new PC */\r | |
510 | OV = 0; /* clear ovflo */\r | |
511 | return SCPE_OK; /* end POP */\r | |
512 | }\r | |
513 | \r | |
514 | switch (op) { /* case on opcode */\r | |
515 | \r | |
516 | /* Loads and stores */\r | |
517 | \r | |
518 | case LDA:\r | |
519 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
520 | if (r = Read (va, &A)) return r; /* get operand */\r | |
521 | break;\r | |
522 | \r | |
523 | case LDB:\r | |
524 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
525 | if (r = Read (va, &B)) return r; /* get operand */\r | |
526 | break;\r | |
527 | \r | |
528 | case LDX:\r | |
529 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
530 | if (r = Read (va, &X)) return r; /* get operand */\r | |
531 | break;\r | |
532 | \r | |
533 | case STA:\r | |
534 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
535 | if (r = Write (va, A)) return r; /* write operand */\r | |
536 | break;\r | |
537 | \r | |
538 | case STB:\r | |
539 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
540 | if (r = Write (va, B)) return r; /* write operand */\r | |
541 | break;\r | |
542 | \r | |
543 | case STX:\r | |
544 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
545 | if (r = Write (va, X)) return r; /* write operand */\r | |
546 | break;\r | |
547 | \r | |
548 | case EAX:\r | |
549 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
550 | if (nml_mode || usr_mode) /* normal or user? */\r | |
551 | X = (X & ~VA_MASK) | (va & VA_MASK); /* only 14b */\r | |
552 | else X = (X & ~XVA_MASK) | (va & XVA_MASK); /* mon, 15b */\r | |
553 | break;\r | |
554 | \r | |
555 | case XMA:\r | |
556 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
557 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
558 | if (r = Write (va, A)) return r; /* write A */\r | |
559 | A = dat; /* load A */\r | |
560 | break;\r | |
561 | \r | |
562 | /* Arithmetic and logical */\r | |
563 | \r | |
564 | case ADD:\r | |
565 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
566 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
567 | A = Add24 (A, dat, 0); /* add */\r | |
568 | break;\r | |
569 | \r | |
570 | case ADC:\r | |
571 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
572 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
573 | OV = 0; /* clear overflow */\r | |
574 | A = Add24 (A, dat, X >> 23); /* add with carry */\r | |
575 | break;\r | |
576 | \r | |
577 | case SUB:\r | |
578 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
579 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
580 | A = Add24 (A, dat ^ DMASK, 1); /* subtract */\r | |
581 | break;\r | |
582 | \r | |
583 | case SUC:\r | |
584 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
585 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
586 | OV = 0; /* clear overflow */\r | |
587 | A = Add24 (A, dat ^ DMASK, X >> 23); /* sub with carry */\r | |
588 | break;\r | |
589 | \r | |
590 | case ADM:\r | |
591 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
592 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
593 | dat = AddM24 (dat, A); /* mem + A */\r | |
594 | if (r = Write (va, dat)) return r; /* rewrite */\r | |
595 | break;\r | |
596 | \r | |
597 | case MIN:\r | |
598 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
599 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
600 | dat = AddM24 (dat, 1); /* mem + 1 */\r | |
601 | if (r = Write (va, dat)) return r; /* rewrite */\r | |
602 | break;\r | |
603 | \r | |
604 | case MUL:\r | |
605 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
606 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
607 | Mul48 (A, dat); /* multiply */\r | |
608 | break;\r | |
609 | \r | |
610 | case DIV:\r | |
611 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
612 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
613 | Div48 (A, B, dat); /* divide */\r | |
614 | break;\r | |
615 | \r | |
616 | case ETR:\r | |
617 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
618 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
619 | A = A & dat; /* and */\r | |
620 | break;\r | |
621 | \r | |
622 | case MRG:\r | |
623 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
624 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
625 | A = A | dat; /* or */\r | |
626 | break;\r | |
627 | \r | |
628 | case EOR:\r | |
629 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
630 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
631 | A = A ^ dat; /* xor */\r | |
632 | break;\r | |
633 | \r | |
634 | /* Skips */\r | |
635 | \r | |
636 | case SKE:\r | |
637 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
638 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
639 | if (A == dat) P = (P + 1) & VA_MASK; /* if A = op, skip */\r | |
640 | break;\r | |
641 | \r | |
642 | case SKG:\r | |
643 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
644 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
645 | if (SXT (A) > SXT (dat)) P = (P + 1) & VA_MASK; /* if A > op, skip */\r | |
646 | break;\r | |
647 | \r | |
648 | case SKM:\r | |
649 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
650 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
651 | if (((A ^ dat) & B) == 0) P = (P + 1) & VA_MASK; /* if A = op masked */\r | |
652 | break;\r | |
653 | \r | |
654 | case SKA:\r | |
655 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
656 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
657 | if ((A & dat) == 0) P = (P + 1) & VA_MASK; /* if !(A & op), skip */\r | |
658 | break;\r | |
659 | \r | |
660 | case SKB:\r | |
661 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
662 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
663 | if ((B & dat) == 0) P = (P + 1) & VA_MASK; /* if !(B & op), skip */\r | |
664 | break;\r | |
665 | \r | |
666 | case SKN:\r | |
667 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
668 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
669 | if (dat & SIGN) P = (P + 1) & VA_MASK; /* if op < 0, skip */\r | |
670 | break;\r | |
671 | \r | |
672 | case SKR:\r | |
673 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
674 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
675 | dat = AddM24 (dat, DMASK); /* decr operand */\r | |
676 | if (r = Write (va, dat)) return r; /* rewrite operand */\r | |
677 | if (dat & SIGN) P = (P + 1) & VA_MASK; /* if op < 0, skip */\r | |
678 | break;\r | |
679 | \r | |
680 | case SKD:\r | |
681 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
682 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
683 | if (SXT_EXP (B) < SXT_EXP (dat)) { /* B < dat? */\r | |
684 | X = (dat - B) & DMASK; /* X = dat - B */\r | |
685 | P = (P + 1) & VA_MASK; /* skip */\r | |
686 | }\r | |
687 | else X = (B - dat) & DMASK; /* X = B - dat */\r | |
688 | break;\r | |
689 | \r | |
690 | /* Control */\r | |
691 | \r | |
692 | case NOP:\r | |
693 | break;\r | |
694 | \r | |
695 | case HLT:\r | |
696 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
697 | return STOP_HALT; /* halt CPU */\r | |
698 | \r | |
699 | case EXU:\r | |
700 | exu_cnt = exu_cnt + 1; /* count chained EXU */\r | |
701 | if (exu_cnt > exu_lim) return STOP_EXULIM; /* too many? */\r | |
702 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
703 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
704 | inst = dat;\r | |
705 | goto EXU_LOOP;\r | |
706 | \r | |
707 | case BRU:\r | |
708 | if (nml_mode && (inst & I_IND)) api_dismiss (); /* normal BRU*, dism */\r | |
709 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
710 | if (r = Read (va, &dat)) return r; /* test dest access */\r | |
711 | PCQ_ENTRY;\r | |
712 | P = va & VA_MASK; /* branch */\r | |
713 | break;\r | |
714 | \r | |
715 | case BRX:\r | |
716 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
717 | X = (X + 1) & DMASK; /* incr X */\r | |
718 | if (X & I_IND) { /* bit 9 set? */\r | |
719 | if (r = Read (va, &dat)) return r; /* test dest access */\r | |
720 | PCQ_ENTRY;\r | |
721 | P = va & VA_MASK; /* branch */\r | |
722 | }\r | |
723 | break;\r | |
724 | \r | |
725 | case BRM:\r | |
726 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
727 | dat = (EM3 << 18) | (EM2 << 15) | pc; /* form return word */\r | |
728 | if (!nml_mode && !usr_mode) /* monitor mode? */\r | |
729 | dat = dat | (mode << 23) | (OV << 21);\r | |
730 | else dat = dat | (OV << 23); /* normal or user */\r | |
731 | if (r = Write (va, dat)) return r; /* write ret word */\r | |
732 | PCQ_ENTRY;\r | |
733 | P = (va + 1) & VA_MASK; /* branch */\r | |
734 | break;\r | |
735 | \r | |
736 | case BRR:\r | |
737 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
738 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
739 | PCQ_ENTRY;\r | |
740 | P = (dat + 1) & VA_MASK; /* branch */\r | |
741 | if (!nml_mode && !usr_mode) { /* monitor mode? */\r | |
742 | OV = OV | ((dat >> 21) & 1); /* restore OV */\r | |
743 | if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */\r | |
744 | usr_mode = 1;\r | |
745 | if (mon_usr_trap) return MM_MONUSR;\r | |
746 | }\r | |
747 | }\r | |
748 | else OV = OV | ((dat >> 23) & 1); /* restore OV */\r | |
749 | break;\r | |
750 | \r | |
751 | case BRI:\r | |
752 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
753 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
754 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
755 | api_dismiss (); /* dismiss hi api */\r | |
756 | PCQ_ENTRY;\r | |
757 | P = dat & VA_MASK; /* branch */\r | |
758 | if (!nml_mode) { /* monitor mode? */\r | |
759 | OV = (dat >> 21) & 1; /* restore OV */\r | |
760 | if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */\r | |
761 | usr_mode = 1;\r | |
762 | if (mon_usr_trap) return MM_MONUSR;\r | |
763 | }\r | |
764 | }\r | |
765 | else OV = (dat >> 23) & 1; /* restore OV */\r | |
766 | break;\r | |
767 | \r | |
768 | /* Register change (microprogrammed) */\r | |
769 | \r | |
770 | case RCH:\r | |
771 | old_A = A; /* save orig reg */\r | |
772 | old_B = B;\r | |
773 | old_X = X;\r | |
774 | if (inst & 000001211) { /* A change? */\r | |
775 | if (inst & 01000) dat = (~old_A + 1) & DMASK; /* CNA */\r | |
776 | else dat = 0;\r | |
777 | if (inst & 00200) dat = dat | old_X;\r | |
778 | if (inst & 00010) dat = dat | old_B;\r | |
779 | if (inst & 00100) A = (A & ~EXPMASK) | (dat & EXPMASK);\r | |
780 | else A = dat;\r | |
781 | }\r | |
782 | if (inst & 000000046) { /* B change? */\r | |
783 | if (inst & 00040) dat = old_X;\r | |
784 | else dat = 0;\r | |
785 | if (inst & 00004) dat = dat | old_A;\r | |
786 | if (inst & 00100) B = (B & ~EXPMASK) | (dat & EXPMASK);\r | |
787 | else B = dat;\r | |
788 | }\r | |
789 | if (inst & 020000420) { /* X change? */\r | |
790 | if (inst & 00400) dat = old_A;\r | |
791 | else dat = 0;\r | |
792 | if (inst & 00020) dat = dat | old_B;\r | |
793 | if (inst & 00100) X = SXT_EXP (dat) & DMASK;\r | |
794 | else X = dat;\r | |
795 | }\r | |
796 | break;\r | |
797 | \r | |
798 | /* Overflow instruction */\r | |
799 | \r | |
800 | case OVF:\r | |
801 | if ((inst & 0100) & OV) P = (P + 1) & VA_MASK;\r | |
802 | if (inst & 0001) OV = 0;\r | |
803 | if ((inst & 0010) && (((X >> 1) ^ X) & EXPS)) OV = 1;\r | |
804 | break;\r | |
805 | \r | |
806 | /* Shifts */\r | |
807 | \r | |
808 | case RSH:\r | |
809 | if (r = EaSh (inst, &va)) return r; /* decode eff addr */\r | |
810 | shf_op = I_GETSHFOP (va); /* get eff op */\r | |
811 | sc = va & I_SHFMSK; /* get eff count */\r | |
812 | switch (shf_op) { /* case on sub-op */\r | |
813 | case 00: /* right arithmetic */\r | |
814 | if (sc) ShfR48 (sc, (A & SIGN)? DMASK: 0);\r | |
815 | break;\r | |
816 | case 04: /* right cycle */\r | |
817 | sc = sc % 48; /* mod 48 */\r | |
818 | if (sc) RotR48 (sc);\r | |
819 | break;\r | |
820 | case 05: /* right logical */\r | |
821 | if (sc) ShfR48 (sc, 0);\r | |
822 | break;\r | |
823 | default:\r | |
824 | CRETINS; /* invalid inst */\r | |
825 | break;\r | |
826 | } /* end case shf op */\r | |
827 | break;\r | |
828 | \r | |
829 | case LSH:\r | |
830 | if (r = EaSh (inst, &va)) return r; /* decode eff addr */\r | |
831 | shf_op = I_GETSHFOP (va); /* get eff op */\r | |
832 | sc = va & I_SHFMSK; /* get eff count */\r | |
833 | switch (shf_op) { /* case on sub-op */\r | |
834 | case 00: /* left arithmetic */\r | |
835 | dat = A; /* save sign */\r | |
836 | if (sc > 48) sc = 48;\r | |
837 | for (i = 0; i < sc; i++) { /* loop */\r | |
838 | A = ((A << 1) | (B >> 23)) & DMASK;\r | |
839 | B = (B << 1) & DMASK;\r | |
840 | if ((A ^ dat) & SIGN) OV = 1;\r | |
841 | }\r | |
842 | break;\r | |
843 | case 02: /* normalize */\r | |
844 | if (sc > 48) sc = 48;\r | |
845 | for (i = 0; i < sc; i++) { /* until max count */\r | |
846 | if ((A ^ (A << 1)) & SIGN) break;\r | |
847 | A = ((A << 1) | (B >> 23)) & DMASK;\r | |
848 | B = (B << 1) & DMASK;\r | |
849 | }\r | |
850 | X = (X - i) & DMASK;\r | |
851 | break;\r | |
852 | case 04: /* left cycle */\r | |
853 | sc = sc % 48; /* mod 48 */\r | |
854 | if (sc) RotR48 (48 - sc); /* rotate */\r | |
855 | break;\r | |
856 | case 06: /* cycle normalize */\r | |
857 | if (sc > 48) sc = 48;\r | |
858 | for (i = 0; i < sc; i++) { /* until max count */\r | |
859 | if ((A ^ (A << 1)) & SIGN) break;\r | |
860 | old_A = A; /* cyclic shift */\r | |
861 | A = ((A << 1) | (B >> 23)) & DMASK;\r | |
862 | B = ((B << 1) | (old_A >> 23)) & DMASK;\r | |
863 | }\r | |
864 | X = (X - i) & DMASK;\r | |
865 | break;\r | |
866 | default:\r | |
867 | CRETINS; /* invalid inst */\r | |
868 | break;\r | |
869 | } /* end case shf op */\r | |
870 | break;\r | |
871 | \r | |
872 | /* I/O instructions */\r | |
873 | \r | |
874 | case MIW: case MIY:\r | |
875 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
876 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
877 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
878 | if (r = op_miwy (inst, dat)) return r; /* process inst */\r | |
879 | int_reqhi = api_findreq (); /* recalc int req */\r | |
880 | chan_req = chan_testact (); /* recalc chan act */\r | |
881 | break;\r | |
882 | \r | |
883 | case WIM: case YIM:\r | |
884 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
885 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
886 | if (r = op_wyim (inst, &dat)) return r; /* process inst */\r | |
887 | if (r = Write (va, dat)) return r; /* write result */\r | |
888 | int_reqhi = api_findreq (); /* recalc int req */\r | |
889 | chan_req = chan_testact (); /* recalc chan act */\r | |
890 | break;\r | |
891 | \r | |
892 | case EOM: case EOD:\r | |
893 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
894 | if (r = op_eomd (inst)) return r; /* process inst */\r | |
895 | int_reqhi = api_findreq (); /* recalc int req */\r | |
896 | chan_req = chan_testact (); /* recalc chan act */\r | |
897 | ion_defer = 1;\r | |
898 | break;\r | |
899 | \r | |
900 | case POT:\r | |
901 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
902 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
903 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
904 | if (r = op_pot (dat)) return r; /* process inst */\r | |
905 | int_reqhi = api_findreq (); /* recalc int req */\r | |
906 | chan_req = chan_testact (); /* recalc chan act */\r | |
907 | break;\r | |
908 | \r | |
909 | case PIN:\r | |
910 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
911 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
912 | if (r = op_pin (&dat)) return r; /* process inst */\r | |
913 | if (r = Write (va, dat)) return r; /* write result */\r | |
914 | int_reqhi = api_findreq (); /* recalc int req */\r | |
915 | chan_req = chan_testact (); /* recalc chan act */\r | |
916 | break;\r | |
917 | \r | |
918 | case SKS:\r | |
919 | if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */\r | |
920 | if (r = op_sks (inst, &dat)) return r; /* process inst */\r | |
921 | if (dat) P = (P + 1) & VA_MASK;\r | |
922 | break;\r | |
923 | \r | |
924 | default:\r | |
925 | if (!nml_mode && usr_mode) return MM_PRVINS; /* usr? priv viol */\r | |
926 | CRETINS; /* invalid inst */\r | |
927 | break;\r | |
928 | }\r | |
929 | \r | |
930 | return SCPE_OK;\r | |
931 | }\r | |
932 | \r | |
933 | /* Effective address calculation */\r | |
934 | \r | |
935 | t_stat Ea (uint32 inst, uint32 *addr)\r | |
936 | {\r | |
937 | int32 i;\r | |
938 | uint32 wd = inst; /* homeable */\r | |
939 | uint32 va = wd & XVA_MASK; /* initial va */\r | |
940 | t_stat r;\r | |
941 | \r | |
942 | for (i = 0; i < ind_lim; i++) { /* count indirects */\r | |
943 | if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK);\r | |
944 | *addr = va;\r | |
945 | if ((wd & I_IND) == 0) { /* end of ind chain? */\r | |
946 | if (hst_lnt) hst[hst_p].ea = *addr; /* record */\r | |
947 | return SCPE_OK;\r | |
948 | }\r | |
949 | if (r = Read (va, &wd)) return r; /* read ind; fails? */\r | |
950 | va = (va & VA_USR) | (wd & XVA_MASK);\r | |
951 | }\r | |
952 | return STOP_INDLIM; /* too many indirects */\r | |
953 | }\r | |
954 | \r | |
955 | /* Effective address calculation for shifts - direct indexing is 9b */\r | |
956 | \r | |
957 | t_stat EaSh (uint32 inst, uint32 *addr)\r | |
958 | {\r | |
959 | int32 i;\r | |
960 | uint32 wd = inst; /* homeable */\r | |
961 | uint32 va = wd & XVA_MASK; /* initial va */\r | |
962 | t_stat r;\r | |
963 | \r | |
964 | for (i = 0; i < ind_lim; i++) { /* count indirects */\r | |
965 | if ((wd & I_IND) == 0) { /* end of ind chain? */\r | |
966 | if (wd & I_IDX) *addr = (va & (VA_MASK & ~I_SHFMSK)) |\r | |
967 | ((va + X) & I_SHFMSK); /* 9b indexing */\r | |
968 | else *addr = va & VA_MASK;\r | |
969 | if (hst_lnt) hst[hst_p].ea = *addr; /* record */\r | |
970 | return SCPE_OK;\r | |
971 | }\r | |
972 | if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK);\r | |
973 | if (r = Read (va, &wd)) return r; /* read ind; fails? */\r | |
974 | va = (va & VA_USR) | (wd & XVA_MASK);\r | |
975 | }\r | |
976 | return STOP_INDLIM; /* too many indirects */\r | |
977 | }\r | |
978 | \r | |
979 | /* Read word from virtual address */\r | |
980 | \r | |
981 | t_stat Read (uint32 va, uint32 *dat)\r | |
982 | {\r | |
983 | uint32 pgn, map, pa;\r | |
984 | \r | |
985 | if (nml_mode) { /* normal? */\r | |
986 | va = va & VA_MASK; /* ignore user */\r | |
987 | if (va < 020000) pa = va; /* first 8K: 1 for 1 */\r | |
988 | else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */\r | |
989 | else pa = va + em3_dyn; /* next 4K: ext EM3 */\r | |
990 | }\r | |
991 | else if (usr_mode || (va & VA_USR)) { /* user mapping? */\r | |
992 | pgn = VA_GETPN (va); /* get page no */\r | |
993 | map = usr_map[pgn]; /* get map entry */\r | |
994 | if (map == MAP_PROT) return MM_NOACC; /* prot? no access */\r | |
995 | pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */\r | |
996 | }\r | |
997 | else {\r | |
998 | pgn = VA_GETPN (va); /* mon, get page no */\r | |
999 | map = mon_map[pgn]; /* get map entry */\r | |
1000 | if (map & MAP_PROT) return MM_NOACC; /* prot? no access */\r | |
1001 | pa = map | (va & VA_POFF); /* map address */\r | |
1002 | }\r | |
1003 | *dat = M[pa]; /* return word */\r | |
1004 | return SCPE_OK;\r | |
1005 | }\r | |
1006 | \r | |
1007 | /* Write word to virtual address */\r | |
1008 | \r | |
1009 | t_stat Write (uint32 va, uint32 dat)\r | |
1010 | {\r | |
1011 | uint32 pgn, map, pa;\r | |
1012 | \r | |
1013 | if (nml_mode) { /* normal? */\r | |
1014 | va = va & VA_MASK; /* ignore user */\r | |
1015 | if (va < 020000) pa = va; /* first 8K: 1 for 1 */\r | |
1016 | else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */\r | |
1017 | else pa = va + em3_dyn; /* next 4K: ext EM3 */\r | |
1018 | }\r | |
1019 | else if (usr_mode || (va & VA_USR)) { /* user mapping? */\r | |
1020 | pgn = VA_GETPN (va); /* get page no */\r | |
1021 | map = usr_map[pgn]; /* get map entry */\r | |
1022 | if (map & MAP_PROT) { /* protected page? */\r | |
1023 | if (map == MAP_PROT) return MM_NOACC; /* zero? no access */\r | |
1024 | else return MM_WRITE; /* else, write prot */\r | |
1025 | }\r | |
1026 | pa = map | (va & VA_POFF); /* map address */\r | |
1027 | }\r | |
1028 | else {\r | |
1029 | pgn = VA_GETPN (va); /* mon, get page no */\r | |
1030 | map = mon_map[pgn]; /* get map entry */\r | |
1031 | if (map & MAP_PROT) return MM_NOACC; /* prot? no access */\r | |
1032 | pa = map | (va & VA_POFF); /* map address */\r | |
1033 | }\r | |
1034 | if (MEM_ADDR_OK (pa)) M[pa] = dat;\r | |
1035 | return SCPE_OK;\r | |
1036 | }\r | |
1037 | \r | |
1038 | /* Relocate addr for console access */\r | |
1039 | \r | |
1040 | uint32 RelocC (int32 va, int32 sw)\r | |
1041 | {\r | |
1042 | uint32 nml = nml_mode, usr = usr_mode;\r | |
1043 | uint32 pa, pgn, map;\r | |
1044 | \r | |
1045 | if (sw & SWMASK ('N')) nml = 1; /* -n: normal */\r | |
1046 | else if (sw & SWMASK ('X')) nml = usr = 0; /* -x: mon */\r | |
1047 | else if (sw & SWMASK ('U')) nml = 0, usr = 1; /* -u: user */\r | |
1048 | else if (!(sw & SWMASK ('V'))) return va; /* -v: curr */\r | |
1049 | set_dyn_map ();\r | |
1050 | if (nml) { /* normal? */\r | |
1051 | if (va < 020000) pa = va; /* first 8K: 1 for 1 */\r | |
1052 | else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */\r | |
1053 | else pa = va + em3_dyn; /* next 4K: ext EM3 */\r | |
1054 | }\r | |
1055 | else {\r | |
1056 | pgn = VA_GETPN (va); /* get page no */\r | |
1057 | map = usr? usr_map[pgn]: mon_map[pgn]; /* get map entry */\r | |
1058 | if (map == MAP_PROT) return MAXMEMSIZE + 1; /* no access page? */\r | |
1059 | pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */\r | |
1060 | }\r | |
1061 | return pa;\r | |
1062 | }\r | |
1063 | \r | |
1064 | /* Arithmetic routines */\r | |
1065 | \r | |
1066 | uint32 Add24 (uint32 s1, uint32 s2, uint32 cin)\r | |
1067 | {\r | |
1068 | uint32 t = s1 + s2 + cin; /* add with carry in */\r | |
1069 | if (t > DMASK) X = X | SIGN; /* carry to X<0> */\r | |
1070 | else X = X & ~SIGN;\r | |
1071 | if (((s1 ^ ~s2) & (s1 ^ t)) & SIGN) OV = 1; /* overflow */\r | |
1072 | return t & DMASK;\r | |
1073 | }\r | |
1074 | \r | |
1075 | uint32 AddM24 (uint32 s1, uint32 s2)\r | |
1076 | {\r | |
1077 | uint32 t = s1 + s2; /* add */\r | |
1078 | if (((s1 ^ ~s2) & (s1 ^ t)) & SIGN) OV = 1; /* overflow */\r | |
1079 | return t & DMASK;\r | |
1080 | }\r | |
1081 | \r | |
1082 | void Mul48 (uint32 s1, uint32 s2)\r | |
1083 | {\r | |
1084 | uint32 a = ABS (s1);\r | |
1085 | uint32 b = ABS (s2);\r | |
1086 | uint32 hi, md, lo, t, u;\r | |
1087 | \r | |
1088 | if ((a == 0) || (b == 0)) { /* ops zero? */\r | |
1089 | A = B = 0;\r | |
1090 | return;\r | |
1091 | }\r | |
1092 | t = a >> 12; /* split op1 */\r | |
1093 | a = a & 07777;\r | |
1094 | u = b >> 12; /* split op2 */\r | |
1095 | b = b & 07777;\r | |
1096 | md = (a * u) + (b * t); /* cross product */\r | |
1097 | lo = (a * b) + ((md & 07777) << 12); /* low result */\r | |
1098 | hi = (t * u) + (md >> 12) + (lo >> 24); /* hi result */\r | |
1099 | A = ((hi << 1) & DMASK) | ((lo & DMASK) >> 23);\r | |
1100 | B = (lo << 1) & DMASK;\r | |
1101 | if ((s1 ^ s2) & SIGN) {\r | |
1102 | B = ((B ^ DMASK) + 1) & DMASK;\r | |
1103 | A = ((A ^ DMASK) + (B == 0)) & DMASK;\r | |
1104 | }\r | |
1105 | else if (A & SIGN) OV = 1;\r | |
1106 | return;\r | |
1107 | }\r | |
1108 | \r | |
1109 | /* Divide - the SDS 940 uses a non-restoring divide. The algorithm\r | |
1110 | runs even for overflow cases. Hence it must be emulated precisely\r | |
1111 | to give the right answers for diagnostics. If the dividend is \r | |
1112 | negative, AB are 2's complemented starting at B<22>, and B<23>\r | |
1113 | is unchanged. */\r | |
1114 | \r | |
1115 | void Div48 (uint32 ar, uint32 br, uint32 m)\r | |
1116 | {\r | |
1117 | int32 i;\r | |
1118 | uint32 quo = 0; /* quotient */\r | |
1119 | uint32 dvdh = ar, dvdl = br; /* dividend */\r | |
1120 | uint32 dvr = ABS (m); /* make dvr pos */\r | |
1121 | \r | |
1122 | if (TSTS (dvdh)) { /* dvd < 0? */\r | |
1123 | dvdl = (((dvdl ^ DMASK) + 2) & (DMASK & ~1)) | /* 23b negate */\r | |
1124 | (dvdl & 1); /* low bit unch */\r | |
1125 | dvdh = ((dvdh ^ DMASK) + (dvdl <= 1)) & DMASK;\r | |
1126 | }\r | |
1127 | if ((dvdh > dvr) || /* divide fail? */\r | |
1128 | ((dvdh == dvr) && dvdl) ||\r | |
1129 | ((dvdh == dvr) && !TSTS (ar ^ m))) OV = 1;\r | |
1130 | dvdh = (dvdh - dvr) & DMASK; /* initial sub */\r | |
1131 | for (i = 0; i < 23; i++) { /* 23 iterations */\r | |
1132 | quo = (quo << 1) | ((dvdh >> 23) ^ 1); /* quo bit = ~sign */\r | |
1133 | dvdh = ((dvdh << 1) | (dvdl >> 23)) & DMASK; /* shift divd */\r | |
1134 | dvdl = (dvdl << 1) & DMASK;\r | |
1135 | if (quo & 1) /* test ~sign */\r | |
1136 | dvdh = (dvdh - dvr) & DMASK; /* sign was +, sub */\r | |
1137 | else dvdh = (dvdh + dvr) & DMASK; /* sign was -, add */\r | |
1138 | }\r | |
1139 | quo = quo << 1; /* shift quo */\r | |
1140 | if (dvdh & SIGN) dvdh = (dvdh + dvr) & DMASK; /* last op -? restore */\r | |
1141 | else quo = quo | 1; /* +, set quo bit */\r | |
1142 | if (TSTS (ar ^ m)) A = NEG (quo); /* sign of quo */\r | |
1143 | else A = quo; /* A = quo */\r | |
1144 | if (TSTS (ar)) B = NEG (dvdh); /* sign of rem */\r | |
1145 | else B = dvdh; /* B = rem */\r | |
1146 | return;\r | |
1147 | }\r | |
1148 | \r | |
1149 | void RotR48 (uint32 sc)\r | |
1150 | {\r | |
1151 | uint32 t = A;\r | |
1152 | \r | |
1153 | if (sc >= 24) {\r | |
1154 | sc = sc - 24;\r | |
1155 | A = ((B >> sc) | (A << (24 - sc))) & DMASK;\r | |
1156 | B = ((t >> sc) | (B << (24 - sc))) & DMASK;\r | |
1157 | }\r | |
1158 | else {\r | |
1159 | A = ((A >> sc) | (B << (24 - sc))) & DMASK;\r | |
1160 | B = ((B >> sc) | (t << (24 - sc))) & DMASK;\r | |
1161 | }\r | |
1162 | return;\r | |
1163 | }\r | |
1164 | \r | |
1165 | void ShfR48 (uint32 sc, uint32 sgn)\r | |
1166 | {\r | |
1167 | if (sc >= 48) A = B = sgn;\r | |
1168 | if (sc >= 24) {\r | |
1169 | sc = sc - 24;\r | |
1170 | B = ((A >> sc) | (sgn << (24 - sc))) & DMASK;\r | |
1171 | A = sgn;\r | |
1172 | }\r | |
1173 | else {\r | |
1174 | B = ((B >> sc) | (A << (24 - sc)) & DMASK);\r | |
1175 | A = ((A >> sc) | (sgn << (24 - sc))) & DMASK;\r | |
1176 | }\r | |
1177 | return;\r | |
1178 | }\r | |
1179 | \r | |
1180 | /* POT routines for RL1, RL2, RL4 */\r | |
1181 | \r | |
1182 | t_stat pot_RL1 (uint32 num, uint32 *dat)\r | |
1183 | {\r | |
1184 | RL1 = *dat;\r | |
1185 | set_dyn_map ();\r | |
1186 | return SCPE_OK;\r | |
1187 | }\r | |
1188 | \r | |
1189 | t_stat pot_RL2 (uint32 num, uint32 *dat)\r | |
1190 | {\r | |
1191 | RL2 = *dat;\r | |
1192 | set_dyn_map ();\r | |
1193 | return SCPE_OK;\r | |
1194 | }\r | |
1195 | \r | |
1196 | t_stat pot_RL4 (uint32 num, uint32 *dat)\r | |
1197 | {\r | |
1198 | RL4 = (*dat) & 03737;\r | |
1199 | set_dyn_map ();\r | |
1200 | return SCPE_OK;\r | |
1201 | }\r | |
1202 | \r | |
1203 | /* Map EM2, EM3, RL1, RL2, RL4 to dynamic forms\r | |
1204 | \r | |
1205 | EM2, EM3 - left shifted 12, base virtual address subtracted\r | |
1206 | RL1, RL2 - page left shifted 11\r | |
1207 | RL3 - filled in as 1 to 1 map\r | |
1208 | RL4 - EM2 or page left shifted 11, PROT bit inserted\r | |
1209 | */\r | |
1210 | \r | |
1211 | void set_dyn_map (void)\r | |
1212 | {\r | |
1213 | em2_dyn = ((EM2 & 07) << 12) - 020000;\r | |
1214 | em3_dyn = ((EM3 & 07) << 12) - 030000;\r | |
1215 | usr_map[0] = (RL1 >> 7) & (MAP_PROT | MAP_PAGE);\r | |
1216 | usr_map[1] = (RL1 >> 1) & (MAP_PROT | MAP_PAGE);\r | |
1217 | usr_map[2] = (RL1 << 5) & (MAP_PROT | MAP_PAGE);\r | |
1218 | usr_map[3] = (RL1 << 11) & (MAP_PROT | MAP_PAGE);\r | |
1219 | usr_map[4] = (RL2 >> 7) & (MAP_PROT | MAP_PAGE);\r | |
1220 | usr_map[5] = (RL2 >> 1) & (MAP_PROT | MAP_PAGE);\r | |
1221 | usr_map[6] = (RL2 << 5) & (MAP_PROT | MAP_PAGE);\r | |
1222 | usr_map[7] = (RL2 << 11) & (MAP_PROT | MAP_PAGE);\r | |
1223 | mon_map[0] = (0 << VA_V_PN);\r | |
1224 | mon_map[1] = (1 << VA_V_PN);\r | |
1225 | mon_map[2] = (2 << VA_V_PN);\r | |
1226 | mon_map[3] = (3 << VA_V_PN);\r | |
1227 | mon_map[4] = ((EM2 & 07) << 12);\r | |
1228 | mon_map[5] = ((EM2 & 07) << 12) + (1 << VA_V_PN);\r | |
1229 | mon_map[6] = (RL4 << 5) & MAP_PAGE;\r | |
1230 | mon_map[7] = (RL4 << 11) & MAP_PAGE;\r | |
1231 | if (mon_map[6] == 0) mon_map[6] = MAP_PROT;\r | |
1232 | if (mon_map[7] == 0) mon_map[7] = MAP_PROT;\r | |
1233 | return;\r | |
1234 | }\r | |
1235 | \r | |
1236 | /* Recalculate api requests */\r | |
1237 | \r | |
1238 | uint32 api_findreq (void)\r | |
1239 | {\r | |
1240 | uint32 i, t;\r | |
1241 | \r | |
1242 | t = (int_req & ~1) & api_mask[api_lvlhi]; /* unmasked int */\r | |
1243 | for (i = 31; t && (i > 0); i--) { /* find highest */\r | |
1244 | if ((t >> i) & 1) return i;\r | |
1245 | }\r | |
1246 | return 0; /* none */\r | |
1247 | }\r | |
1248 | \r | |
1249 | /* Dismiss highest priority interrupt */\r | |
1250 | \r | |
1251 | void api_dismiss (void)\r | |
1252 | {\r | |
1253 | uint32 i, t;\r | |
1254 | \r | |
1255 | t = 1u << api_lvlhi; /* highest active */\r | |
1256 | int_req = int_req & ~t; /* clear int req */\r | |
1257 | api_lvl = api_lvl & ~t; /* clear api level */\r | |
1258 | api_lvlhi = 0; /* assume all clear */\r | |
1259 | for (i = 31; api_lvl && (i > 0); i--) { /* find highest api */\r | |
1260 | if ((api_lvl >> i) & 1) { /* bit set? */\r | |
1261 | api_lvlhi = i; /* record level */\r | |
1262 | break; /* done */\r | |
1263 | }\r | |
1264 | }\r | |
1265 | int_reqhi = api_findreq (); /* recalc intreq */\r | |
1266 | return;\r | |
1267 | }\r | |
1268 | \r | |
1269 | /* Reset routine */\r | |
1270 | \r | |
1271 | t_stat cpu_reset (DEVICE *dptr)\r | |
1272 | {\r | |
1273 | OV = 0;\r | |
1274 | EM2 = 2;\r | |
1275 | EM3 = 3;\r | |
1276 | RL1 = RL2 = RL4 = 0;\r | |
1277 | ion = ion_defer = 0;\r | |
1278 | nml_mode = 1;\r | |
1279 | usr_mode = 0;\r | |
1280 | mon_usr_trap = 0;\r | |
1281 | int_req = 0;\r | |
1282 | int_reqhi = 0;\r | |
1283 | api_lvl = 0;\r | |
1284 | api_lvlhi = 0;\r | |
1285 | alert = 0;\r | |
1286 | pcq_r = find_reg ("PCQ", NULL, dptr);\r | |
1287 | if (pcq_r) pcq_r->qptr = 0;\r | |
1288 | else return SCPE_IERR;\r | |
1289 | sim_brk_types = sim_brk_dflt = SWMASK ('E');\r | |
1290 | return SCPE_OK;\r | |
1291 | }\r | |
1292 | \r | |
1293 | /* Memory examine */\r | |
1294 | \r | |
1295 | t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r | |
1296 | {\r | |
1297 | uint32 pa;\r | |
1298 | \r | |
1299 | pa = RelocC (addr, sw);\r | |
1300 | if (pa > MAXMEMSIZE) return SCPE_REL;\r | |
1301 | if (pa >= MEMSIZE) return SCPE_NXM;\r | |
1302 | if (vptr != NULL) *vptr = M[pa] & DMASK;\r | |
1303 | return SCPE_OK;\r | |
1304 | }\r | |
1305 | \r | |
1306 | /* Memory deposit */\r | |
1307 | \r | |
1308 | t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r | |
1309 | {\r | |
1310 | uint32 pa;\r | |
1311 | \r | |
1312 | pa = RelocC (addr, sw);\r | |
1313 | if (pa > MAXMEMSIZE) return SCPE_REL;\r | |
1314 | if (pa >= MEMSIZE) return SCPE_NXM;\r | |
1315 | M[pa] = val & DMASK;\r | |
1316 | return SCPE_OK;\r | |
1317 | }\r | |
1318 | \r | |
1319 | /* Set memory size */\r | |
1320 | \r | |
1321 | t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1322 | {\r | |
1323 | int32 mc = 0;\r | |
1324 | uint32 i;\r | |
1325 | \r | |
1326 | if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 037777) != 0))\r | |
1327 | return SCPE_ARG;\r | |
1328 | for (i = val; i < MEMSIZE; i++) mc = mc | M[i];\r | |
1329 | if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))\r | |
1330 | return SCPE_OK;\r | |
1331 | MEMSIZE = val;\r | |
1332 | for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;\r | |
1333 | return SCPE_OK;\r | |
1334 | }\r | |
1335 | \r | |
1336 | /* Set system type (1 = Genie, 0 = standard) */\r | |
1337 | \r | |
1338 | t_stat cpu_set_type (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1339 | {\r | |
1340 | extern t_stat drm_reset (DEVICE *dptr);\r | |
1341 | extern DEVICE drm_dev, mux_dev, muxl_dev;\r | |
1342 | extern UNIT drm_unit, mux_unit;\r | |
1343 | extern DIB mux_dib;\r | |
1344 | \r | |
1345 | if ((cpu_unit.flags & UNIT_GENIE) == (uint32) val) return SCPE_OK;\r | |
1346 | if ((drm_unit.flags & UNIT_ATT) || /* attached? */\r | |
1347 | (mux_unit.flags & UNIT_ATT)) return SCPE_NOFNC; /* can't do it */\r | |
1348 | if (val) { /* Genie? */\r | |
1349 | drm_dev.flags = drm_dev.flags & ~DEV_DIS; /* enb drum */\r | |
1350 | mux_dev.flags = mux_dev.flags & ~DEV_DIS; /* enb mux */\r | |
1351 | muxl_dev.flags = muxl_dev.flags & ~DEV_DIS;\r | |
1352 | mux_dib.dev = DEV3_GMUX; /* Genie mux */\r | |
1353 | }\r | |
1354 | else {\r | |
1355 | drm_dev.flags = drm_dev.flags | DEV_DIS; /* dsb drum */\r | |
1356 | mux_dib.dev = DEV3_SMUX; /* std mux */\r | |
1357 | return drm_reset (&drm_dev);\r | |
1358 | }\r | |
1359 | return SCPE_OK;\r | |
1360 | }\r | |
1361 | \r | |
1362 | /* The real time clock runs continuously; therefore, it only has\r | |
1363 | a unit service routine and a reset routine. The service routine\r | |
1364 | sets an interrupt that invokes the clock counter. The clock counter\r | |
1365 | is a "one instruction interrupt", and only MIN/SKR are valid.\r | |
1366 | */\r | |
1367 | \r | |
1368 | t_stat rtc_svc (UNIT *uptr)\r | |
1369 | {\r | |
1370 | if (rtc_pie) int_req = int_req | INT_RTCP; /* set pulse intr */\r | |
1371 | sim_activate (&rtc_unit, sim_rtcn_calb (rtc_tps, TMR_RTC)); /* reactivate */\r | |
1372 | return SCPE_OK;\r | |
1373 | }\r | |
1374 | \r | |
1375 | /* Clock interrupt instruction */\r | |
1376 | \r | |
1377 | t_stat rtc_inst (uint32 inst)\r | |
1378 | {\r | |
1379 | uint32 op, dat, val, va;\r | |
1380 | t_stat r;\r | |
1381 | \r | |
1382 | op = I_GETOP (inst); /* get opcode */\r | |
1383 | if (op == MIN) val = 1; /* incr */\r | |
1384 | else if (op == SKR) val = DMASK; /* decr */\r | |
1385 | else return STOP_RTCINS; /* can't do it */\r | |
1386 | if (r = Ea (inst, &va)) return r; /* decode eff addr */\r | |
1387 | if (r = Read (va, &dat)) return r; /* get operand */\r | |
1388 | dat = AddM24 (dat, val); /* mem +/- 1 */\r | |
1389 | if (r = Write (va, dat)) return r; /* rewrite */\r | |
1390 | if (dat == 0) int_req = int_req | INT_RTCS; /* set clk sync int */\r | |
1391 | return SCPE_OK;\r | |
1392 | }\r | |
1393 | \r | |
1394 | /* Clock reset */\r | |
1395 | \r | |
1396 | t_stat rtc_reset (DEVICE *dptr)\r | |
1397 | {\r | |
1398 | rtc_pie = 0; /* disable pulse */\r | |
1399 | sim_activate (&rtc_unit, rtc_unit.wait); /* activate unit */\r | |
1400 | return SCPE_OK;\r | |
1401 | }\r | |
1402 | \r | |
1403 | /* Set frequency */\r | |
1404 | \r | |
1405 | t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1406 | {\r | |
1407 | if (cptr) return SCPE_ARG;\r | |
1408 | if ((val != 50) && (val != 60)) return SCPE_IERR;\r | |
1409 | rtc_tps = val;\r | |
1410 | return SCPE_OK;\r | |
1411 | }\r | |
1412 | \r | |
1413 | /* Show frequency */\r | |
1414 | \r | |
1415 | t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
1416 | {\r | |
1417 | fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz");\r | |
1418 | return SCPE_OK;\r | |
1419 | }\r | |
1420 | \r | |
1421 | /* Record history */\r | |
1422 | \r | |
1423 | void inst_hist (uint32 ir, uint32 pc, uint32 tp)\r | |
1424 | {\r | |
1425 | hst_p = (hst_p + 1); /* next entry */\r | |
1426 | if (hst_p >= hst_lnt) hst_p = 0;\r | |
1427 | hst[hst_p].typ = tp | (OV << 4);\r | |
1428 | hst[hst_p].pc = pc;\r | |
1429 | hst[hst_p].ir = ir;\r | |
1430 | hst[hst_p].a = A;\r | |
1431 | hst[hst_p].b = B;\r | |
1432 | hst[hst_p].x = X;\r | |
1433 | hst[hst_p].ea = HIST_NOEA;\r | |
1434 | return;\r | |
1435 | }\r | |
1436 | \r | |
1437 | /* Set history */\r | |
1438 | \r | |
1439 | t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)\r | |
1440 | {\r | |
1441 | int32 i, lnt;\r | |
1442 | t_stat r;\r | |
1443 | \r | |
1444 | if (cptr == NULL) {\r | |
1445 | for (i = 0; i < hst_lnt; i++) hst[i].typ = 0;\r | |
1446 | hst_p = 0;\r | |
1447 | return SCPE_OK;\r | |
1448 | }\r | |
1449 | lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);\r | |
1450 | if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;\r | |
1451 | hst_p = 0;\r | |
1452 | if (hst_lnt) {\r | |
1453 | free (hst);\r | |
1454 | hst_lnt = 0;\r | |
1455 | hst = NULL;\r | |
1456 | }\r | |
1457 | if (lnt) {\r | |
1458 | hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));\r | |
1459 | if (hst == NULL) return SCPE_MEM;\r | |
1460 | hst_lnt = lnt;\r | |
1461 | }\r | |
1462 | return SCPE_OK;\r | |
1463 | }\r | |
1464 | \r | |
1465 | /* Show history */\r | |
1466 | \r | |
1467 | t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)\r | |
1468 | {\r | |
1469 | int32 ov, k, di, lnt;\r | |
1470 | char *cptr = (char *) desc;\r | |
1471 | t_stat r;\r | |
1472 | t_value sim_eval;\r | |
1473 | InstHistory *h;\r | |
1474 | extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,\r | |
1475 | UNIT *uptr, int32 sw);\r | |
1476 | static char *cyc[] = { " ", " ", "INT", "TRP" };\r | |
1477 | \r | |
1478 | if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */\r | |
1479 | if (cptr) {\r | |
1480 | lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);\r | |
1481 | if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG;\r | |
1482 | }\r | |
1483 | else lnt = hst_lnt;\r | |
1484 | di = hst_p - lnt; /* work forward */\r | |
1485 | if (di < 0) di = di + hst_lnt;\r | |
1486 | fprintf (st, "CYC PC OV A B X EA IR\n\n");\r | |
1487 | for (k = 0; k < lnt; k++) { /* print specified */\r | |
1488 | h = &hst[(++di) % hst_lnt]; /* entry pointer */\r | |
1489 | if (h->typ) { /* instruction? */\r | |
1490 | ov = (h->typ >> 4) & 1; /* overflow */\r | |
1491 | fprintf (st, "%s %05o %o %08o %08o %08o ", cyc[h->typ & 3],\r | |
1492 | h->pc, ov, h->a, h->b, h->x);\r | |
1493 | if (h->ea & HIST_NOEA) fprintf (st, " ");\r | |
1494 | else fprintf (st, "%05o ", h->ea);\r | |
1495 | sim_eval = h->ir;\r | |
1496 | if ((fprint_sym (st, h->pc, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0)\r | |
1497 | fprintf (st, "(undefined) %08o", h->ir);\r | |
1498 | fputc ('\n', st); /* end line */\r | |
1499 | } /* end else instruction */\r | |
1500 | } /* end for */\r | |
1501 | return SCPE_OK;\r | |
1502 | }\r |