First Commit of my working state
[simh.git] / Ibm1130 / ibm1130_cpu.c
1 /* ibm1130_cpu.c: IBM 1130 CPU simulator
2
3 Based on the SIMH package written by Robert M Supnik
4
5 * (C) Copyright 2002, Brian Knittel.
6 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
7 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
8 * usual yada-yada. Please keep this notice and the copyright in any distributions
9 * or modifications.
10 *
11 * This is not a supported product, but I welcome bug reports and fixes.
12 * Mail to simh@ibm1130.org
13
14 25-Jun-01 BLK Written
15 10-May-02 BLK Fixed bug in MDX instruction
16 27-Mar-02 BLK Made BOSC work even in short form
17 16-Aug-02 BLK Fixed bug in multiply instruction; didn't work with negative values
18 18-Mar-03 BLK Fixed bug in divide instruction; didn't work with negative values
19 23-Jul-03 BLK Prevented tti polling in CGI mode
20 24-Nov-03 BLK Fixed carry bit error in subtract and subtract double, found by Bob Flanders
21 20-Oct-04 BLK Changed "(unsigned int32)" to "(uint32)" to accomodate improved definitions of simh types
22 Also commented out my echo command as it's now a standard simh command
23 27-Nov-05 BLK Added Arithmetic Factor Register support per Carl Claunch (GUI only)
24 06-Dec-06 BLK Moved CGI stuff out of ibm1130_cpu.c
25
26 >> To do: verify actual operands stored in ARF, need to get this from state diagrams in the schematic set
27 Also: determine how many bits are actually stored in the IAR in a real 1130, by forcing wraparound
28 and storing the IAR.
29
30 IBM 1800 support is just beginning. Mode set is done (SET CPU 1800 or SET CPU 1130).
31 Index registers are handled (1800 has real registers, 1130 uses core locations 1, 2 and 3 --
32 but does the 1800 make its hardware index registers appear in the address space?)
33 Need to add: memory protect feature, more interrupt levels, GUI mods, IO device mods, timers, watchdog.
34 Memory protect was interesting -- they borrowed one of the two parity bits. XIO(0) on 1800 is used for
35 interval timers, console data switches, console sense/program select/CE switches, interrupt mask register,
36 programmed interrupt, console interrupt and operations monitor (watchdog)
37 very interesting stuff.
38
39 The register state for the IBM 1130 CPU is:
40
41 IAR instruction address register
42 ACC accumulator
43 EXT accumulator extension
44 Oflow overflow bit
45 Carry carry bit
46 CES console entry switches
47 ipl current interrupt level, -1 = non interrupt
48 iplpending bitmap of pending interrupts
49 wait_state current CPU state: running or waiting
50 DSW console run/stop switch device status word
51 RUNMODE processor step/run mode (may also imply IntRun)
52 BREAK breakpoint address
53 WRU simulator-break character
54 IntRun Int Run flag (causes level 5 interrupt after every instruction)
55 ILSW0..5 interrupt level status words
56 XR1, 2, 3 for IBM 1800 only, index registers 1, 2, and 3
57
58 The SAR (storage address register) and SBR (storage buffer register) are updated
59 but not saved in the CPU state; they matter only to the GUI.
60
61 Interrupt handling: interrupts occur when any device on any level has an
62 active interrupt. XIO commands can clear specific IRQ bits. When this
63 happens, we have to evaluate all devices on the same IRQ level for remaining
64 indicators. The flag int_req is set with a bit corresponding to the IRQ level
65 when any interrupt indicator is activated.
66
67 The 1130 console has a switch that controls several run modes: SS (single processor
68 step), SCLK (single clock step), SINST (single instruction step), INT_RUN
69 (IRQ 5 after each non interrupt-handler instruction) and RUN (normal operation).
70 This simulator does not implement SS and SCLK. The simulator GUI console handles
71 SINST, so we only have to worry about INT_RUN. The console command SET CPU IntRun sets
72 the tmode (trace mode) flag; this causes a level 5 interrupt after each
73 instruction.
74
75 The IBM 1130 instruction formats are
76
77 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
78 | opcode | F| T | | general format
79 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
80
81 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
82 | opcode | 0| T | DISPLACEMENT | short instruction
83 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
84
85 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
86 | opcode | 1| T | I| MODIFIER | long instruction
87 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
88 | ADDRESS |
89 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
90
91 opcode in MSBits
92
93 F = format. 0 = short (1 word), 1 = long (2 word) instruction
94
95 T = Tag 00 = no index register (e.g. IAR relative)
96 01 = use index register 1 (e.g. core address 1 = M[1])
97 02 = use index register 2 (e.g. core address 2 = M[2])
98 03 = use index register 3 (e.g. core address 3 = M[3])
99
100 DISPLACEMENT = two's complement (must be sign-extended)
101
102 I = Indirect
103
104 Note that IAR = instruction address+1 when instruction is being decoded.
105
106 In normal addressing mode, effective address (EA) is computed as follows:
107
108 F = 0 T = 0 EA = IAR + DISPLACEMENT
109 0 1 IAR + DISPLACEMENT + M[1]
110 0 2 IAR + DISPLACEMENT + M[2]
111 0 3 IAR + DISPLACEMENT + M[3]
112
113 F = 1 T = 0 I = 0 EA = ADDRESS
114 1 1 0 ADDRESS + M[1]
115 1 2 0 ADDRESS + M[2]
116 1 3 0 ADDRESS + M[3]
117 1 0 1 M[ADDRESS]
118 1 1 1 M[ADDRESS + M[1]]
119 1 2 1 M[ADDRESS + M[2]]
120 1 3 1 M[ADDRESS + M[3]]
121
122 Loads or stores are then made to/from MEM[EA]. Some instructions have special
123 weird addressing modes. Simulator code precomputes standard addressing for
124 all instructions though it's not always used.
125
126 General notes:
127
128 Adding I/O devices requires modifications to three modules:
129
130 ibm1130_defs.h add interrupt request definitions
131 ibm1130_cpu.c add XIO command linkages
132 ibm1130_sys.c add to sim_devices array
133 */
134
135 /* ------------------------------------------------------------------------
136 * Definitions
137 * ------------------------------------------------------------------------ */
138
139 #include <stdarg.h>
140
141 #include "ibm1130_defs.h"
142
143 #define save_ibkpt (cpu_unit.u3) /* will be SAVEd */
144
145 #define UPDATE_BY_TIMER
146 #define ENABLE_BACKTRACE
147 /* #define USE_MY_ECHO_CMD */ /* simh now has echo command built in */
148 #define ENABLE_1800_SUPPORT /* define to enable support for 1800 CPU simulation mode */
149
150 static void cgi_start(void);
151 static void cgi_stop(t_stat reason);
152 static int simh_status_to_stopcode (int status);
153
154 /* hook pointers from scp.c */
155 void (*sim_vm_init) (void) = &sim_init;
156 extern char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream);
157 extern void (*sim_vm_post) (t_bool from_scp);
158 extern CTAB *sim_vm_cmd;
159
160 /* space to store extra simulator-specific commands */
161 #define MAX_EXTRA_COMMANDS 10
162 CTAB x_cmds[MAX_EXTRA_COMMANDS];
163
164 #ifdef _WIN32
165 # define CRLF "\r\n"
166 #else
167 # define CRLF "\n"
168 #endif
169
170 /* ------------------------------------------------------------------------
171 * initializers for globals
172 * ------------------------------------------------------------------------ */
173
174 #define SIGN_BIT(v) ((v) & 0x8000)
175 #define DWSIGN_BIT(v) ((v) & 0x80000000)
176
177 uint16 M[MAXMEMSIZE]; /* core memory, up to 32Kwords (note: don't even think about trying 64K) */
178 uint16 ILSW[6] = {0,0,0,0,0,0}; /* interrupt level status words */
179 uint16 XR[3] = {0,0,0}; /* IBM 1800 index registers */
180 int32 IAR; /* instruction address register */
181 int32 prev_IAR; /* instruction address register at start of current instruction */
182 int32 SAR, SBR; /* storage address/buffer registers */
183 int32 OP, TAG, CCC; /* instruction decoded pieces */
184 int32 CES; /* console entry switches */
185 int32 ACC, EXT; /* accumulator and extension */
186 int32 ARF; /* arithmetic factor, a non-addressable internal CPU register */
187 int32 RUNMODE; /* processor run/step mode */
188 int32 ipl = -1; /* current interrupt level (-1 = not handling irq) */
189 int32 iplpending = 0; /* interrupted IPL's */
190 int32 tbit = 0; /* trace flag (causes level 5 IRQ after each instr) */
191 int32 V = 0, C = 0; /* condition codes */
192 int32 wait_state = 0; /* wait state (waiting for an IRQ) */
193 int32 wait_lamp = TRUE; /* alternate indicator to light the wait lamp on the GUI */
194 int32 int_req = 0; /* sum of interrupt request levels active */
195 int32 int_lamps = 0; /* accumulated version of int_req - gives lamp persistence */
196 int32 int_mask; /* current active interrupt mask (ipl sensitive) */
197 int32 mem_mask; /* mask for memory address bits based on current memory size */
198 int32 cpu_dsw = 0; /* CPU device status word */
199 int32 ibkpt_addr = -1; /* breakpoint addr */
200 int32 sim_gui = TRUE; /* enable gui */
201 t_bool running = FALSE; /* TRUE if CPU is running */
202 t_bool power = TRUE; /* TRUE if CPU power is on */
203 t_bool cgi = FALSE; /* TRUE if we are running as a CGI program */
204 t_bool cgiwritable = FALSE; /* TRUE if we can write the disk images back to the image file in CGI mode */
205 t_bool is_1800 = FALSE; /* TRUE if we are simulating an IBM 1800 processor */
206 t_stat reason; /* CPU execution loop control */
207
208 static int32 int_masks[6] = {
209 0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E /* IPL 0 is highest prio (sees no other interrupts) */
210 };
211
212 /* ------------------------------------------------------------------------
213 * Function declarations
214 * ------------------------------------------------------------------------ */
215
216 t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
217 t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
218 t_stat cpu_reset (DEVICE *dptr);
219 t_stat cpu_svc (UNIT *uptr);
220 t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc);
221 t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc);
222 void calc_ints (void);
223
224 extern t_stat ts_wr (int32 data, int32 addr, int32 access);
225 extern t_stat detach_cmd (int flags, char *cptr);
226 extern UNIT cr_unit;
227 extern int32 sim_switches;
228
229 #ifdef ENABLE_BACKTRACE
230 static void archive_backtrace(char *inst);
231 static void reset_backtrace (void);
232 static void show_backtrace (int nshow);
233 static t_stat backtrace_cmd (int flag, char *cptr);
234 #else
235 #define archive_backtrace(inst)
236 #define reset_backtrace()
237 #define show_backtrace(ntrace)
238 #endif
239
240 #ifdef GUI_SUPPORT
241 # define ARFSET(v) ARF = (v) & 0xFFFF /* set Arithmetic Factor Register (used for display purposes only) */
242 #else
243 # define ARFSET(v) /* without GUI, no need for setting ARF */
244 #endif
245
246 static void init_console_window (void);
247 static void destroy_console_window (void);
248 static t_stat view_cmd (int flag, char *cptr);
249 static t_stat cpu_attach (UNIT *uptr, char *cptr);
250 static t_bool bsctest (int32 DSPLC, t_bool reset_V);
251 static void exit_irq (void);
252 static void trace_instruction (void);
253
254 /* ------------------------------------------------------------------------
255 * CPU data structures:
256 * cpu_dev CPU device descriptor
257 * cpu_unit CPU unit descriptor
258 * cpu_reg CPU register list
259 * cpu_mod CPU modifier list
260 *
261 * The CPU is attachable; attaching a file to it write a log of instructions
262 * and registers
263 * ------------------------------------------------------------------------ */
264
265 #define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* flag for memory size setting */
266 #define UNIT_1800 (1 << (UNIT_V_UF + 0)) /* flag for 1800 mode */
267
268 UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX | UNIT_BINK | UNIT_ATTABLE | UNIT_SEQ, INIMEMSIZE) };
269
270 REG cpu_reg[] = {
271 { HRDATA (IAR, IAR, 32) },
272 { HRDATA (ACC, ACC, 32) },
273 { HRDATA (EXT, EXT, 32) },
274 { FLDATA (Oflow, V, 1) },
275 { FLDATA (Carry, C, 1) },
276 { HRDATA (CES, CES, 32) },
277 { HRDATA (ipl, ipl, 32), REG_RO },
278 { HRDATA (iplpending, iplpending, 32), REG_RO },
279 { HRDATA (wait_state, wait_state, 32)},
280 { HRDATA (DSW, cpu_dsw, 32), REG_RO },
281 { HRDATA (RUNMODE, RUNMODE, 32) },
282 { HRDATA (BREAK, ibkpt_addr, 32) },
283 { ORDATA (WRU, sim_int_char, 8) },
284 { FLDATA (IntRun, tbit, 1) },
285
286 { HRDATA (ILSW0, ILSW[0], 32), REG_RO },
287 { HRDATA (ILSW1, ILSW[1], 32), REG_RO },
288 { HRDATA (ILSW2, ILSW[2], 32), REG_RO },
289 { HRDATA (ILSW3, ILSW[3], 32), REG_RO },
290 { HRDATA (ILSW4, ILSW[4], 32), REG_RO },
291 { HRDATA (ILSW5, ILSW[5], 32), REG_RO },
292
293 #ifdef ENABLE_1800_SUPPORT
294 { HRDATA (IS_1800, is_1800, 32), REG_RO|REG_HIDDEN}, /* is_1800 flag is part of state, but hidden */
295 { HRDATA (XR1, XR[0], 16), REG_RO|REG_HIDDEN}, /* index registers are unhidden if CPU set to 1800 mode */
296 { HRDATA (XR2, XR[1], 16), REG_RO|REG_HIDDEN},
297 { HRDATA (XR3, XR[2], 16), REG_RO|REG_HIDDEN},
298 #endif
299
300 { HRDATA (ARF, ARF, 32) },
301 { NULL}
302 };
303
304 MTAB cpu_mod[] = {
305 { UNIT_MSIZE, 4096, NULL, "4KW", &cpu_set_size},
306 { UNIT_MSIZE, 8192, NULL, "8KW", &cpu_set_size},
307 { UNIT_MSIZE, 16384, NULL, "16KW", &cpu_set_size},
308 { UNIT_MSIZE, 32768, NULL, "32KW", &cpu_set_size},
309 #ifdef ENABLE_1800_SUPPORT
310 { UNIT_1800, 0, "1130", "1130", &cpu_set_type},
311 { UNIT_1800, UNIT_1800, "1800", "1800", &cpu_set_type},
312 #endif
313 { 0 } };
314
315 DEVICE cpu_dev = {
316 "CPU", &cpu_unit, cpu_reg, cpu_mod,
317 1, 16, 16, 1, 16, 16,
318 &cpu_ex, &cpu_dep, &cpu_reset,
319 NULL, cpu_attach, NULL}; /* attaching to CPU creates cpu log file */
320
321 /* ------------------------------------------------------------------------
322 * Memory read/write -- save SAR and SBR on the way in and out
323 *
324 * (It can be helpful to set breakpoints on a = 1, 2, or 3 in these routines
325 * to detect attempts to read/set index registers using normal memory addessing.
326 * APL\1130 does this in some places, I think these are why it had to be modified
327 * to run on the 1800. Of course not all read/write to 1, 2 or implies an attempt
328 * to read/set and index register -- they could using the address in the normal way).
329 * ------------------------------------------------------------------------ */
330
331 int32 ReadW (int32 a)
332 {
333 SAR = a;
334 SBR = (int32) M[(a) & mem_mask];
335 return SBR;
336 }
337
338 void WriteW (int32 a, int32 d)
339 {
340 SAR = a;
341 SBR = d;
342 M[a & mem_mask] = (int16) d;
343 }
344
345 /* ------------------------------------------------------------------------
346 * read and write index registers. On the 1130, they're in core addresses 1, 2, 3.
347 * on the 1800, they're separate registers
348 * ------------------------------------------------------------------------ */
349
350 static uint16 ReadIndex (int32 tag)
351 {
352 #ifdef ENABLE_1800_SUPPORT
353 if (is_1800)
354 return XR[tag-1]; /* 1800: fetch from register */
355 #endif
356
357 SAR = tag; /* 1130: ordinary read from memory (like ReadW) */
358 SBR = (int32) M[(tag) & mem_mask];
359 return SBR;
360 }
361
362 static void WriteIndex (int32 tag, int32 d)
363 {
364 #ifdef ENABLE_1800_SUPPORT
365 if (is_1800) {
366 XR[tag-1] = d; /* 1800: store in register */
367 return;
368 }
369 #endif
370
371 SAR = tag; /* 1130: ordinary write to memory (same as WriteW) */
372 SBR = d;
373 M[tag & mem_mask] = (int16) d;
374 }
375
376 /* ------------------------------------------------------------------------
377 * upcase - force a string to uppercase (ASCII)
378 * ------------------------------------------------------------------------ */
379
380 char *upcase (char *str)
381 {
382 char *s;
383
384 for (s = str; *s; s++) {
385 if (*s >= 'a' && *s <= 'z')
386 *s -= 32;
387 }
388
389 return str;
390 }
391
392 /* ------------------------------------------------------------------------
393 * calc_ints - set appropriate bits in int_req if any interrupts are pending on given levels
394 *
395 * int_req:
396 * bit 5 4 3 2 1 0
397 * \ \ \ \ \ \
398 * \ \ \ \ \ interrupt level 5 pending (lowest priority)
399 * \ . . .
400 * interrupt level 0 pending (highest priority)
401 *
402 * int_mask is set according to current interrupt level (ipl)
403 *
404 * 0 0 0 0 0 0 ipl = 0 (currently servicing highest priority interrupt)
405 * 1 0 0 0 0 0 1
406 * 1 1 0 0 0 0 2
407 * 1 1 1 0 0 0 3
408 * 1 1 1 1 0 0 4
409 * 1 1 1 1 1 0 5 (currently servicing lowest priority interrupt)
410 * 1 1 1 1 1 1 -1 (not servicing an interrupt)
411 * ------------------------------------------------------------------------ */
412
413 void calc_ints (void)
414 {
415 register int i;
416 register int32 newbits = 0;
417
418 GUI_BEGIN_CRITICAL_SECTION /* using critical section here so we don't mislead the GUI thread */
419
420 for (i = 6; --i >= 0; ) {
421 newbits >>= 1;
422 if (ILSW[i])
423 newbits |= 0x20;
424 }
425
426 int_req = newbits;
427 int_lamps |= int_req;
428 int_mask = (ipl < 0) ? 0xFFFF : int_masks[ipl]; /* be sure this is set correctly */
429
430 GUI_END_CRITICAL_SECTION
431 }
432
433 /* ------------------------------------------------------------------------
434 * instruction processor
435 * ------------------------------------------------------------------------ */
436
437 #define INCREMENT_IAR IAR = (IAR + 1) & mem_mask
438 #define DECREMENT_IAR IAR = (IAR - 1) & mem_mask
439
440 void bail (char *msg)
441 {
442 printf("%s\n", msg);
443 exit(1);
444 }
445
446 static void weirdop (char *msg, int offset)
447 {
448 printf("Weird opcode: %s at %04x\n", msg, IAR+offset);
449 }
450
451 static char *xio_devs[] = {
452 "0?", "console", "1142card", "1134papertape",
453 "dsk0", "1627plot", "1132print", "switches",
454 "1231omr", "2501card", "comm", "b?",
455 "sys7", "d?", "e?", "f?",
456 "10?", "dsk1", "dsk2", "dsk3",
457 "dsk4", "dsk5", "dsk6", "dsk7+",
458 "18?", "2250disp", "2741attachment", "1b",
459 "1c?", "1d?", "1e?", "1f?"
460 };
461
462 static char *xio_funcs[] = {
463 "0?", "write", "read", "sense_irq",
464 "control", "initw", "initr", "sense"
465 };
466
467 t_stat sim_instr (void)
468 {
469 extern int32 sim_interval;
470 extern UNIT *sim_clock_queue;
471 int32 i, eaddr, INDIR, IR, F, DSPLC, word2, oldval, newval, src, src2, dst, abit, xbit;
472 int32 iocc_addr, iocc_op, iocc_dev, iocc_func, iocc_mod;
473 char msg[50];
474 int cwincount = 0, status;
475 static long ninstr = 0;
476 static char *intlabel[] = {"INT0","INT1","INT2","INT3","INT4","INT5"};
477
478 if (cgi) /* give CGI hook function a chance to do something */
479 cgi_start();
480
481 if (running) /* this is definitely not reentrant */
482 return -1;
483
484 if (! power) /* this matters only to the GUI */
485 return STOP_POWER_OFF;
486
487 running = TRUE;
488
489 mem_mask = MEMSIZE - 1; /* set other useful variables */
490 calc_ints();
491
492 /* Main instruction fetch/decode loop */
493
494 reason = 0;
495 wait_lamp = 0; /* release lock on wait lamp */
496
497 #ifdef GUI_SUPPORT
498 update_gui(TRUE);
499 gui_run(TRUE);
500 #endif
501
502 while (reason == 0) {
503 IAR &= mem_mask;
504
505 #ifdef GUI_SUPPORT
506 #ifndef UPDATE_BY_TIMER
507 #if (UPDATE_INTERVAL > 0)
508 if (--cwincount <= 0) {
509 update_gui(FALSE); /* update console lamps only every so many instructions */
510 cwincount = UPDATE_INTERVAL + (rand() % MIN(UPDATE_INTERVAL, 32));
511 }
512 #else
513 update_gui(FALSE);
514 #endif /* ifdef UPDATE_INTERVAL */
515 #endif /* ifndef UPDATE_BY_TIMER */
516 #endif /* ifdef GUI_SUPPORT */
517
518 if (sim_interval <= 0) { /* any events timed out? */
519 if (sim_clock_queue != NULL) {
520 if ((status = sim_process_event()) != 0)
521 reason = simh_status_to_stopcode(status);
522
523 calc_ints();
524 continue;
525 }
526 }
527
528 if (int_req & int_mask) { /* any pending interrupts? */
529 for (i = 0; i <= 5; i++) /* find highest pending interrupt */
530 if ((int_req & int_mask) & (0x20 >> i))
531 break;
532
533 if (i >= 6) { /* nothing to do? */
534 calc_ints(); /* weird. recalculate */
535 continue; /* back to fetch */
536 }
537
538 GUI_BEGIN_CRITICAL_SECTION
539
540 if (ipl >= 0) /* save previous IPL in bit stack */
541 iplpending |= (0x20 >> ipl);
542
543 ipl = i; /* set new interrupt level */
544 int_mask = int_masks[i]; /* set appropriate mask */
545
546 GUI_END_CRITICAL_SECTION
547
548 wait_state = 0; /* exit wait state */
549 eaddr = ReadW(8+i); /* get IRQ vector */
550 archive_backtrace(intlabel[i]);
551 WriteW(eaddr, IAR); /* save IAR */
552 IAR = (eaddr+1) & mem_mask; /* go to next address */
553 continue; /* now continue processing */
554 } /* end if int_req */
555
556 if (wait_state) { /* waiting? */
557 sim_interval = 0; /* run the clock out */
558
559 if (sim_qcount() <= (cgi ? 0 : 1)) { /* one routine queued? we're waiting for keyboard only */
560 if (keyboard_is_busy()) { /* we are actually waiting for a keystroke */
561 if ((status = sim_process_event()) != SCPE_OK) /* get it with wait_state still set */
562 reason = simh_status_to_stopcode(status);
563 }
564 else { /* CPU is not expecting a keystroke (keyboard interrupt) */
565 if (wait_state == WAIT_OP)
566 reason = STOP_WAIT; /* end the simulation */
567 else
568 reason = STOP_INVALID_INSTR;
569 }
570 }
571
572 if (gdu_active()) /* but don't stop simulator if 2250 GDU is running */
573 reason = 0;
574
575 continue;
576 }
577
578 if (IAR == ibkpt_addr) { /* simulator breakpoint? */
579 save_ibkpt = ibkpt_addr; /* save bkpt */
580 ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */
581 sim_activate(&cpu_unit, 1); /* sched re-enable after next instruction */
582 reason = STOP_IBKPT; /* stop simulation */
583 cwincount = 0;
584 continue;
585 }
586
587 ninstr++;
588 if (cpu_unit.flags & UNIT_ATT)
589 trace_instruction(); /* log CPU details if logging is enabled */
590
591 prev_IAR = IAR; /* save IAR before incrementing it */
592
593 IR = ReadW(IAR); /* fetch 1st word of instruction */
594 INCREMENT_IAR;
595 sim_interval = sim_interval - 1; /* this constitutes one tick of the simulation clock */
596
597 OP = (IR >> 11) & 0x1F; /* opcode */
598 F = IR & 0x0400; /* format bit: 1 = long instr */
599 TAG = IR & 0x0300; /* tag bits: index reg x */
600 if (TAG)
601 TAG >>= 8;
602
603 /* here I compute the usual effective address on the assumption that the instruction will need it. Some don't. */
604
605 if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */
606 INDIR = IR & 0x0080; /* indirect bit */
607 DSPLC = IR & 0x007F; /* displacement or modifier */
608 if (DSPLC & 0x0040)
609 DSPLC |= ~ 0x7F; /* sign extend */
610
611 word2 = ReadW(IAR); /* get reference address */
612 INCREMENT_IAR; /* bump the instruction address register */
613
614 eaddr = word2; /* assume standard addressing & compute effective address */
615 if (TAG) /* if indexed */
616 eaddr += ReadIndex(TAG); /* add index register value */
617 if (INDIR) /* if indirect addressing */
618 eaddr = ReadW(eaddr); /* pick up referenced address */
619 }
620 else { /* short instruction, use displacement */
621 INDIR = 0; /* never indirect */
622 DSPLC = IR & 0x00FF; /* get displacement */
623 if (DSPLC & 0x0080)
624 DSPLC |= ~ 0xFF;
625
626 if (TAG) /* if indexed */
627 eaddr = ReadIndex(TAG) + DSPLC; /* add index register value */
628 else
629 eaddr = IAR + DSPLC; /* otherwise relative to IAR after fetch */
630 }
631
632 switch (OP) { /* decode instruction */
633 case 0x01: /* --- XIO --- */
634 iocc_addr = ReadW(eaddr); /* get IOCC packet */
635 iocc_op = ReadW(eaddr|1); /* note 'or' not plus, address must be even for proper operation */
636
637 iocc_dev = (iocc_op >> 11) & 0x001F;
638 iocc_func = (iocc_op >> 8) & 0x0007;
639 iocc_mod = iocc_op & 0x00FF;
640
641 if (cpu_unit.flags & UNIT_ATT)
642 trace_io("* XIO %s %s mod %02x addr %04x", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr);
643
644 /* fprintf(stderr, "* XIO %s %s mod %02x addr %04x\n", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr); */
645
646 ACC = 0; /* ACC is destroyed, and default XIO_SENSE_DEV result is 0 */
647
648 switch (iocc_func) {
649 case XIO_UNUSED:
650 sprintf(msg, "Unknown op %x on device %02x", iocc_func, iocc_dev);
651 xio_error(msg);
652 break;
653
654 case XIO_SENSE_IRQ: /* examine current Interrupt Level Status Word */
655 ACC = (ipl >= 0) ? ILSW[ipl] : 0;
656 break;
657
658 default: /* perform device-specific operation */
659 switch (iocc_dev) {
660 case 0x01: /* console keyboard and printer */
661 xio_1131_console(iocc_addr, iocc_func, iocc_mod);
662 break;
663 case 0x02: /* 1142 card reader/punch */
664 xio_1142_card(iocc_addr, iocc_func, iocc_mod);
665 break;
666 case 0x03: /* 1134 paper tape reader/punch */
667 xio_1134_papertape(iocc_addr, iocc_func, iocc_mod);
668 break;
669 case 0x04: /* CPU disk storage */
670 xio_disk(iocc_addr, iocc_func, iocc_mod, 0);
671 break;
672 case 0x05: /* 1627 plotter */
673 xio_1627_plotter(iocc_addr, iocc_func, iocc_mod);
674 break;
675 case 0x06: /* 1132 Printer */
676 xio_1132_printer(iocc_addr, iocc_func, iocc_mod);
677 break;
678 case 0x07: /* console switches, stop key, run mode */
679 xio_1131_switches(iocc_addr, iocc_func, iocc_mod);
680 break;
681 case 0x08: /* 1231 optical mark reader */
682 xio_1231_optical(iocc_addr, iocc_func, iocc_mod);
683 break;
684 case 0x09: /* 2501 card reader */
685 xio_2501_card(iocc_addr, iocc_func, iocc_mod);
686 break;
687 case 0x0a: /* synchronous comm adapter */
688 xio_sca(iocc_addr, iocc_func, iocc_mod);
689 break;
690 case 0x0c: /* IBM System/7 interprocessor link */
691 xio_system7(iocc_addr, iocc_func, iocc_mod);
692 break;
693 case 0x11: /* 2310 Disk Storage, Drive 1, or 2311 Disk Storage Drive. Drive 1, Disk 1 */
694 xio_disk(iocc_addr, iocc_func, iocc_mod, 1);
695 break;
696 case 0x12: /* 2310 Disk Storage, Drive 2, or 2311 Disk Storage Drive. Drive 1, Disk 2 */
697 xio_disk(iocc_addr, iocc_func, iocc_mod, 2);
698 break;
699 case 0x13: /* 2310 Disk Storage, Drive 3, or 2311 Disk Storage Drive. Drive 1, Disk 3 */
700 xio_disk(iocc_addr, iocc_func, iocc_mod, 3);
701 break;
702 case 0x14: /* 2310 Disk Storage, Drive 4, or 2311 Disk Storage Drive. Drive 1, Disk 4 */
703 xio_disk(iocc_addr, iocc_func, iocc_mod, 4);
704 break;
705 case 0x15: /* 1403 Printer */
706 xio_1403_printer(iocc_addr, iocc_func, iocc_mod);
707 break;
708 case 0x16: /* 2311 Disk Storage Drive. Drive 1, Disk 5 */
709 xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
710 break;
711 case 0x17: /* 2311 Disk Storage Drive, Drive 2, Disk 1 through 5 */
712 xio_disk(iocc_addr, iocc_func, iocc_mod, -1);
713 break;
714 case 0x19: /* 2250 Display Unit */
715 xio_2250_display(iocc_addr, iocc_func, iocc_mod);
716 break;
717 case 0x1a: /* 2741 Attachment (nonstandard serial interface used by APL\1130 */
718 xio_t2741_terminal(iocc_addr, iocc_func, iocc_mod);
719 break;
720 default:
721 sprintf(msg, "unknown device %02x", iocc_dev);
722 xio_error(msg);
723 break;
724 }
725 }
726
727 calc_ints(); /* after every XIO, reset int_mask just in case */
728 break;
729
730 case 0x02: /* --- SLA,SLT,SLC,SLCA,NOP - Shift Left family --- */
731 if (F) {
732 weirdop("Long Left Shift", -2);
733 DECREMENT_IAR;
734 }
735
736 CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x003F;
737 ARFSET(CCC);
738 if (CCC == 0)
739 break; /* shift of zero is a NOP */
740
741 switch (IR & 0x00C0) {
742 case 0x0040: /* SLCA */
743 if (TAG) {
744 while (CCC > 0 && (ACC & 0x8000) == 0) {
745 ACC <<= 1;
746 CCC--;
747 }
748 C = (CCC != 0);
749 WriteIndex(TAG, (ReadIndex(TAG) & 0xFF00) | CCC); /* put low 6 bits back into index register and zero bits 8 and 9 */
750 break;
751 }
752 /* if TAG == 0, fall through and treat like normal shift SLA */
753
754 case 0x0000: /* SLA */
755 while (CCC > 0) {
756 C = (ACC & 0x8000);
757 ACC = (ACC << 1) & 0xFFFF;
758 CCC--;
759 }
760 break;
761
762 case 0x00C0: /* SLC */
763 if (TAG) {
764 while (CCC > 0 && (ACC & 0x8000) == 0) {
765 abit = (EXT & 0x8000) >> 15;
766 ACC = ((ACC << 1) & 0xFFFF) | abit;
767 EXT = (EXT << 1);
768 CCC--;
769 }
770 C = (CCC != 0);
771 WriteIndex(TAG, ReadIndex(TAG) & 0xFF00 | CCC); /* put 6 bits back into low byte of index register */
772 break;
773 }
774 /* if TAG == 0, fall through and treat like normal shift SLT */
775
776 case 0x0080: /* SLT */
777 while (CCC > 0) {
778 C = (ACC & 0x8000);
779 abit = (EXT & 0x8000) >> 15;
780 ACC = ((ACC << 1) & 0xFFFF) | abit;
781 EXT = (EXT << 1) & 0xFFFF;
782 CCC--;
783 }
784 break;
785
786 default:
787 bail("SLA switch, can't happen");
788 break;
789 }
790 break;
791
792 case 0x03: /* --- SRA, SRT, RTE - Shift Right family --- */
793 if (F) {
794 weirdop("Long Right Shift", -2);
795 DECREMENT_IAR;
796 }
797
798 CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x3F;
799 ARFSET(CCC);
800 if (CCC == 0)
801 break; /* NOP */
802
803 switch (IR & 0x00C0) {
804 case 0x0000: /* SRA */
805 ACC = (CCC < 16) ? ((ACC & 0xFFFF) >> CCC) : 0;
806 CCC = 0;
807 break;
808
809 case 0x0040: /* invalid */
810 wait_state = WAIT_INVALID_OP;
811 break;
812
813 case 0x0080: /* SRT */
814 while (CCC > 0) {
815 xbit = (ACC & 0x0001) << 15;
816 abit = (ACC & 0x8000);
817 ACC = (ACC >> 1) & 0x7FFF | abit;
818 EXT = (EXT >> 1) & 0x7FFF | xbit;
819 CCC--;
820 }
821 break;
822
823 case 0x00C0: /* RTE */
824 while (CCC > 0) {
825 abit = (EXT & 0x0001) << 15;
826 xbit = (ACC & 0x0001) << 15;
827 ACC = (ACC >> 1) & 0x7FFF | abit;
828 EXT = (EXT >> 1) & 0x7FFF | xbit;
829 CCC--;
830 }
831 break;
832
833 default:
834 bail("SRA switch, can't happen");
835 break;
836 }
837 break;
838
839 case 0x04: /* --- LDS - Load Status --- */
840 if (F) { /* never fetches second word? */
841 weirdop("Long LDS", -2);
842 DECREMENT_IAR;
843 }
844
845 V = (DSPLC & 1);
846 C = (DSPLC & 2) >> 1;
847 break;
848
849 case 0x05: /* --- STS - Store Status --- */
850 newval = ReadW(eaddr) & 0xFF00;
851 if (C)
852 newval |= 2;
853 if (V)
854 newval |= 1;
855
856 WriteW(eaddr, newval);
857 C = V = 0; /* clear flags after storing */
858 break;
859
860 case 0x06: /* --- WAIT --- */
861 wait_state = WAIT_OP;
862 if (F) { /* what happens if we use long format? */
863 weirdop("Long WAIT", -2);
864 DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word? */
865 }
866 break;
867
868 case 0x08: /* --- BSI - Branch and store IAR --- */
869 if (F) {
870 if (bsctest(IR, F)) /* do standard BSC long format testing */
871 break; /* if any condition is true, do nothing */
872 }
873 WriteW(eaddr, IAR); /* do subroutine call */
874 archive_backtrace("BSI"); /* save info in back-trace buffer */
875 IAR = (eaddr + 1) & mem_mask;
876 break;
877
878 case 0x09: /* --- BSC - Branch and skip on Condition --- */
879 if (F) {
880 if (bsctest(IR, F)) /* long format; any indicator cancels branch */
881 break;
882
883 archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */
884 IAR = eaddr; /* no indicator means branch taken */
885 }
886 else { /* short format: skip if any indicator hits */
887 if (bsctest(IR, F)) {
888 archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */
889 INCREMENT_IAR;
890 }
891 }
892 /* 27Mar02: moved this test out of the (F) condition; BOSC works even in the
893 * short form. The displacement field in this instruction is always the set of
894 * condition bits, and the interrupt clear bit doesn't collide. */
895
896 if (DSPLC & 0x40) { /* BOSC = exit from interrupt handler */
897 exit_irq();
898 cwincount = 0;
899 }
900 break;
901
902 case 0x0c: /* --- LDX - Load Index --- */
903 if (F)
904 eaddr = (INDIR) ? ReadW(word2) : word2;
905 else
906 eaddr = DSPLC;
907
908 if (TAG)
909 WriteIndex(TAG, eaddr);
910 else {
911 archive_backtrace("LDX"); /* save info in back-trace buffer */
912 IAR = eaddr; /* what happens in short form? can onlyjump to low addresses? */
913 }
914 break;
915
916 case 0x0d: /* --- STX - Store Index --- */
917 if (F) { /* compute EA without any indexing */
918 eaddr = (INDIR) ? ReadW(word2) : word2;
919 }
920 else {
921 eaddr = IAR + DSPLC;
922 }
923 WriteW(eaddr, TAG ? ReadIndex(TAG) : IAR);
924 break;
925
926 case 0x0e: /* --- MDX - Modify Index and Skip --- */
927 if (F) { /* long format: adjust memory location */
928 if (TAG) {
929 oldval = ReadIndex(TAG); /* add word2 to index */
930 newval = oldval + (INDIR ? ReadW(word2) : word2);
931 WriteIndex(TAG, newval);
932 }
933 else {
934 oldval = ReadW(word2);
935 DSPLC = IR & 0x00FF; /* use extended displacement (no INDIR bit, it's is part of displacement in this op) */
936 if (DSPLC & 0x0080)
937 DSPLC |= ~ 0xFF;
938 newval = oldval + DSPLC; /* add modifier to @word2 */
939 WriteW(word2, newval);
940 }
941 }
942 else { /* short format: adust IAR or index */
943 if (TAG) {
944 oldval = ReadIndex(TAG); /* add displacement to index */
945 newval = oldval + DSPLC;
946 WriteIndex(TAG, newval);
947 }
948 else {
949 oldval = IAR; /* add displacement to IAR */
950 newval = IAR + DSPLC;
951 archive_backtrace("MDX");
952 IAR = newval & mem_mask;
953 }
954 }
955
956 if ((F || TAG) && (((newval & 0xFFFF) == 0) || ((oldval & 0x8000) != (newval & 0x8000)))) {
957 archive_backtrace("SKP");
958 INCREMENT_IAR; /* skip if index sign change or zero */
959 }
960 break;
961
962 case 0x10: /* --- A - Add --- */
963 /* in adds and subtracts, carry is set or cleared, overflow is set only */
964 src = ReadW(eaddr);
965 ARFSET(src);
966 src2 = ACC;
967 ACC = (ACC + src) & 0xFFFF;
968
969 C = ACC < src;
970 if (! V)
971 V = SIGN_BIT((~src ^ src2) & (src ^ ACC));
972 break;
973
974 case 0x11: /* --- AD - Add Double --- */
975 src = ((ACC << 16) | (EXT & 0xFFFF));
976 ARFSET(EXT);
977 src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
978 dst = src + src2;
979 ACC = (dst >> 16) & 0xFFFF;
980 EXT = dst & 0xFFFF;
981
982 C = (uint32) dst < (uint32) src;
983 if (! V)
984 V = DWSIGN_BIT((~src ^ src2) & (src ^ dst));
985 break;
986
987 case 0x12: /* --- S - Subtract --- */
988 src = ACC;
989 ARFSET(src);
990 src2 = ReadW(eaddr);
991 ACC = (ACC-src2) & 0xFFFF;
992
993 C = src < src2;
994 if (! V)
995 V = SIGN_BIT((src ^ src2) & (src ^ ACC));
996 break;
997
998 case 0x13: /* --- SD - Subtract Double --- */
999 src = ((ACC << 16) | (EXT & 0xFFFF));
1000 ARFSET(EXT);
1001 src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
1002 dst = src - src2;
1003 ACC = (dst >> 16) & 0xFFFF;
1004 EXT = dst & 0xFFFF;
1005
1006 C = (uint32) src < (uint32) src2;
1007 if (! V)
1008 V = DWSIGN_BIT((src ^ src2) & (src ^ dst));
1009 break;
1010
1011 case 0x14: /* --- M - Multiply --- */
1012 if ((src = ACC & 0xFFFF) & 0x8000) /* sign extend the values */
1013 src |= ~0xFFFF;
1014 if ((src2 = ReadW(eaddr)) & 0x8000)
1015 src2 |= ~0xFFFF;
1016
1017 ARFSET(src2);
1018 dst = src * src2;
1019 ACC = (dst >> 16) & 0xFFFF; /* split the results */
1020 EXT = dst & 0xFFFF;
1021 break;
1022
1023 case 0x15: /* --- D - Divide --- */
1024 src = ((ACC << 16) | (EXT & 0xFFFF));
1025 if ((src2 = ReadW(eaddr)) & 0x8000)
1026 src2 |= ~0xFFFF; /* oops: sign extend was missing, fixed 18Mar03 */
1027
1028 ARFSET(src2);
1029
1030 if (src2 == 0)
1031 V = 1; /* divide by zero just sets overflow, ACC & EXT are undefined */
1032 else {
1033 ACC = (src / src2) & 0xFFFF;
1034 EXT = (src % src2) & 0xFFFF;
1035 }
1036 break;
1037
1038 case 0x18: /* --- LD - Load ACC --- */
1039 ACC = ReadW(eaddr);
1040 break;
1041
1042 case 0x19: /* --- LDD - Load Double --- */
1043 ACC = ReadW(eaddr);
1044 EXT = ReadW(eaddr|1); /* notice address is |1 not +1 */
1045 break;
1046
1047 case 0x1a: /* --- STO - Store ACC --- */
1048 WriteW(eaddr, ACC);
1049 break;
1050
1051 case 0x1b: /* --- STD - Store Double --- */
1052 WriteW(eaddr|1, EXT);
1053 WriteW(eaddr, ACC); /* order is important: if odd addr, only ACC is stored */
1054 break;
1055
1056 case 0x1c: /* --- AND - Logical AND --- */
1057 src = ReadW(eaddr);
1058 ARFSET(src);
1059 ACC &= src;
1060 break;
1061
1062 case 0x1d: /* --- OR - Logical OR --- */
1063 src = ReadW(eaddr);
1064 ARFSET(src);
1065 ACC |= src;
1066 break;
1067
1068 case 0x1e: /* --- EOR - Logical Excl OR --- */
1069 src = ReadW(eaddr);
1070 ARFSET(src);
1071 ACC ^= src;
1072 break;
1073
1074 case 0x16:
1075 case 0x17:
1076 #ifdef ENABLE_1800_SUPPORT
1077 if (is_1800) {
1078 if (OP == 0x16) { /* --- CMP - Compare --- */
1079 src = ACC; /* like subtract but result isn't stored */
1080 src2 = ReadW(eaddr);
1081 dst = (ACC-src2) & 0xFFFF;
1082 C = src < src2;
1083
1084 if (dst & 0x8000) /* if ACC < operand, skip 1 instruction */
1085 IAR = IAR+1;
1086 else if ((dst & 0xFFFF) == 0) /* if ACC == operand, skip 2 instructions */
1087 IAR = IAR+2;
1088 }
1089 else { /* --- DCMP - Compare Double --- */
1090 src = ((ACC << 16) | (EXT & 0xFFFF));
1091 src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1);
1092 dst = src - src2;
1093 C = (uint32) src < (uint32) src2;
1094
1095 if (dst & 0x80000000) /* if ACC_EXT < operand, skip 1 instruction */
1096 IAR = IAR+1;
1097 else if (dst == 0) /* if ACC_EXT == operand, skip 2 instructions */
1098 IAR = IAR+2;
1099 }
1100
1101 break; /* these are legal instructions on the 1800 */
1102 }
1103 #endif
1104 /* 1130: these are not legal instructions, fall through */
1105
1106 default:
1107 /* all invalid instructions act like waits */
1108 /* case 0x00: */
1109 /* case 0x07: */
1110 /* case 0x0a: */
1111 /* case 0x0b: */
1112 /* case 0x0e: */
1113 /* case 0x0f: */
1114 /* case 0x1f: */
1115 wait_state = WAIT_INVALID_OP;
1116 if (F)
1117 DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word? */
1118
1119 break;
1120 } /* end instruction decode switch */
1121
1122 if (RUNMODE != MODE_RUN && RUNMODE != MODE_INT_RUN)
1123 reason = STOP_WAIT;
1124
1125 if (tbit && (ipl < 0)) { /* if INT_RUN mode, set IRQ5 after this instr */
1126 GUI_BEGIN_CRITICAL_SECTION
1127 SETBIT(cpu_dsw, CPU_DSW_INT_RUN);
1128 SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
1129 int_req |= INT_REQ_5;
1130 GUI_END_CRITICAL_SECTION
1131 }
1132 } /* end main loop */
1133
1134 #ifdef GUI_SUPPORT
1135 gui_run(FALSE);
1136 #endif
1137
1138 running = FALSE;
1139 int_lamps = 0; /* display only currently active interrupts while halted */
1140
1141 if (reason == STOP_WAIT || reason == STOP_INVALID_INSTR) {
1142 wait_state = 0; /* on resume, don't wait */
1143 wait_lamp = TRUE; /* but keep the lamp lit on the GUI */
1144
1145 CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP); /* and on resume, reset program start bit */
1146 if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0)
1147 CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
1148 }
1149
1150 if (cgi) /* give CGI hook function a chance to do something */
1151 cgi_stop(reason);
1152
1153 return reason;
1154 }
1155
1156 /*
1157 * simh_status_to_stopcode - convert a SCPE_xxx value from sim_process_event into a STOP_xxx code
1158 */
1159
1160 static int simh_status_to_stopcode (int status)
1161 {
1162 return (status == SCPE_BREAK) ? STOP_BREAK :
1163 (status == SCPE_STOP) ? STOP_IMMEDIATE :
1164 (status == SCPE_STEP) ? STOP_STEP : STOP_OTHER;
1165 }
1166
1167 /* ------------------------------------------------------------------------
1168 * bsctest - perform standard set of condition tests. We return TRUE if any
1169 * of the condition bits specified in DSPLC test positive, FALSE if none are true.
1170 * If reset_V is TRUE, we reset the oVerflow flag after testing it.
1171 * ------------------------------------------------------------------------ */
1172
1173 static t_bool bsctest (int32 DSPLC, t_bool reset_V)
1174 {
1175 if (DSPLC & 0x01) { /* Overflow off (note inverted sense) */
1176 if (! V)
1177 return TRUE;
1178 else if (reset_V) /* reset after testing */
1179 V = 0;
1180 }
1181
1182 if (DSPLC & 0x02) { /* Carry off (note inverted sense) */
1183 if (! C)
1184 return TRUE;
1185 }
1186
1187 if (DSPLC & 0x04) /* Even */
1188 if ((ACC & 1) == 0)
1189 return TRUE;
1190
1191 if (DSPLC & 0x08) /* Positive */
1192 if ((ACC & 0x8000) == 0 && ACC != 0)
1193 return TRUE;
1194
1195 if (DSPLC & 0x10) /* Negative */
1196 if (ACC & 0x8000)
1197 return TRUE;
1198
1199 if (DSPLC & 0x20) /* Zero */
1200 if ((ACC & 0xFFFF) == 0)
1201 return TRUE;
1202
1203 return FALSE;
1204 }
1205
1206 /* ------------------------------------------------------------------------
1207 * exit_irq - pop interrupt stack as part of return from subroutine (BOSC)
1208 * ------------------------------------------------------------------------ */
1209
1210 static void exit_irq (void)
1211 {
1212 int i, bit;
1213
1214 GUI_BEGIN_CRITICAL_SECTION
1215
1216 if (ipl == 5 && tbit) { /* if we are exiting an INT_RUN interrupt, clear it for the next instruction */
1217 CLRBIT(cpu_dsw, CPU_DSW_INT_RUN);
1218 if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0)
1219 CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
1220 }
1221
1222 ipl = -1; /* default: return to main processor level */
1223 int_mask = 0xFFFF;
1224
1225 if (iplpending) { /* restore previous interrupt status */
1226 for (i = 0, bit = 0x20; i < 6; i++, bit >>= 1) {
1227 if (iplpending & bit) {
1228 iplpending &= ~bit;
1229 ipl = i;
1230 int_mask = int_masks[i];
1231 break;
1232 }
1233 }
1234 }
1235 GUI_END_CRITICAL_SECTION
1236
1237 calc_ints(); /* recompute pending interrupt mask */
1238 } /* because we probably cleared some ILSW bits before this instruction */
1239
1240 /* let a device halt the simulation */
1241
1242 void break_simulation (t_stat stopreason)
1243 {
1244 reason = stopreason;
1245 }
1246
1247 /* ------------------------------------------------------------------------
1248 * SIMH required routines
1249 * ------------------------------------------------------------------------ */
1250
1251 /* ------------------------------------------------------------------------
1252 * Reset routine
1253 * ------------------------------------------------------------------------ */
1254
1255 t_stat cpu_reset (DEVICE *dptr)
1256 {
1257 wait_state = 0; /* cancel wait */
1258 wait_lamp = TRUE; /* but keep the wait lamp lit on the GUI */
1259
1260 if (cpu_unit.flags & UNIT_ATT) { /* record reset in CPU log */
1261 fseek(cpu_unit.fileref, 0, SEEK_END);
1262 fprintf(cpu_unit.fileref, "---RESET---" CRLF);
1263 }
1264
1265 GUI_BEGIN_CRITICAL_SECTION
1266
1267 CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP|CPU_DSW_INT_RUN);
1268 CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);
1269
1270 reset_backtrace();
1271
1272 ipl = -1;
1273 int_mask = 0xFFFF;
1274 int_req = 0; /* hmmm, it SHOULD reset the int req, right? */
1275 int_lamps = 0;
1276 iplpending = 0;
1277 memset(ILSW, 0, sizeof(ILSW));
1278
1279 cpu_dsw = 0; /* clear int req and prot stop bits */
1280 tbit = 0; /* cancel INT_RUN mode */
1281
1282 C = V = 0; /* clear processor flags */
1283 IAR = SAR = SBR = 0; /* clear IAR and other registers */
1284 ACC = EXT = OP = TAG = CCC = C = V = 0;
1285
1286 mem_mask = MEMSIZE - 1; /* wraparound mask */
1287
1288 GUI_END_CRITICAL_SECTION
1289
1290 return cpu_svc(&cpu_unit); /* reset breakpoint */
1291 }
1292
1293 /* ------------------------------------------------------------------------
1294 * Memory examine
1295 * ------------------------------------------------------------------------ */
1296
1297 t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
1298 {
1299 if (vptr == NULL) return SCPE_ARG;
1300
1301 /* check this out -- save command hits it in weird way */
1302 /* I wish I remembered what I meant when I wrote that */
1303 if (addr < MEMSIZE) {
1304 *vptr = M[addr] & 0xFFFF;
1305 return SCPE_OK;
1306 }
1307 return SCPE_NXM;
1308 }
1309
1310 /* ------------------------------------------------------------------------
1311 * Memory deposit
1312 * ------------------------------------------------------------------------ */
1313
1314 t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
1315 {
1316 if (addr < MEMSIZE) {
1317 M[addr] = (uint16) (val & 0xFFFF);
1318 return SCPE_OK;
1319 }
1320 return SCPE_NXM;
1321 }
1322
1323 /* ------------------------------------------------------------------------
1324 * Breakpoint service
1325 * ------------------------------------------------------------------------ */
1326
1327 t_stat cpu_svc (UNIT *uptr)
1328 {
1329 if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt)
1330 ibkpt_addr = save_ibkpt;
1331
1332 save_ibkpt = -1;
1333 return SCPE_OK;
1334 }
1335
1336 /* ------------------------------------------------------------------------
1337 * Memory allocation
1338 * ------------------------------------------------------------------------ */
1339
1340 t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc)
1341 {
1342 t_bool used;
1343 int32 i;
1344
1345 if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0))
1346 return SCPE_ARG;
1347
1348 for (i = value, used = FALSE; i < (int32) MEMSIZE; i++) {
1349 if (M[i] != 0) {
1350 used = TRUE;
1351 break;
1352 }
1353 }
1354
1355 if (used && ! get_yn ("Really truncate memory [N]?", FALSE))
1356 return SCPE_OK;
1357
1358 for (i = MEMSIZE; i < value; i++) /* clear expanded area */
1359 M[i] = 0;
1360
1361 MEMSIZE = value;
1362 mem_mask = MEMSIZE - 1;
1363
1364 return SCPE_OK;
1365 }
1366
1367 /* processor type */
1368
1369 t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc)
1370 {
1371 REG *r;
1372
1373 is_1800 = (value & UNIT_1800) != 0; /* set is_1800 mode flag */
1374
1375 for (r = cpu_reg; r->name != NULL; r++) { /* unhide or hide 1800-specific registers & state */
1376 if (strnicmp(r->name, "XR", 2) == 0) {
1377 if (value & UNIT_1800)
1378 CLRBIT(r->flags, REG_HIDDEN|REG_RO);
1379 else
1380 SETBIT(r->flags, REG_HIDDEN|REG_RO);
1381 }
1382 }
1383
1384 return SCPE_OK;
1385 }
1386
1387 /* ------------------------------------------------------------------------
1388 * IO function for console switches
1389 * ------------------------------------------------------------------------ */
1390
1391 void xio_1131_switches (int32 addr, int32 func, int32 modify)
1392 {
1393 char msg[80];
1394
1395 switch (func) {
1396 case XIO_READ:
1397 WriteW(addr, CES);
1398 break;
1399
1400 case XIO_SENSE_DEV:
1401 ACC = cpu_dsw;
1402 break;
1403
1404 default:
1405 sprintf(msg, "Invalid console switch function %x", func);
1406 xio_error(msg);
1407 }
1408 }
1409
1410 /* ------------------------------------------------------------------------
1411 * Illegal IO operation. Not yet sure what the actual CPU does in this case
1412 * ------------------------------------------------------------------------ */
1413
1414 void xio_error (char *msg)
1415 {
1416 printf("*** XIO error at %04x: %s\n", prev_IAR, msg);
1417 if (cgi) /* if this happens in CGI mode, probably best to halt */
1418 break_simulation(STOP_CRASH);
1419 }
1420
1421 /* ------------------------------------------------------------------------
1422 * register_cmd - add a command to the extensible command table
1423 * ------------------------------------------------------------------------ */
1424
1425 t_stat register_cmd (char *name, t_stat (*action)(int32 flag, char *ptr), int arg, char *help)
1426 {
1427 int i;
1428
1429 for (i = 0; i < MAX_EXTRA_COMMANDS; i++) { /* find end of command table */
1430 if (x_cmds[i].action == action)
1431 return SCPE_OK; /* command is already there, just return */
1432 if (x_cmds[i].name == NULL)
1433 break;
1434 }
1435
1436 if (i >= (MAX_EXTRA_COMMANDS-1)) { /* no more room (we need room for the NULL) */
1437 fprintf(stderr, "The command table is full - rebuild the simulator with more free slots\n");
1438 return SCPE_ARG;
1439 }
1440
1441 x_cmds[i].action = action; /* add new command */
1442 x_cmds[i].name = name;
1443 x_cmds[i].arg = arg;
1444 x_cmds[i].help = help;
1445
1446 i++;
1447 x_cmds[i].action = NULL; /* move the NULL terminator */
1448 x_cmds[i].name = NULL;
1449
1450 return SCPE_OK;
1451 }
1452
1453 #ifdef USE_MY_ECHO_CMD
1454 /* ------------------------------------------------------------------------
1455 * echo_cmd - just echo the command line
1456 * ------------------------------------------------------------------------ */
1457
1458 static t_stat echo_cmd (int flag, char *cptr)
1459 {
1460 printf("%s\n", cptr);
1461 return SCPE_OK;
1462 }
1463 #endif
1464
1465 /* ------------------------------------------------------------------------
1466 * sim_init - initialize simulator upon startup of scp, before reset
1467 * ------------------------------------------------------------------------ */
1468
1469 void sim_init (void)
1470 {
1471 sim_gui = ! (sim_switches & SWMASK('G')); /* -g means no GUI */
1472
1473 sim_vm_cmd = x_cmds; /* provide list of additional commands */
1474
1475 #ifdef GUI_SUPPORT
1476 /* set hook routines for GUI command processing */
1477 if (sim_gui) {
1478 sim_vm_read = &read_cmdline;
1479 sim_vm_post = &update_gui;
1480 }
1481 #endif
1482
1483 #ifdef ENABLE_BACKTRACE
1484 /* add the BACKTRACE command */
1485 register_cmd("BACKTRACE", &backtrace_cmd, 0, "ba{cktrace} {n} list last n branches/skips/interrupts\n");
1486 #endif
1487
1488 register_cmd("VIEW", &view_cmd, 0, "v{iew} filename view a text file with notepad\n");
1489
1490 #ifdef USE_MY_ECHO_CMD
1491 register_cmd("ECHO", &echo_cmd, 0, "echo args... echo arguments passed to command\n");
1492 #endif
1493 }
1494
1495 /* ------------------------------------------------------------------------
1496 * archive_backtrace - record a jump, skip, branch or whatever
1497 * ------------------------------------------------------------------------ */
1498
1499 #ifdef ENABLE_BACKTRACE
1500
1501 #define MAXARCHIVE 16
1502
1503 static struct tag_arch {
1504 int iar;
1505 char *inst;
1506 } arch[MAXARCHIVE];
1507 int narchived = 0, archind = 0;
1508
1509 static void archive_backtrace (char *inst)
1510 {
1511 static int prevind;
1512
1513 if (narchived < MAXARCHIVE)
1514 narchived++;
1515
1516 if (narchived > 0 && arch[prevind].iar == prev_IAR)
1517 return;
1518
1519 arch[archind].iar = prev_IAR;
1520 arch[archind].inst = inst;
1521
1522 prevind = archind;
1523 archind = (archind+1) % MAXARCHIVE;
1524 }
1525
1526 static void reset_backtrace (void)
1527 {
1528 narchived = 0;
1529 archind = 0;
1530 }
1531
1532 void void_backtrace (int afrom, int ato)
1533 {
1534 int i;
1535
1536 afrom &= mem_mask;
1537 ato &= mem_mask;
1538
1539 for (i = 0; i < narchived; i++)
1540 if (arch[i].iar >= afrom && arch[i].iar <= ato)
1541 arch[i].inst = "OVERWRITTEN";
1542 }
1543
1544 static void show_backtrace (int nshow)
1545 {
1546 int n = narchived, i = archind;
1547
1548 if (n > nshow) n = nshow;
1549
1550 while (--n >= 0) {
1551 i = (i > 0) ? (i-1) : (MAXARCHIVE-1);
1552 printf("from %04x (%s) ", arch[i].iar, arch[i].inst);
1553 }
1554
1555 if (narchived)
1556 putchar('\n');
1557 }
1558
1559 static t_stat backtrace_cmd (int flag, char *cptr)
1560 {
1561 int n;
1562
1563 if ((n = atoi(cptr)) <= 0)
1564 n = 6;
1565
1566 show_backtrace(n);
1567 return SCPE_OK;
1568 }
1569 #else
1570
1571 /* stub this for the disk routine */
1572
1573 void void_backtrace (int afrom, int ato)
1574 {
1575 }
1576
1577 #endif
1578
1579 /*************************************************************************************
1580 * CPU log routines -- attaching a file to the CPU creates a trace of instructions and register values
1581 *
1582 * Syntax is WEIRD:
1583 *
1584 * attach cpu logfile log instructions and registers to file "logfile"
1585 * attach -f cpu cpu.log log instructions, registers and floating point acc
1586 * attach -m cpu mapfile logfile read addresses from "mapfile", log instructions to "logfile"
1587 * attach -f -m cpu mapfile logfile same and log floating point stuff too
1588 *
1589 * mapfile if specified is a list of symbols and addresses of the form:
1590 * symbol hexval
1591 *
1592 * e.g.
1593 * FSIN 082E
1594 * FARC 09D4
1595 * FMPY 09A4
1596 * NORM 0976
1597 * XMDS 095A
1598 * START 021A
1599 *
1600 * These values are easily obtained from a load map created by
1601 * XEQ L
1602 *
1603 * The log output is of the form
1604 *
1605 * IAR ACC EXT (flt) XR1 XR2 XR3 CVI FAC OPERATION
1606 * --------------- ---- ---- -------- ---- ---- ---- --- ------------- -----------------------
1607 * 002a 002a 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 4c80 BSC I ,0028
1608 * 081d PAUSE+000d 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7400 MDM L 00f0,0 (0)
1609 * 0820 PAUSE+0010 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7201 MDX 2 0001
1610 * 0821 PAUSE+0011 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6a03 STX 2 0003
1611 * 0822 PAUSE+0012 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6600 LDX L2 0231
1612 * 0824 PAUSE+0014 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,0237
1613 * 0237 START+001d 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4480 BSI I ,3fff
1614 * 082f FSIN +0001 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4356 BSI 3 0056
1615 * 3fd5 ILS01+35dd 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,08de
1616 *
1617 * IAR - instruction address register value, optionally including symbol and offset
1618 * ACC - accumulator
1619 * EXT - extension
1620 * flt - ACC+EXT interpreted as the mantissa of a floating pt number (value 0.5 -> 1)
1621 * XR* - index registers
1622 * CVI - carry, overflow and interrupt indicators
1623 * FAC - floating point accumulator (exponent at 125+XR3, mantissa at 126+XR3 and 127+XR3)
1624 * OP - opcode value and crude disassembly
1625 *
1626 * flt and FAC are displayed only when the -f flag is specified in the attach command
1627 * The label and offset and displayed only when the -m flag is specified in the attach command
1628 *
1629 * The register values shown are the values BEFORE the instruction is executed.
1630 *************************************************************************************/
1631
1632 t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw);
1633
1634 typedef struct tag_symentry {
1635 struct tag_symentry *next;
1636 int addr;
1637 char sym[6];
1638 } SYMENTRY, *PSYMENTRY;
1639
1640 static PSYMENTRY syms = NULL;
1641 static t_bool new_log, log_fac;
1642
1643 static t_stat cpu_attach (UNIT *uptr, char *cptr)
1644 {
1645 char mapfile[200], buf[200], sym[100];
1646 int addr;
1647 PSYMENTRY n, prv, s;
1648 FILE *fd;
1649
1650 remove(cptr); /* delete old log file, if present */
1651 new_log = TRUE;
1652 log_fac = sim_switches & SWMASK ('F'); /* display the FAC and the ACC/EXT as fixed point. */
1653
1654 for (s = syms; s != NULL; s = n) { /* free any old map entries */
1655 n = s->next;
1656 free(s);
1657 }
1658 syms = NULL;
1659
1660 if (sim_switches & SWMASK('M')) { /* use a map file to display relative addresses */
1661 cptr = get_glyph(cptr, mapfile, 0);
1662 if (! *mapfile) {
1663 printf("/m must be followed by a filename\n");
1664 return SCPE_ARG;
1665 }
1666 if ((fd = fopen(mapfile, "r")) == NULL) {
1667 perror(mapfile);
1668 return SCPE_OPENERR;
1669 }
1670
1671 while (fgets(buf, sizeof(buf), fd) != NULL) { /* read symbols & addresses, link in descending address order */
1672 if (sscanf(buf, "%s %x", sym, &addr) != 2)
1673 continue;
1674 if (*buf == ';')
1675 continue;
1676
1677 for (prv = NULL, s = syms; s != NULL; prv = s, s = s->next) {
1678 if (s->addr < addr)
1679 break;
1680 }
1681
1682 if ((n = malloc(sizeof(SYMENTRY))) == NULL) {
1683 printf("out of memory reading map!\n");
1684 break;
1685 }
1686
1687 sym[5] = '\0';
1688 strcpy(n->sym, sym);
1689 upcase(n->sym);
1690 n->addr = addr;
1691
1692 if (prv == NULL) {
1693 n->next = syms;
1694 syms = n;
1695 }
1696 else {
1697 n->next = prv->next;
1698 prv ->next = n;
1699 }
1700 }
1701 fclose(fd);
1702 }
1703
1704 return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */
1705 }
1706
1707 static void trace_instruction (void)
1708 {
1709 t_value v[2];
1710 float fac;
1711 short exp;
1712 int addr;
1713 PSYMENTRY s;
1714 long mant, sign;
1715 char facstr[20], fltstr[20];
1716
1717 if ((cpu_unit.flags & UNIT_ATT) == 0)
1718 return;
1719
1720 if (new_log) {
1721 fseek(cpu_unit.fileref, 0, SEEK_END);
1722 new_log = FALSE;
1723
1724 fprintf(cpu_unit.fileref, " IAR%s ACC EXT %s XR1 XR2 XR3 CVI %sOPERATION" CRLF,
1725 syms ? " " : "", log_fac ? " (flt) " : "", log_fac ? " FAC " : "");
1726 fprintf(cpu_unit.fileref, "----%s ---- ---- %s---- ---- ---- --- %s-----------------------" CRLF,
1727 syms ? "-----------" : "", log_fac ? "-------- " : "", log_fac ? "------------- " : "");
1728 }
1729
1730 if (! log_fac)
1731 facstr[0] = fltstr[0] = '\0';
1732 else {
1733 mant = ((ACC & 0xFFFF) << 16) | (EXT & 0xFFFF);
1734 if (mant == 0x80000000) {
1735 sign = TRUE;
1736 fac = 1.f;
1737 }
1738 else {
1739 if ((sign = mant & 0x80000000) != 0)
1740 mant = -mant;
1741 fac = (float) mant * ((float) 1./ (float) (unsigned long) 0x80000000);
1742 }
1743 sprintf(fltstr, "%c%.5f ", sign ? '-' : ' ', fac);
1744
1745 if (BETWEEN(M[3], 0x300, MEMSIZE-128)) {
1746 exp = (short) ((M[M[3]+125] & 0xFF) - 128);
1747 mant = (M[M[3]+126] << 8) | ((M[M[3]+127] >> 8) & 0xFF);
1748 if ((sign = (mant & 0x00800000)) != 0)
1749 mant = (-mant) & 0x00FFFFFF;
1750
1751 fac = (float) mant * ((float) 1. / (float) 0x00800000);
1752
1753 if (exp > 30) {
1754 fac *= (float) (1 << 30);
1755 exp -= 30;
1756 while (exp > 0)
1757 fac *= 2;
1758 }
1759 else if (exp > 0)
1760 fac *= (float) (1 << exp);
1761 else if (exp < -30) {
1762 fac /= (float) (1 << 30);
1763 exp += 30;
1764 while (exp < 0)
1765 fac /= 2;
1766 }
1767 else if (exp < 0)
1768 fac /= (float) (1 << -exp);
1769
1770 sprintf(facstr, "%c%.5e ", sign ? '-' : ' ', fac);
1771 }
1772 else
1773 strcpy(facstr, " ");
1774 }
1775
1776 addr = IAR & 0xFFFF;
1777 fprintf(cpu_unit.fileref, "%04x ", addr);
1778
1779 if (syms) {
1780 for (s = syms; s != NULL; s = s->next)
1781 if (s->addr <= addr)
1782 break;
1783
1784 if (s == NULL)
1785 fprintf(cpu_unit.fileref, " %04x ", addr);
1786 else
1787 fprintf(cpu_unit.fileref, "%-5s+%04x ", s->sym, addr - s->addr);
1788 }
1789
1790 fprintf(cpu_unit.fileref, "%04x %04x %s%04x %04x %04x %c%c%c %s",
1791 ACC & 0xFFFF, EXT & 0xFFFF, fltstr, M[1] & 0xFFFF, M[2] & 0xFFFF, M[3] & 0xFFFF,
1792 C ? 'C' : ' ', V ? 'V' : ' ', (ipl < 0) ? ' ' : (ipl+'0'), facstr);
1793
1794 v[0] = M[ IAR & mem_mask];
1795 v[1] = M[(IAR+1) & mem_mask];
1796 fprint_sym(cpu_unit.fileref, IAR & mem_mask, v, NULL, SWMASK('M')); /* disassemble instruction */
1797
1798 fputs(CRLF, cpu_unit.fileref);
1799 }
1800
1801 void trace_io (char *fmt, ...)
1802 {
1803 va_list args;
1804
1805 if ((cpu_unit.flags & UNIT_ATT) == 0)
1806 return;
1807
1808 va_start(args, fmt); /* get pointer to argument list */
1809 vfprintf(cpu_unit.fileref, fmt, args); /* write errors to cpu log file */
1810 va_end(args);
1811
1812 fputs(CRLF, cpu_unit.fileref);
1813 }
1814
1815 void trace_both (char *fmt, ...)
1816 {
1817 va_list args;
1818
1819 if (cpu_unit.flags & UNIT_ATT) {
1820 va_start(args, fmt); /* get pointer to argument list */
1821 vfprintf(cpu_unit.fileref, fmt, args);
1822 va_end(args);
1823 fputs(CRLF, cpu_unit.fileref);
1824 }
1825
1826 va_start(args, fmt); /* get pointer to argument list */
1827 vfprintf(stdout, fmt, args);
1828 va_end(args);
1829 putchar('\n');
1830 }
1831
1832 /* debugging */
1833
1834 void debug_print (char *fmt, ...)
1835 {
1836 va_list args;
1837
1838 va_start(args, fmt);
1839 vprintf(fmt, args);
1840 if (cpu_unit.flags & UNIT_ATT)
1841 vfprintf(cpu_unit.fileref, fmt, args);
1842 va_end(args);
1843
1844 if (strchr(fmt, '\n') == NULL) { /* be sure to emit a newline */
1845 putchar('\n');
1846 if (cpu_unit.flags & UNIT_ATT)
1847 putc('\n', cpu_unit.fileref);
1848 }
1849 }
1850
1851 #ifdef _WIN32
1852 #include <windows.h>
1853 #endif
1854
1855 /* view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck) */
1856
1857 static t_stat view_cmd (int flag, char *cptr)
1858 {
1859 #ifdef _WIN32
1860 char cmdline[256];
1861
1862 sprintf(cmdline, "notepad %s", cptr);
1863 WinExec(cmdline, SW_SHOWNORMAL);
1864 #endif
1865 return SCPE_OK;
1866 }
1867
1868 /* web server version - hooks for CGI mode. These function pointer can be set by the CGI version's main() routine */
1869
1870 void (*cgi_start_hook)(void) = NULL; /* these can be defined by a CGI wrapper to do things on start and stop of simulation */
1871 void (*cgi_end_hook)(void) = NULL;
1872
1873 static void cgi_start (void)
1874 {
1875 if (cgi_start_hook != NULL)
1876 (*cgi_start_hook)();
1877 }
1878
1879 static void cgi_stop (t_stat reason)
1880 {
1881 if (cgi_end_hook != NULL)
1882 (*cgi_end_hook)();
1883 }