1 /* ibm1130_cpu.c: IBM 1130 CPU simulator
3 Based on the SIMH package written by Robert M Supnik
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
11 * This is not a supported product, but I welcome bug reports and fixes.
12 * Mail to simh@ibm1130.org
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
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
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.
39 The register state for the IBM 1130 CPU is:
41 IAR instruction address register
43 EXT accumulator extension
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
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.
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.
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
75 The IBM 1130 instruction formats are
77 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
78 | opcode | F| T | | general format
79 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
81 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
82 | opcode | 0| T | DISPLACEMENT | short instruction
83 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
85 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
86 | opcode | 1| T | I| MODIFIER | long instruction
87 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
89 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
93 F = format. 0 = short (1 word), 1 = long (2 word) instruction
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])
100 DISPLACEMENT = two's complement (must be sign-extended)
104 Note that IAR = instruction address+1 when instruction is being decoded.
106 In normal addressing mode, effective address (EA) is computed as follows:
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]
113 F = 1 T = 0 I = 0 EA = 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]]
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.
128 Adding I/O devices requires modifications to three modules:
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
135 /* ------------------------------------------------------------------------
137 * ------------------------------------------------------------------------ */
141 #include "ibm1130_defs.h"
143 #define save_ibkpt (cpu_unit.u3) /* will be SAVEd */
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 */
150 static void cgi_start(void);
151 static void cgi_stop(t_stat reason
);
152 static int simh_status_to_stopcode (int status
);
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
;
160 /* space to store extra simulator-specific commands */
161 #define MAX_EXTRA_COMMANDS 10
162 CTAB x_cmds
[MAX_EXTRA_COMMANDS
];
170 /* ------------------------------------------------------------------------
171 * initializers for globals
172 * ------------------------------------------------------------------------ */
174 #define SIGN_BIT(v) ((v) & 0x8000)
175 #define DWSIGN_BIT(v) ((v) & 0x80000000)
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 */
208 static int32 int_masks
[6] = {
209 0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E /* IPL 0 is highest prio (sees no other interrupts) */
212 /* ------------------------------------------------------------------------
213 * Function declarations
214 * ------------------------------------------------------------------------ */
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);
224 extern t_stat
ts_wr (int32 data
, int32 addr
, int32 access
);
225 extern t_stat
detach_cmd (int flags
, char *cptr
);
227 extern int32 sim_switches
;
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
);
235 #define archive_backtrace(inst)
236 #define reset_backtrace()
237 #define show_backtrace(ntrace)
241 # define ARFSET(v) ARF = (v) & 0xFFFF /* set Arithmetic Factor Register (used for display purposes only) */
243 # define ARFSET(v) /* without GUI, no need for setting ARF */
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);
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
261 * The CPU is attachable; attaching a file to it write a log of instructions
263 * ------------------------------------------------------------------------ */
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 */
268 UNIT cpu_unit
= { UDATA (&cpu_svc
, UNIT_FIX
| UNIT_BINK
| UNIT_ATTABLE
| UNIT_SEQ
, INIMEMSIZE
) };
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) },
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
},
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
},
300 { HRDATA (ARF
, ARF
, 32) },
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
},
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 */
321 /* ------------------------------------------------------------------------
322 * Memory read/write -- save SAR and SBR on the way in and out
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 * ------------------------------------------------------------------------ */
331 int32
ReadW (int32 a
)
334 SBR
= (int32
) M
[(a
) & mem_mask
];
338 void WriteW (int32 a
, int32 d
)
342 M
[a
& mem_mask
] = (int16
) d
;
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 * ------------------------------------------------------------------------ */
350 static uint16
ReadIndex (int32 tag
)
352 #ifdef ENABLE_1800_SUPPORT
354 return XR
[tag
-1]; /* 1800: fetch from register */
357 SAR
= tag
; /* 1130: ordinary read from memory (like ReadW) */
358 SBR
= (int32
) M
[(tag
) & mem_mask
];
362 static void WriteIndex (int32 tag
, int32 d
)
364 #ifdef ENABLE_1800_SUPPORT
366 XR
[tag
-1] = d
; /* 1800: store in register */
371 SAR
= tag
; /* 1130: ordinary write to memory (same as WriteW) */
373 M
[tag
& mem_mask
] = (int16
) d
;
376 /* ------------------------------------------------------------------------
377 * upcase - force a string to uppercase (ASCII)
378 * ------------------------------------------------------------------------ */
380 char *upcase (char *str
)
384 for (s
= str
; *s
; s
++) {
385 if (*s
>= 'a' && *s
<= 'z')
392 /* ------------------------------------------------------------------------
393 * calc_ints - set appropriate bits in int_req if any interrupts are pending on given levels
398 * \ \ \ \ \ interrupt level 5 pending (lowest priority)
400 * interrupt level 0 pending (highest priority)
402 * int_mask is set according to current interrupt level (ipl)
404 * 0 0 0 0 0 0 ipl = 0 (currently servicing highest priority interrupt)
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 * ------------------------------------------------------------------------ */
413 void calc_ints (void)
416 register int32 newbits
= 0;
418 GUI_BEGIN_CRITICAL_SECTION
/* using critical section here so we don't mislead the GUI thread */
420 for (i
= 6; --i
>= 0; ) {
427 int_lamps
|= int_req
;
428 int_mask
= (ipl
< 0) ? 0xFFFF : int_masks
[ipl
]; /* be sure this is set correctly */
430 GUI_END_CRITICAL_SECTION
433 /* ------------------------------------------------------------------------
434 * instruction processor
435 * ------------------------------------------------------------------------ */
437 #define INCREMENT_IAR IAR = (IAR + 1) & mem_mask
438 #define DECREMENT_IAR IAR = (IAR - 1) & mem_mask
440 void bail (char *msg
)
446 static void weirdop (char *msg
, int offset
)
448 printf("Weird opcode: %s at %04x\n", msg
, IAR
+offset
);
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?"
462 static char *xio_funcs
[] = {
463 "0?", "write", "read", "sense_irq",
464 "control", "initw", "initr", "sense"
467 t_stat
sim_instr (void)
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
;
474 int cwincount
= 0, status
;
475 static long ninstr
= 0;
476 static char *intlabel
[] = {"INT0","INT1","INT2","INT3","INT4","INT5"};
478 if (cgi
) /* give CGI hook function a chance to do something */
481 if (running
) /* this is definitely not reentrant */
484 if (! power
) /* this matters only to the GUI */
485 return STOP_POWER_OFF
;
489 mem_mask
= MEMSIZE
- 1; /* set other useful variables */
492 /* Main instruction fetch/decode loop */
495 wait_lamp
= 0; /* release lock on wait lamp */
502 while (reason
== 0) {
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));
514 #endif /* ifdef UPDATE_INTERVAL */
515 #endif /* ifndef UPDATE_BY_TIMER */
516 #endif /* ifdef GUI_SUPPORT */
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
);
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
))
533 if (i
>= 6) { /* nothing to do? */
534 calc_ints(); /* weird. recalculate */
535 continue; /* back to fetch */
538 GUI_BEGIN_CRITICAL_SECTION
540 if (ipl
>= 0) /* save previous IPL in bit stack */
541 iplpending
|= (0x20 >> ipl
);
543 ipl
= i
; /* set new interrupt level */
544 int_mask
= int_masks
[i
]; /* set appropriate mask */
546 GUI_END_CRITICAL_SECTION
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 */
556 if (wait_state
) { /* waiting? */
557 sim_interval
= 0; /* run the clock out */
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
);
564 else { /* CPU is not expecting a keystroke (keyboard interrupt) */
565 if (wait_state
== WAIT_OP
)
566 reason
= STOP_WAIT
; /* end the simulation */
568 reason
= STOP_INVALID_INSTR
;
572 if (gdu_active()) /* but don't stop simulator if 2250 GDU is running */
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 */
588 if (cpu_unit
.flags
& UNIT_ATT
)
589 trace_instruction(); /* log CPU details if logging is enabled */
591 prev_IAR
= IAR
; /* save IAR before incrementing it */
593 IR
= ReadW(IAR
); /* fetch 1st word of instruction */
595 sim_interval
= sim_interval
- 1; /* this constitutes one tick of the simulation clock */
597 OP
= (IR
>> 11) & 0x1F; /* opcode */
598 F
= IR
& 0x0400; /* format bit: 1 = long instr */
599 TAG
= IR
& 0x0300; /* tag bits: index reg x */
603 /* here I compute the usual effective address on the assumption that the instruction will need it. Some don't. */
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 */
609 DSPLC
|= ~ 0x7F; /* sign extend */
611 word2
= ReadW(IAR
); /* get reference address */
612 INCREMENT_IAR
; /* bump the instruction address register */
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 */
620 else { /* short instruction, use displacement */
621 INDIR
= 0; /* never indirect */
622 DSPLC
= IR
& 0x00FF; /* get displacement */
626 if (TAG
) /* if indexed */
627 eaddr
= ReadIndex(TAG
) + DSPLC
; /* add index register value */
629 eaddr
= IAR
+ DSPLC
; /* otherwise relative to IAR after fetch */
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 */
637 iocc_dev
= (iocc_op
>> 11) & 0x001F;
638 iocc_func
= (iocc_op
>> 8) & 0x0007;
639 iocc_mod
= iocc_op
& 0x00FF;
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
);
644 /* fprintf(stderr, "* XIO %s %s mod %02x addr %04x\n", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr); */
646 ACC
= 0; /* ACC is destroyed, and default XIO_SENSE_DEV result is 0 */
650 sprintf(msg
, "Unknown op %x on device %02x", iocc_func
, iocc_dev
);
654 case XIO_SENSE_IRQ
: /* examine current Interrupt Level Status Word */
655 ACC
= (ipl
>= 0) ? ILSW
[ipl
] : 0;
658 default: /* perform device-specific operation */
660 case 0x01: /* console keyboard and printer */
661 xio_1131_console(iocc_addr
, iocc_func
, iocc_mod
);
663 case 0x02: /* 1142 card reader/punch */
664 xio_1142_card(iocc_addr
, iocc_func
, iocc_mod
);
666 case 0x03: /* 1134 paper tape reader/punch */
667 xio_1134_papertape(iocc_addr
, iocc_func
, iocc_mod
);
669 case 0x04: /* CPU disk storage */
670 xio_disk(iocc_addr
, iocc_func
, iocc_mod
, 0);
672 case 0x05: /* 1627 plotter */
673 xio_1627_plotter(iocc_addr
, iocc_func
, iocc_mod
);
675 case 0x06: /* 1132 Printer */
676 xio_1132_printer(iocc_addr
, iocc_func
, iocc_mod
);
678 case 0x07: /* console switches, stop key, run mode */
679 xio_1131_switches(iocc_addr
, iocc_func
, iocc_mod
);
681 case 0x08: /* 1231 optical mark reader */
682 xio_1231_optical(iocc_addr
, iocc_func
, iocc_mod
);
684 case 0x09: /* 2501 card reader */
685 xio_2501_card(iocc_addr
, iocc_func
, iocc_mod
);
687 case 0x0a: /* synchronous comm adapter */
688 xio_sca(iocc_addr
, iocc_func
, iocc_mod
);
690 case 0x0c: /* IBM System/7 interprocessor link */
691 xio_system7(iocc_addr
, iocc_func
, iocc_mod
);
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);
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);
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);
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);
705 case 0x15: /* 1403 Printer */
706 xio_1403_printer(iocc_addr
, iocc_func
, iocc_mod
);
708 case 0x16: /* 2311 Disk Storage Drive. Drive 1, Disk 5 */
709 xio_disk(iocc_addr
, iocc_func
, iocc_mod
, -1);
711 case 0x17: /* 2311 Disk Storage Drive, Drive 2, Disk 1 through 5 */
712 xio_disk(iocc_addr
, iocc_func
, iocc_mod
, -1);
714 case 0x19: /* 2250 Display Unit */
715 xio_2250_display(iocc_addr
, iocc_func
, iocc_mod
);
717 case 0x1a: /* 2741 Attachment (nonstandard serial interface used by APL\1130 */
718 xio_t2741_terminal(iocc_addr
, iocc_func
, iocc_mod
);
721 sprintf(msg
, "unknown device %02x", iocc_dev
);
727 calc_ints(); /* after every XIO, reset int_mask just in case */
730 case 0x02: /* --- SLA,SLT,SLC,SLCA,NOP - Shift Left family --- */
732 weirdop("Long Left Shift", -2);
736 CCC
= ((TAG
== 0) ? DSPLC
: ReadIndex(TAG
)) & 0x003F;
739 break; /* shift of zero is a NOP */
741 switch (IR
& 0x00C0) {
742 case 0x0040: /* SLCA */
744 while (CCC
> 0 && (ACC
& 0x8000) == 0) {
749 WriteIndex(TAG
, (ReadIndex(TAG
) & 0xFF00) | CCC
); /* put low 6 bits back into index register and zero bits 8 and 9 */
752 /* if TAG == 0, fall through and treat like normal shift SLA */
754 case 0x0000: /* SLA */
757 ACC
= (ACC
<< 1) & 0xFFFF;
762 case 0x00C0: /* SLC */
764 while (CCC
> 0 && (ACC
& 0x8000) == 0) {
765 abit
= (EXT
& 0x8000) >> 15;
766 ACC
= ((ACC
<< 1) & 0xFFFF) | abit
;
771 WriteIndex(TAG
, ReadIndex(TAG
) & 0xFF00 | CCC
); /* put 6 bits back into low byte of index register */
774 /* if TAG == 0, fall through and treat like normal shift SLT */
776 case 0x0080: /* SLT */
779 abit
= (EXT
& 0x8000) >> 15;
780 ACC
= ((ACC
<< 1) & 0xFFFF) | abit
;
781 EXT
= (EXT
<< 1) & 0xFFFF;
787 bail("SLA switch, can't happen");
792 case 0x03: /* --- SRA, SRT, RTE - Shift Right family --- */
794 weirdop("Long Right Shift", -2);
798 CCC
= ((TAG
== 0) ? DSPLC
: ReadIndex(TAG
)) & 0x3F;
803 switch (IR
& 0x00C0) {
804 case 0x0000: /* SRA */
805 ACC
= (CCC
< 16) ? ((ACC
& 0xFFFF) >> CCC
) : 0;
809 case 0x0040: /* invalid */
810 wait_state
= WAIT_INVALID_OP
;
813 case 0x0080: /* SRT */
815 xbit
= (ACC
& 0x0001) << 15;
816 abit
= (ACC
& 0x8000);
817 ACC
= (ACC
>> 1) & 0x7FFF | abit
;
818 EXT
= (EXT
>> 1) & 0x7FFF | xbit
;
823 case 0x00C0: /* RTE */
825 abit
= (EXT
& 0x0001) << 15;
826 xbit
= (ACC
& 0x0001) << 15;
827 ACC
= (ACC
>> 1) & 0x7FFF | abit
;
828 EXT
= (EXT
>> 1) & 0x7FFF | xbit
;
834 bail("SRA switch, can't happen");
839 case 0x04: /* --- LDS - Load Status --- */
840 if (F
) { /* never fetches second word? */
841 weirdop("Long LDS", -2);
846 C
= (DSPLC
& 2) >> 1;
849 case 0x05: /* --- STS - Store Status --- */
850 newval
= ReadW(eaddr
) & 0xFF00;
856 WriteW(eaddr
, newval
);
857 C
= V
= 0; /* clear flags after storing */
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? */
868 case 0x08: /* --- BSI - Branch and store IAR --- */
870 if (bsctest(IR
, F
)) /* do standard BSC long format testing */
871 break; /* if any condition is true, do nothing */
873 WriteW(eaddr
, IAR
); /* do subroutine call */
874 archive_backtrace("BSI"); /* save info in back-trace buffer */
875 IAR
= (eaddr
+ 1) & mem_mask
;
878 case 0x09: /* --- BSC - Branch and skip on Condition --- */
880 if (bsctest(IR
, F
)) /* long format; any indicator cancels branch */
883 archive_backtrace((DSPLC
& 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */
884 IAR
= eaddr
; /* no indicator means branch taken */
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 */
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. */
896 if (DSPLC
& 0x40) { /* BOSC = exit from interrupt handler */
902 case 0x0c: /* --- LDX - Load Index --- */
904 eaddr
= (INDIR
) ? ReadW(word2
) : word2
;
909 WriteIndex(TAG
, eaddr
);
911 archive_backtrace("LDX"); /* save info in back-trace buffer */
912 IAR
= eaddr
; /* what happens in short form? can onlyjump to low addresses? */
916 case 0x0d: /* --- STX - Store Index --- */
917 if (F
) { /* compute EA without any indexing */
918 eaddr
= (INDIR
) ? ReadW(word2
) : word2
;
923 WriteW(eaddr
, TAG
? ReadIndex(TAG
) : IAR
);
926 case 0x0e: /* --- MDX - Modify Index and Skip --- */
927 if (F
) { /* long format: adjust memory location */
929 oldval
= ReadIndex(TAG
); /* add word2 to index */
930 newval
= oldval
+ (INDIR
? ReadW(word2
) : word2
);
931 WriteIndex(TAG
, newval
);
934 oldval
= ReadW(word2
);
935 DSPLC
= IR
& 0x00FF; /* use extended displacement (no INDIR bit, it's is part of displacement in this op) */
938 newval
= oldval
+ DSPLC
; /* add modifier to @word2 */
939 WriteW(word2
, newval
);
942 else { /* short format: adust IAR or index */
944 oldval
= ReadIndex(TAG
); /* add displacement to index */
945 newval
= oldval
+ DSPLC
;
946 WriteIndex(TAG
, newval
);
949 oldval
= IAR
; /* add displacement to IAR */
950 newval
= IAR
+ DSPLC
;
951 archive_backtrace("MDX");
952 IAR
= newval
& mem_mask
;
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 */
962 case 0x10: /* --- A - Add --- */
963 /* in adds and subtracts, carry is set or cleared, overflow is set only */
967 ACC
= (ACC
+ src
) & 0xFFFF;
971 V
= SIGN_BIT((~src
^ src2
) & (src
^ ACC
));
974 case 0x11: /* --- AD - Add Double --- */
975 src
= ((ACC
<< 16) | (EXT
& 0xFFFF));
977 src2
= (ReadW(eaddr
) << 16) + ReadW(eaddr
|1);
979 ACC
= (dst
>> 16) & 0xFFFF;
982 C
= (uint32
) dst
< (uint32
) src
;
984 V
= DWSIGN_BIT((~src
^ src2
) & (src
^ dst
));
987 case 0x12: /* --- S - Subtract --- */
991 ACC
= (ACC
-src2
) & 0xFFFF;
995 V
= SIGN_BIT((src
^ src2
) & (src
^ ACC
));
998 case 0x13: /* --- SD - Subtract Double --- */
999 src
= ((ACC
<< 16) | (EXT
& 0xFFFF));
1001 src2
= (ReadW(eaddr
) << 16) + ReadW(eaddr
|1);
1003 ACC
= (dst
>> 16) & 0xFFFF;
1006 C
= (uint32
) src
< (uint32
) src2
;
1008 V
= DWSIGN_BIT((src
^ src2
) & (src
^ dst
));
1011 case 0x14: /* --- M - Multiply --- */
1012 if ((src
= ACC
& 0xFFFF) & 0x8000) /* sign extend the values */
1014 if ((src2
= ReadW(eaddr
)) & 0x8000)
1019 ACC
= (dst
>> 16) & 0xFFFF; /* split the results */
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 */
1031 V
= 1; /* divide by zero just sets overflow, ACC & EXT are undefined */
1033 ACC
= (src
/ src2
) & 0xFFFF;
1034 EXT
= (src
% src2
) & 0xFFFF;
1038 case 0x18: /* --- LD - Load ACC --- */
1042 case 0x19: /* --- LDD - Load Double --- */
1044 EXT
= ReadW(eaddr
|1); /* notice address is |1 not +1 */
1047 case 0x1a: /* --- STO - Store ACC --- */
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 */
1056 case 0x1c: /* --- AND - Logical AND --- */
1062 case 0x1d: /* --- OR - Logical OR --- */
1068 case 0x1e: /* --- EOR - Logical Excl OR --- */
1076 #ifdef ENABLE_1800_SUPPORT
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;
1084 if (dst
& 0x8000) /* if ACC < operand, skip 1 instruction */
1086 else if ((dst
& 0xFFFF) == 0) /* if ACC == operand, skip 2 instructions */
1089 else { /* --- DCMP - Compare Double --- */
1090 src
= ((ACC
<< 16) | (EXT
& 0xFFFF));
1091 src2
= (ReadW(eaddr
) << 16) + ReadW(eaddr
|1);
1093 C
= (uint32
) src
< (uint32
) src2
;
1095 if (dst
& 0x80000000) /* if ACC_EXT < operand, skip 1 instruction */
1097 else if (dst
== 0) /* if ACC_EXT == operand, skip 2 instructions */
1101 break; /* these are legal instructions on the 1800 */
1104 /* 1130: these are not legal instructions, fall through */
1107 /* all invalid instructions act like waits */
1115 wait_state
= WAIT_INVALID_OP
;
1117 DECREMENT_IAR
; /* assume it wouldn't have fetched 2nd word? */
1120 } /* end instruction decode switch */
1122 if (RUNMODE
!= MODE_RUN
&& RUNMODE
!= MODE_INT_RUN
)
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
1132 } /* end main loop */
1139 int_lamps
= 0; /* display only currently active interrupts while halted */
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 */
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
);
1150 if (cgi
) /* give CGI hook function a chance to do something */
1157 * simh_status_to_stopcode - convert a SCPE_xxx value from sim_process_event into a STOP_xxx code
1160 static int simh_status_to_stopcode (int status
)
1162 return (status
== SCPE_BREAK
) ? STOP_BREAK
:
1163 (status
== SCPE_STOP
) ? STOP_IMMEDIATE
:
1164 (status
== SCPE_STEP
) ? STOP_STEP
: STOP_OTHER
;
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 * ------------------------------------------------------------------------ */
1173 static t_bool
bsctest (int32 DSPLC
, t_bool reset_V
)
1175 if (DSPLC
& 0x01) { /* Overflow off (note inverted sense) */
1178 else if (reset_V
) /* reset after testing */
1182 if (DSPLC
& 0x02) { /* Carry off (note inverted sense) */
1187 if (DSPLC
& 0x04) /* Even */
1191 if (DSPLC
& 0x08) /* Positive */
1192 if ((ACC
& 0x8000) == 0 && ACC
!= 0)
1195 if (DSPLC
& 0x10) /* Negative */
1199 if (DSPLC
& 0x20) /* Zero */
1200 if ((ACC
& 0xFFFF) == 0)
1206 /* ------------------------------------------------------------------------
1207 * exit_irq - pop interrupt stack as part of return from subroutine (BOSC)
1208 * ------------------------------------------------------------------------ */
1210 static void exit_irq (void)
1214 GUI_BEGIN_CRITICAL_SECTION
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
);
1222 ipl
= -1; /* default: return to main processor level */
1225 if (iplpending
) { /* restore previous interrupt status */
1226 for (i
= 0, bit
= 0x20; i
< 6; i
++, bit
>>= 1) {
1227 if (iplpending
& bit
) {
1230 int_mask
= int_masks
[i
];
1235 GUI_END_CRITICAL_SECTION
1237 calc_ints(); /* recompute pending interrupt mask */
1238 } /* because we probably cleared some ILSW bits before this instruction */
1240 /* let a device halt the simulation */
1242 void break_simulation (t_stat stopreason
)
1244 reason
= stopreason
;
1247 /* ------------------------------------------------------------------------
1248 * SIMH required routines
1249 * ------------------------------------------------------------------------ */
1251 /* ------------------------------------------------------------------------
1253 * ------------------------------------------------------------------------ */
1255 t_stat
cpu_reset (DEVICE
*dptr
)
1257 wait_state
= 0; /* cancel wait */
1258 wait_lamp
= TRUE
; /* but keep the wait lamp lit on the GUI */
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
);
1265 GUI_BEGIN_CRITICAL_SECTION
1267 CLRBIT(cpu_dsw
, CPU_DSW_PROGRAM_STOP
|CPU_DSW_INT_RUN
);
1268 CLRBIT(ILSW
[5], ILSW_5_INT_RUN_PROGRAM_STOP
);
1274 int_req
= 0; /* hmmm, it SHOULD reset the int req, right? */
1277 memset(ILSW
, 0, sizeof(ILSW
));
1279 cpu_dsw
= 0; /* clear int req and prot stop bits */
1280 tbit
= 0; /* cancel INT_RUN mode */
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;
1286 mem_mask
= MEMSIZE
- 1; /* wraparound mask */
1288 GUI_END_CRITICAL_SECTION
1290 return cpu_svc(&cpu_unit
); /* reset breakpoint */
1293 /* ------------------------------------------------------------------------
1295 * ------------------------------------------------------------------------ */
1297 t_stat
cpu_ex (t_value
*vptr
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1299 if (vptr
== NULL
) return SCPE_ARG
;
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;
1310 /* ------------------------------------------------------------------------
1312 * ------------------------------------------------------------------------ */
1314 t_stat
cpu_dep (t_value val
, t_addr addr
, UNIT
*uptr
, int32 sw
)
1316 if (addr
< MEMSIZE
) {
1317 M
[addr
] = (uint16
) (val
& 0xFFFF);
1323 /* ------------------------------------------------------------------------
1324 * Breakpoint service
1325 * ------------------------------------------------------------------------ */
1327 t_stat
cpu_svc (UNIT
*uptr
)
1329 if ((ibkpt_addr
& ~ILL_ADR_FLAG
) == save_ibkpt
)
1330 ibkpt_addr
= save_ibkpt
;
1336 /* ------------------------------------------------------------------------
1338 * ------------------------------------------------------------------------ */
1340 t_stat
cpu_set_size (UNIT
*uptr
, int32 value
, char *cptr
, void *desc
)
1345 if ((value
<= 0) || (value
> MAXMEMSIZE
) || ((value
& 0xFFF) != 0))
1348 for (i
= value
, used
= FALSE
; i
< (int32
) MEMSIZE
; i
++) {
1355 if (used
&& ! get_yn ("Really truncate memory [N]?", FALSE
))
1358 for (i
= MEMSIZE
; i
< value
; i
++) /* clear expanded area */
1362 mem_mask
= MEMSIZE
- 1;
1367 /* processor type */
1369 t_stat
cpu_set_type (UNIT
*uptr
, int32 value
, char *cptr
, void *desc
)
1373 is_1800
= (value
& UNIT_1800
) != 0; /* set is_1800 mode flag */
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
);
1380 SETBIT(r
->flags
, REG_HIDDEN
|REG_RO
);
1387 /* ------------------------------------------------------------------------
1388 * IO function for console switches
1389 * ------------------------------------------------------------------------ */
1391 void xio_1131_switches (int32 addr
, int32 func
, int32 modify
)
1405 sprintf(msg
, "Invalid console switch function %x", func
);
1410 /* ------------------------------------------------------------------------
1411 * Illegal IO operation. Not yet sure what the actual CPU does in this case
1412 * ------------------------------------------------------------------------ */
1414 void xio_error (char *msg
)
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
);
1421 /* ------------------------------------------------------------------------
1422 * register_cmd - add a command to the extensible command table
1423 * ------------------------------------------------------------------------ */
1425 t_stat
register_cmd (char *name
, t_stat (*action
)(int32 flag
, char *ptr
), int arg
, char *help
)
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
)
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");
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
;
1447 x_cmds
[i
].action
= NULL
; /* move the NULL terminator */
1448 x_cmds
[i
].name
= NULL
;
1453 #ifdef USE_MY_ECHO_CMD
1454 /* ------------------------------------------------------------------------
1455 * echo_cmd - just echo the command line
1456 * ------------------------------------------------------------------------ */
1458 static t_stat
echo_cmd (int flag
, char *cptr
)
1460 printf("%s\n", cptr
);
1465 /* ------------------------------------------------------------------------
1466 * sim_init - initialize simulator upon startup of scp, before reset
1467 * ------------------------------------------------------------------------ */
1469 void sim_init (void)
1471 sim_gui
= ! (sim_switches
& SWMASK('G')); /* -g means no GUI */
1473 sim_vm_cmd
= x_cmds
; /* provide list of additional commands */
1476 /* set hook routines for GUI command processing */
1478 sim_vm_read
= &read_cmdline
;
1479 sim_vm_post
= &update_gui
;
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");
1488 register_cmd("VIEW", &view_cmd
, 0, "v{iew} filename view a text file with notepad\n");
1490 #ifdef USE_MY_ECHO_CMD
1491 register_cmd("ECHO", &echo_cmd
, 0, "echo args... echo arguments passed to command\n");
1495 /* ------------------------------------------------------------------------
1496 * archive_backtrace - record a jump, skip, branch or whatever
1497 * ------------------------------------------------------------------------ */
1499 #ifdef ENABLE_BACKTRACE
1501 #define MAXARCHIVE 16
1503 static struct tag_arch
{
1507 int narchived
= 0, archind
= 0;
1509 static void archive_backtrace (char *inst
)
1513 if (narchived
< MAXARCHIVE
)
1516 if (narchived
> 0 && arch
[prevind
].iar
== prev_IAR
)
1519 arch
[archind
].iar
= prev_IAR
;
1520 arch
[archind
].inst
= inst
;
1523 archind
= (archind
+1) % MAXARCHIVE
;
1526 static void reset_backtrace (void)
1532 void void_backtrace (int afrom
, int ato
)
1539 for (i
= 0; i
< narchived
; i
++)
1540 if (arch
[i
].iar
>= afrom
&& arch
[i
].iar
<= ato
)
1541 arch
[i
].inst
= "OVERWRITTEN";
1544 static void show_backtrace (int nshow
)
1546 int n
= narchived
, i
= archind
;
1548 if (n
> nshow
) n
= nshow
;
1551 i
= (i
> 0) ? (i
-1) : (MAXARCHIVE
-1);
1552 printf("from %04x (%s) ", arch
[i
].iar
, arch
[i
].inst
);
1559 static t_stat
backtrace_cmd (int flag
, char *cptr
)
1563 if ((n
= atoi(cptr
)) <= 0)
1571 /* stub this for the disk routine */
1573 void void_backtrace (int afrom
, int ato
)
1579 /*************************************************************************************
1580 * CPU log routines -- attaching a file to the CPU creates a trace of instructions and register values
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
1589 * mapfile if specified is a list of symbols and addresses of the form:
1600 * These values are easily obtained from a load map created by
1603 * The log output is of the form
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
1617 * IAR - instruction address register value, optionally including symbol and offset
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
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
1629 * The register values shown are the values BEFORE the instruction is executed.
1630 *************************************************************************************/
1632 t_stat
fprint_sym (FILE *of
, t_addr addr
, t_value
*val
, UNIT
*uptr
, int32 sw
);
1634 typedef struct tag_symentry
{
1635 struct tag_symentry
*next
;
1638 } SYMENTRY
, *PSYMENTRY
;
1640 static PSYMENTRY syms
= NULL
;
1641 static t_bool new_log
, log_fac
;
1643 static t_stat
cpu_attach (UNIT
*uptr
, char *cptr
)
1645 char mapfile
[200], buf
[200], sym
[100];
1647 PSYMENTRY n
, prv
, s
;
1650 remove(cptr
); /* delete old log file, if present */
1652 log_fac
= sim_switches
& SWMASK ('F'); /* display the FAC and the ACC/EXT as fixed point. */
1654 for (s
= syms
; s
!= NULL
; s
= n
) { /* free any old map entries */
1660 if (sim_switches
& SWMASK('M')) { /* use a map file to display relative addresses */
1661 cptr
= get_glyph(cptr
, mapfile
, 0);
1663 printf("/m must be followed by a filename\n");
1666 if ((fd
= fopen(mapfile
, "r")) == NULL
) {
1668 return SCPE_OPENERR
;
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)
1677 for (prv
= NULL
, s
= syms
; s
!= NULL
; prv
= s
, s
= s
->next
) {
1682 if ((n
= malloc(sizeof(SYMENTRY
))) == NULL
) {
1683 printf("out of memory reading map!\n");
1688 strcpy(n
->sym
, sym
);
1697 n
->next
= prv
->next
;
1704 return attach_unit(uptr
, quotefix(cptr
)); /* fix quotes in filenames & attach */
1707 static void trace_instruction (void)
1715 char facstr
[20], fltstr
[20];
1717 if ((cpu_unit
.flags
& UNIT_ATT
) == 0)
1721 fseek(cpu_unit
.fileref
, 0, SEEK_END
);
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
? "------------- " : "");
1731 facstr
[0] = fltstr
[0] = '\0';
1733 mant
= ((ACC
& 0xFFFF) << 16) | (EXT
& 0xFFFF);
1734 if (mant
== 0x80000000) {
1739 if ((sign
= mant
& 0x80000000) != 0)
1741 fac
= (float) mant
* ((float) 1./ (float) (unsigned long) 0x80000000);
1743 sprintf(fltstr
, "%c%.5f ", sign
? '-' : ' ', fac
);
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;
1751 fac
= (float) mant
* ((float) 1. / (float) 0x00800000);
1754 fac
*= (float) (1 << 30);
1760 fac
*= (float) (1 << exp
);
1761 else if (exp
< -30) {
1762 fac
/= (float) (1 << 30);
1768 fac
/= (float) (1 << -exp
);
1770 sprintf(facstr
, "%c%.5e ", sign
? '-' : ' ', fac
);
1773 strcpy(facstr
, " ");
1776 addr
= IAR
& 0xFFFF;
1777 fprintf(cpu_unit
.fileref
, "%04x ", addr
);
1780 for (s
= syms
; s
!= NULL
; s
= s
->next
)
1781 if (s
->addr
<= addr
)
1785 fprintf(cpu_unit
.fileref
, " %04x ", addr
);
1787 fprintf(cpu_unit
.fileref
, "%-5s+%04x ", s
->sym
, addr
- s
->addr
);
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
);
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 */
1798 fputs(CRLF
, cpu_unit
.fileref
);
1801 void trace_io (char *fmt
, ...)
1805 if ((cpu_unit
.flags
& UNIT_ATT
) == 0)
1808 va_start(args
, fmt
); /* get pointer to argument list */
1809 vfprintf(cpu_unit
.fileref
, fmt
, args
); /* write errors to cpu log file */
1812 fputs(CRLF
, cpu_unit
.fileref
);
1815 void trace_both (char *fmt
, ...)
1819 if (cpu_unit
.flags
& UNIT_ATT
) {
1820 va_start(args
, fmt
); /* get pointer to argument list */
1821 vfprintf(cpu_unit
.fileref
, fmt
, args
);
1823 fputs(CRLF
, cpu_unit
.fileref
);
1826 va_start(args
, fmt
); /* get pointer to argument list */
1827 vfprintf(stdout
, fmt
, args
);
1834 void debug_print (char *fmt
, ...)
1838 va_start(args
, fmt
);
1840 if (cpu_unit
.flags
& UNIT_ATT
)
1841 vfprintf(cpu_unit
.fileref
, fmt
, args
);
1844 if (strchr(fmt
, '\n') == NULL
) { /* be sure to emit a newline */
1846 if (cpu_unit
.flags
& UNIT_ATT
)
1847 putc('\n', cpu_unit
.fileref
);
1852 #include <windows.h>
1855 /* view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck) */
1857 static t_stat
view_cmd (int flag
, char *cptr
)
1862 sprintf(cmdline
, "notepad %s", cptr
);
1863 WinExec(cmdline
, SW_SHOWNORMAL
);
1868 /* web server version - hooks for CGI mode. These function pointer can be set by the CGI version's main() routine */
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
;
1873 static void cgi_start (void)
1875 if (cgi_start_hook
!= NULL
)
1876 (*cgi_start_hook
)();
1879 static void cgi_stop (t_stat reason
)
1881 if (cgi_end_hook
!= NULL
)