First Commit of my working state
[simh.git] / scp.c
1 /* scp.c: simulator control program
2
3 Copyright (c) 1993-2008, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 31-Mar-08 RMS Fixed bug in local/global register search (found by Mark Pizzolato)
27 Fixed bug in restore of RO units (from Mark Pizzolato)
28 06-Feb-08 RMS Added SET/SHO/NO BR with default argument
29 18-Jul-07 RMS Modified match_ext for VMS ext;version support
30 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all
31 Fixed bug in get_sim_opt
32 Fixed bug in restoration with changed memory size
33 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing
34 30-Jan-07 RMS Fixed bugs in get_ipaddr
35 17-Oct-06 RMS Added idle support
36 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q
37 14-Jul-06 RMS Added sim_activate_abs
38 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure
39 Added -E switch to do_cmd to exit on any error
40 14-Feb-06 RMS Upgraded save file format to V3.5
41 18-Jan-06 RMS Added fprint_stopped_gen
42 Added breakpoint spaces
43 Fixed unaligned register access (found by Doug Carman)
44 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)
45 30-Aug-05 RMS Revised to trim trailing spaces on file names
46 25-Aug-05 RMS Added variable default device support
47 23-Aug-05 RMS Added Linux line history support
48 16-Aug-05 RMS Fixed C++ declaration and cast problems
49 01-May-05 RMS Revised syntax for SET DEBUG (from Dave Bryan)
50 22-Mar-05 JDB Modified DO command to allow ten-level nesting
51 18-Mar-05 RMS Moved DETACH tests into detach_unit (from Dave Bryan)
52 Revised interface to fprint_sym, fparse_sym
53 07-Feb-05 RMS Added ASSERT command (from Dave Bryan)
54 02-Feb-05 RMS Fixed bug in global register search
55 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST
56 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file
57 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy
58 Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (from Dave Bryan)
59 Revised to flush output files after simulation stop (from Dave Bryan)
60 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions
61 27-Sep-04 RMS Fixed comma-separation options in set (from David Bryan)
62 09-Sep-04 RMS Added -p option for RESET
63 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST
64 17-Jul-04 RMS Added ECHO command (from Dave Bryan)
65 12-Jul-04 RMS Fixed problem ATTACHing to read only files
66 (found by John Dundas)
67 28-May-04 RMS Added SET/SHOW CONSOLE
68 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2)
69 RMS Added debug print routines (from Dave Hittner)
70 RMS Added sim_vm_parse_addr and sim_vm_fprint_addr
71 RMS Added REG_VMAD support
72 RMS Split out libraries
73 RMS Moved logging function to SCP
74 RMS Exposed step counter interface(s)
75 RMS Fixed double logging of SHOW BREAK (found by Mark Pizzolato)
76 RMS Fixed implementation of REG_VMIO
77 RMS Added SET/SHOW DEBUG, SET/SHOW <device> DEBUG,
78 SHOW <device> MODIFIERS, SHOW <device> RADIX
79 RMS Changed sim_fsize to take uptr argument
80 29-Dec-03 RMS Added Telnet console output stall support
81 01-Nov-03 RMS Cleaned up implicit detach on attach/restore
82 Fixed bug in command line read while logging (found by Mark Pizzolato)
83 01-Sep-03 RMS Fixed end-of-file problem in dep, idep
84 Fixed error on trailing spaces in dep, idep
85 15-Jul-03 RMS Removed unnecessary test in reset_all
86 15-Jun-03 RMS Added register flag REG_VMIO
87 25-Apr-03 RMS Added extended address support (V3.0)
88 Fixed bug in SAVE (found by Peter Schorn)
89 Added u5, u6 fields
90 Added logical name support
91 03-Mar-03 RMS Added sim_fsize
92 27-Feb-03 RMS Fixed bug in multiword deposits to files
93 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char*
94 Added multiple actions, .ini file support
95 Added multiple switch evaluations per line
96 07-Feb-03 RMS Added VMS support for ! (from Mark Pizzolato)
97 01-Feb-03 RMS Added breakpoint table extension, actions
98 14-Jan-03 RMS Added missing function prototypes
99 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support,
100 case sensitive SET options
101 22-Dec-02 RMS Added ! (OS command) feature (from Mark Pizzolato)
102 17-Dec-02 RMS Added get_ipaddr
103 02-Dec-02 RMS Added EValuate command
104 16-Nov-02 RMS Fixed bug in register name match algorithm
105 13-Oct-02 RMS Fixed Borland compiler warnings (found by Hans Pufal)
106 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (found by David Hittner)
107 Added support for fixed buffer devices
108 Added support for Telnet console, removed VT support
109 Added help <command>
110 Added VMS file optimizations (from Robert Alan Byer)
111 Added quiet mode, DO with parameters, GUI interface,
112 extensible commands (from Brian Knittel)
113 Added device enable/disable commands
114 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from Brian Knittel)
115 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by
116 Norm Lastovic)
117 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG
118 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added
119 magtape record length error (found by Jonathan Engdahl)
120 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval
121 (found by Brian Knittel)
122 10-Feb-02 RMS Fixed problem in clock calibration
123 06-Jan-02 RMS Moved device enable/disable to simulators
124 30-Dec-01 RMS Generalized timer packaged, added circular arrays
125 19-Dec-01 RMS Fixed DO command bug (found by John Dundas)
126 07-Dec-01 RMS Implemented breakpoint package
127 05-Dec-01 RMS Fixed bug in universal register logic
128 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers
129 24-Nov-01 RMS Added unit-based registers
130 16-Nov-01 RMS Added DO command
131 28-Oct-01 RMS Added relative range addressing
132 08-Oct-01 RMS Added SHOW VERSION
133 30-Sep-01 RMS Relaxed attach test in BOOT
134 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod
135 17-Sep-01 RMS Removed multiple console support
136 07-Sep-01 RMS Removed conditional externs on function prototypes
137 Added special modifier print
138 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7)
139 18-Jul-01 RMS Minor changes for Macintosh port
140 12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy)
141 27-May-01 RMS Added multiple console support
142 16-May-01 RMS Added logging
143 15-May-01 RMS Added features from Tim Litt
144 12-May-01 RMS Fixed missing return in disable_cmd
145 25-Mar-01 RMS Added ENABLE/DISABLE
146 14-Mar-01 RMS Revised LOAD/DUMP interface (again)
147 05-Mar-01 RMS Added clock calibration support
148 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0
149 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine
150 21-Jan-01 RMS Added relative time
151 22-Dec-00 RMS Fixed find_device for devices ending in numbers
152 08-Dec-00 RMS V2.5a changes
153 30-Oct-00 RMS Added output file option to examine
154 11-Jul-99 RMS V2.5 changes
155 13-Apr-99 RMS Fixed handling of 32b addresses
156 04-Oct-98 RMS V2.4 changes
157 20-Aug-98 RMS Added radix commands
158 05-Jun-98 RMS Fixed bug in ^D handling for UNIX
159 10-Apr-98 RMS Added switches to all commands
160 26-Oct-97 RMS Added search capability
161 25-Jan-97 RMS Revised data types
162 23-Jan-97 RMS Added bi-endian I/O
163 06-Sep-96 RMS Fixed bug in variable length IEXAMINE
164 16-Jun-96 RMS Changed interface to parse/print_sym
165 06-Apr-96 RMS Added error checking in reset all
166 07-Jan-96 RMS Added register buffers in save/restore
167 11-Dec-95 RMS Fixed ordering bug in save/restore
168 22-May-95 RMS Added symbolic input
169 13-Apr-95 RMS Added symbolic printouts
170 */
171
172 /* Macros and data structures */
173
174 #include "sim_defs.h"
175 #include "sim_rev.h"
176 #include <signal.h>
177 #include <ctype.h>
178
179 #ifdef HAVE_READLINE
180 #include <sim_readline.h>
181 #endif
182
183 #define EX_D 0 /* deposit */
184 #define EX_E 1 /* examine */
185 #define EX_I 2 /* interactive */
186 #define SCH_OR 0 /* search logicals */
187 #define SCH_AND 1
188 #define SCH_XOR 2
189 #define SCH_E 0 /* search booleans */
190 #define SCH_N 1
191 #define SCH_G 2
192 #define SCH_L 3
193 #define SCH_EE 4
194 #define SCH_NE 5
195 #define SCH_GE 6
196 #define SCH_LE 7
197 #define SSH_ST 0 /* set */
198 #define SSH_SH 1 /* show */
199 #define SSH_CL 2 /* clear */
200
201 #define DO_NEST_LVL 10 /* DO cmd nesting level */
202 #define SRBSIZ 1024 /* save/restore buffer */
203 #define SIM_BRK_INILNT 4096 /* bpt tbl length */
204 #define SIM_BRK_ALLTYP 0xFFFFFFFF
205 #define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \
206 sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \
207 x = sim_interval
208
209 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
210 #define SZ_R(rp) \
211 (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
212 #if defined (USE_INT64)
213 #define SZ_LOAD(sz,v,mb,j) \
214 if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \
215 else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
216 else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
217 else v = *(((t_uint64 *) mb) + ((uint32) j));
218 #define SZ_STORE(sz,v,mb,j) \
219 if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \
220 else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
221 else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
222 else *(((t_uint64 *) mb) + ((uint32) j)) = v;
223 #else
224 #define SZ_LOAD(sz,v,mb,j) \
225 if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \
226 else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
227 else v = *(((uint32 *) mb) + ((uint32) j));
228 #define SZ_STORE(sz,v,mb,j) \
229 if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \
230 else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
231 else *(((uint32 *) mb) + ((uint32) j)) = v;
232 #endif
233 #define GET_SWITCHES(cp) \
234 if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
235 #define GET_RADIX(val,dft) \
236 if (sim_switches & SWMASK ('O')) val = 8; \
237 else if (sim_switches & SWMASK ('D')) val = 10; \
238 else if (sim_switches & SWMASK ('H')) val = 16; \
239 else val = dft;
240
241 /* VM interface */
242
243 extern char sim_name[];
244 extern DEVICE *sim_devices[];
245 extern REG *sim_PC;
246 extern const char *sim_stop_messages[];
247 extern t_stat sim_instr (void);
248 extern t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int32 flag);
249 extern int32 sim_emax;
250 extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
251 UNIT *uptr, int32 sw);
252 extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val,
253 int32 sw);
254
255 /* The per-simulator init routine is a weak global that defaults to NULL
256 The other per-simulator pointers can be overrriden by the init routine */
257
258 void (*sim_vm_init) (void);
259 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
260 void (*sim_vm_post) (t_bool from_scp) = NULL;
261 CTAB *sim_vm_cmd = NULL;
262 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
263 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr) = NULL;
264
265 /* Prototypes */
266
267 /* Set and show command processors */
268
269 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
270 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
271 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
272 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
273 t_stat ssh_break (FILE *st, char *cptr, int32 flg);
274 t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr);
275 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
276 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
277 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
278 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
279 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
280 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
281 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
282 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
283 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
284 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
285 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
286 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
287 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
288 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg);
289 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag);
290 t_stat sim_check_console (int32 sec);
291 t_stat sim_save (FILE *sfile);
292 t_stat sim_rest (FILE *rfile);
293
294 /* Breakpoint package */
295
296 t_stat sim_brk_init (void);
297 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act);
298 t_stat sim_brk_clr (t_addr loc, int32 sw);
299 t_stat sim_brk_clrall (int32 sw);
300 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
301 t_stat sim_brk_showall (FILE *st, int32 sw);
302 char *sim_brk_getact (char *buf, int32 size);
303 void sim_brk_clract (void);
304 void sim_brk_npc (uint32 cnt);
305 BRKTAB *sim_brk_new (t_addr loc);
306
307 /* Commands support routines */
308
309 SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr);
310 int32 test_search (t_value val, SCHTAB *schptr);
311 char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc);
312 int32 get_switches (char *cptr);
313 char *get_sim_sw (char *cptr);
314 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
315 t_value get_rval (REG *rptr, uint32 idx);
316 void put_rval (REG *rptr, uint32 idx, t_value val);
317 t_value strtotv (char *inptr, char **endptr, uint32 radix);
318 void fprint_help (FILE *st);
319 void fprint_stopped (FILE *st, t_stat r);
320 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
321 char *read_line (char *ptr, int32 size, FILE *stream);
322 REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr);
323 char *sim_trim_endspc (char *cptr);
324
325 /* Forward references */
326
327 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr);
328 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
329 t_bool qdisable (DEVICE *dptr);
330 t_stat attach_err (UNIT *uptr, t_stat stat);
331 t_stat detach_all (int32 start_device, t_bool shutdown);
332 t_stat assign_device (DEVICE *dptr, char *cptr);
333 t_stat deassign_device (DEVICE *dptr);
334 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr);
335 t_stat run_boot_prep (void);
336 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,
337 REG *lowr, REG *highr, uint32 lows, uint32 highs);
338 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
339 t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx);
340 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,
341 t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
342 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
343 t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr,
344 UNIT *uptr, int32 dfltinc);
345 t_stat step_svc (UNIT *ptr);
346 void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[]);
347
348 /* Global data */
349
350 DEVICE *sim_dflt_dev = NULL;
351 UNIT *sim_clock_queue = NULL;
352 int32 sim_interval = 0;
353 int32 sim_switches = 0;
354 FILE *sim_ofile = NULL;
355 SCHTAB *sim_schptr = FALSE;
356 DEVICE *sim_dfdev = NULL;
357 UNIT *sim_dfunit = NULL;
358 int32 sim_opt_out = 0;
359 int32 sim_is_running = 0;
360 uint32 sim_brk_summ = 0;
361 uint32 sim_brk_types = 0;
362 uint32 sim_brk_dflt = 0;
363 char *sim_brk_act = NULL;
364 BRKTAB *sim_brk_tab = NULL;
365 int32 sim_brk_ent = 0;
366 int32 sim_brk_lnt = 0;
367 int32 sim_brk_ins = 0;
368 t_bool sim_brk_pend[SIM_BKPT_N_SPC] = { FALSE };
369 t_addr sim_brk_ploc[SIM_BKPT_N_SPC] = { 0 };
370 int32 sim_quiet = 0;
371 int32 sim_step = 0;
372 static double sim_time;
373 static uint32 sim_rtime;
374 static int32 noqueue_time;
375 volatile int32 stop_cpu = 0;
376 t_value *sim_eval = NULL;
377 int32 sim_deb_close = 0; /* 1 = close debug */
378 FILE *sim_log = NULL; /* log file */
379 FILE *sim_deb = NULL; /* debug file */
380 static SCHTAB sim_stab;
381
382 static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) };
383 #if defined USE_INT64
384 static const char *sim_si64 = "64b data";
385 #else
386 static const char *sim_si64 = "32b data";
387 #endif
388 #if defined USE_ADDR64
389 static const char *sim_sa64 = "64b addresses";
390 #else
391 static const char *sim_sa64 = "32b addresses";
392 #endif
393 #if defined USE_NETWORK
394 static const char *sim_snet = "Ethernet support";
395 #else
396 static const char *sim_snet = "no Ethernet";
397 #endif
398
399 /* Tables and strings */
400
401 const char save_vercur[] = "V3.5";
402 const char save_ver32[] = "V3.2";
403 const char save_ver30[] = "V3.0";
404 const char *scp_error_messages[] = {
405 "Address space exceeded",
406 "Unit not attached",
407 "I/O error",
408 "Checksum error",
409 "Format error",
410 "Unit not attachable",
411 "File open error",
412 "Memory exhausted",
413 "Invalid argument",
414 "Step expired",
415 "Unknown command",
416 "Read only argument",
417 "Command not completed",
418 "Simulation stopped",
419 "Goodbye",
420 "Console input I/O error",
421 "Console output I/O error",
422 "End of file",
423 "Relocation error",
424 "No settable parameters",
425 "Unit already attached",
426 "Hardware timer error",
427 "SIGINT handler setup error",
428 "Console terminal setup error",
429 "Subscript out of range",
430 "Command not allowed",
431 "Unit disabled",
432 "Read only operation not allowed",
433 "Invalid switch",
434 "Missing value",
435 "Too few arguments",
436 "Too many arguments",
437 "Non-existent device",
438 "Non-existent unit",
439 "Non-existent register",
440 "Non-existent parameter",
441 "Nested DO command limit exceeded",
442 "Internal error",
443 "Invalid magtape record length",
444 "Console Telnet connection lost",
445 "Console Telnet connection timed out",
446 "Console Telnet output stall",
447 "Assertion failed"
448 };
449
450 const size_t size_map[] = { sizeof (int8),
451 sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
452 #if defined (USE_INT64)
453 , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
454 #endif
455 };
456
457 const t_value width_mask[] = { 0,
458 0x1, 0x3, 0x7, 0xF,
459 0x1F, 0x3F, 0x7F, 0xFF,
460 0x1FF, 0x3FF, 0x7FF, 0xFFF,
461 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
462 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
463 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
464 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
465 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
466 #if defined (USE_INT64)
467 , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
468 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
469 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
470 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
471 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
472 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
473 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF,
474 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
475 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF,
476 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
477 #endif
478 };
479
480 static CTAB cmd_table[] = {
481 { "RESET", &reset_cmd, 0,
482 "r{eset} {ALL|<device>} reset simulator\n" },
483 { "EXAMINE", &exdep_cmd, EX_E,
484 "e{xamine} <list> examine memory or registers\n" },
485 { "IEXAMINE", &exdep_cmd, EX_E+EX_I,
486 "ie{xamine} <list> interactive examine memory or registers\n" },
487 { "DEPOSIT", &exdep_cmd, EX_D,
488 "d{eposit} <list> <val> deposit in memory or registers\n" },
489 { "IDEPOSIT", &exdep_cmd, EX_D+EX_I,
490 "id{eposit} <list> interactive deposit in memory or registers\n" },
491 { "EVALUATE", &eval_cmd, 0,
492 "ev{aluate} <expr> evaluate symbolic expression\n" },
493 { "RUN", &run_cmd, RU_RUN,
494 "ru{n} {new PC} reset and start simulation\n" },
495 { "GO", &run_cmd, RU_GO,
496 "go {new PC} start simulation\n" },
497 { "STEP", &run_cmd, RU_STEP,
498 "s{tep} {n} simulate n instructions\n" },
499 { "CONT", &run_cmd, RU_CONT,
500 "c{ont} continue simulation\n" },
501 { "BOOT", &run_cmd, RU_BOOT,
502 "b{oot} <unit> bootstrap unit\n" },
503 { "BREAK", &brk_cmd, SSH_ST,
504 "br{eak} <list> set breakpoints\n" },
505 { "NOBREAK", &brk_cmd, SSH_CL,
506 "nobr{eak} <list> clear breakpoints\n" },
507 { "ATTACH", &attach_cmd, 0,
508 "at{tach} <unit> <file> attach file to simulated unit\n" },
509 { "DETACH", &detach_cmd, 0,
510 "det{ach} <unit> detach file from simulated unit\n" },
511 { "ASSIGN", &assign_cmd, 0,
512 "as{sign} <device> <name> assign logical name for device\n" },
513 { "DEASSIGN", &deassign_cmd, 0,
514 "dea{ssign} <device> deassign logical name for device\n" },
515 { "SAVE", &save_cmd, 0,
516 "sa{ve} <file> save simulator to file\n" },
517 { "RESTORE", &restore_cmd, 0,
518 "rest{ore}|ge{t} <file> restore simulator from file\n" },
519 { "GET", &restore_cmd, 0, NULL },
520 { "LOAD", &load_cmd, 0,
521 "l{oad} <file> {<args>} load binary file\n" },
522 { "DUMP", &load_cmd, 1,
523 "du(mp) <file> {<args>} dump binary file\n" },
524 { "EXIT", &exit_cmd, 0,
525 "exi{t}|q{uit}|by{e} exit from simulation\n" },
526 { "QUIT", &exit_cmd, 0, NULL },
527 { "BYE", &exit_cmd, 0, NULL },
528 { "SET", &set_cmd, 0,
529 "set console arg{,arg...} set console options\n"
530 "set break <list> set breakpoints\n"
531 "set nobreak <list> clear breakpoints\n"
532 "set throttle x{M|K|%%} set simulation rate\n"
533 "set nothrottle set simulation rate to maximum\n"
534 "set <dev> OCT|DEC|HEX set device display radix\n"
535 "set <dev> ENABLED enable device\n"
536 "set <dev> DISABLED disable device\n"
537 "set <dev> DEBUG{=arg} set device debug flags\n"
538 "set <dev> NODEBUG={arg} clear device debug flags\n"
539 "set <dev> arg{,arg...} set device parameters\n"
540 "set <unit> ENABLED enable unit\n"
541 "set <unit> DISABLED disable unit\n"
542 "set <unit> arg{,arg...} set unit parameters\n"
543 },
544 { "SHOW", &show_cmd, 0,
545 "sh{ow} br{eak} <list> show breakpoints\n"
546 "sh{ow} con{figuration} show configuration\n"
547 "sh{ow} cons{ole} {arg} show console options\n"
548 "sh{ow} dev{ices} show devices\n"
549 "sh{ow} m{odifiers} show modifiers\n"
550 "sh{ow} n{ames} show logical names\n"
551 "sh{ow} q{ueue} show event queue\n"
552 "sh{ow} ti{me} show simulated time\n"
553 "sh{ow} th{rottle} show simulation rate\n"
554 "sh{ow} ve{rsion} show simulator version\n"
555 "sh{ow} <dev> RADIX show device display radix\n"
556 "sh{ow} <dev> DEBUG show device debug flags\n"
557 "sh{ow} <dev> MODIFIERS show device modifiers\n"
558 "sh{ow} <dev} NAMES show device logical name\n"
559 "sh{ow} <dev> {arg,...} show device parameters\n"
560 "sh{ow} <unit> {arg,...} show unit parameters\n" },
561 { "DO", &do_cmd, 1,
562 "do <file> {arg,arg...} process command file\n" },
563 { "ECHO", &echo_cmd, 0,
564 "echo <string> display <string>\n" },
565 { "ASSERT", &assert_cmd, 0,
566 "assert {<dev>} <cond> test simulator state against condition\n" },
567 { "HELP", &help_cmd, 0,
568 "h{elp} type this message\n"
569 "h{elp} <command> type help for command\n" },
570 { "!", &spawn_cmd, 0,
571 "! execute local command interpreter\n"
572 "! <command> execute local host command\n" },
573 { NULL, NULL, 0 }
574 };
575
576 /* Main command loop */
577
578 int main (int argc, char *argv[])
579 {
580 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr;
581 int32 i, sw;
582 t_bool lookswitch;
583 t_stat stat;
584 CTAB *cmdp;
585
586 #ifdef HAVE_READLINE
587 readline_read_history();
588 #endif
589
590 #if defined (__MWERKS__) && defined (macintosh)
591 argc = ccommand (&argv);
592 #endif
593
594 *cbuf = 0; /* init arg buffer */
595 sim_switches = 0; /* init switches */
596 lookswitch = TRUE;
597 for (i = 1; i < argc; i++) { /* loop thru args */
598 if (argv[i] == NULL) continue; /* paranoia */
599 if ((*argv[i] == '-') && lookswitch) { /* switch? */
600 if ((sw = get_switches (argv[i])) < 0) {
601 fprintf (stderr, "Invalid switch %s\n", argv[i]);
602 return 0;
603 }
604 sim_switches = sim_switches | sw;
605 }
606 else {
607 if ((strlen (argv[i]) + strlen (cbuf) + 1) >= CBUFSIZE) {
608 fprintf (stderr, "Argument string too long\n");
609 return 0;
610 }
611 if (*cbuf) strcat (cbuf, " "); /* concat args */
612 strcat (cbuf, argv[i]);
613 lookswitch = FALSE; /* no more switches */
614 }
615 } /* end for */
616 sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */
617
618 if (sim_vm_init != NULL) (*sim_vm_init)(); /* call once only */
619 sim_finit (); /* init fio package */
620 stop_cpu = 0;
621 sim_interval = 0;
622 sim_time = sim_rtime = 0;
623 noqueue_time = 0;
624 sim_clock_queue = NULL;
625 sim_is_running = 0;
626 sim_log = NULL;
627 if (sim_emax <= 0) sim_emax = 1;
628 sim_timer_init ();
629
630 if ((stat = sim_ttinit ()) != SCPE_OK) {
631 fprintf (stderr, "Fatal terminal initialization error\n%s\n",
632 scp_error_messages[stat - SCPE_BASE]);
633 return 0;
634 }
635 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
636 fprintf (stderr, "Unable to allocate examine buffer\n");
637 return 0;
638 };
639 if ((stat = reset_all_p (0)) != SCPE_OK) {
640 fprintf (stderr, "Fatal simulator initialization error\n%s\n",
641 scp_error_messages[stat - SCPE_BASE]);
642 return 0;
643 }
644 if ((stat = sim_brk_init ()) != SCPE_OK) {
645 fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
646 scp_error_messages[stat - SCPE_BASE]);
647 return 0;
648 }
649 if (!sim_quiet) {
650 printf ("\n");
651 show_version (stdout, NULL, NULL, 0, NULL);
652 }
653 if (sim_dflt_dev == NULL) sim_dflt_dev = sim_devices[0]; /* if no default */
654
655 if (*cbuf) /* cmd file arg? */
656 stat = do_cmd (0, cbuf); /* proc cmd file */
657 else if (*argv[0]) { /* sim name arg? */
658 char nbuf[PATH_MAX + 7], *np; /* "path.ini" */
659 nbuf[0] = '"'; /* starting " */
660 strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */
661 if (np = match_ext (nbuf, "EXE")) *np = 0; /* remove .exe */
662 strcat (nbuf, ".ini\""); /* add .ini" */
663 stat = do_cmd (-1, nbuf); /* proc cmd file */
664 }
665
666 while (stat != SCPE_EXIT) { /* in case exit */
667
668 if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */
669 printf ("sim> %s\n", cptr); /* echo */
670 else if (sim_vm_read != NULL) /* sim routine? */
671 cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin);
672
673 else
674 #ifdef HAVE_READLINE
675 cptr = readline_read_line(cbuf,CBUFSIZE,"sim>");
676 #else
677 printf("sim>");
678 cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */
679 #endif
680 if (cptr == NULL){ /* ignore EOF */
681 printf("\r");
682 continue;
683 }
684 if (*cptr == 0) continue; /* ignore blank */
685
686 if (sim_log) fprintf (sim_log, "sim> %s\n", cptr); /* log cmd */
687 cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */
688 sim_switches = 0; /* init switches */
689 if (cmdp = find_cmd (gbuf)) /* lookup command */
690 stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */
691 else stat = SCPE_UNK;
692 if (stat >= SCPE_BASE) { /* error? */
693 printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
694 if (sim_log) fprintf (sim_log, "%s\n",
695 scp_error_messages[stat - SCPE_BASE]);
696 }
697 if (sim_vm_post != NULL) (*sim_vm_post) (TRUE);
698 } /* end while */
699
700 detach_all (0, TRUE); /* close files */
701 sim_set_deboff (0, NULL); /* close debug */
702 sim_set_logoff (0, NULL); /* close log */
703 sim_set_notelnet (0, NULL); /* close Telnet */
704 sim_ttclose (); /* close console */
705
706 /* Write command history back to file */
707 #ifdef HAVE_READLINE
708 readline_write_history();
709 #endif
710
711 return 0;
712 }
713
714 /* Find command routine */
715
716 CTAB *find_cmd (char *gbuf)
717 {
718 CTAB *cmdp = NULL;
719
720 if (sim_vm_cmd) cmdp = find_ctab (sim_vm_cmd, gbuf); /* try ext commands */
721 if (cmdp == NULL) cmdp = find_ctab (cmd_table, gbuf); /* try regular cmds */
722 return cmdp;
723 }
724
725 /* Exit command */
726
727 t_stat exit_cmd (int32 flag, char *cptr)
728 {
729 return SCPE_EXIT;
730 }
731
732 /* Help command */
733
734 void fprint_help (FILE *st)
735 {
736 CTAB *cmdp;
737
738 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
739 if (cmdp->help) fprintf (st, cmdp->help);
740 }
741 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
742 if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name)))
743 fprintf (st, cmdp->help);
744 }
745 return;
746 }
747
748 t_stat help_cmd (int32 flag, char *cptr)
749 {
750 char gbuf[CBUFSIZE];
751 CTAB *cmdp;
752
753 GET_SWITCHES (cptr);
754 if (*cptr) {
755 cptr = get_glyph (cptr, gbuf, 0);
756 if (*cptr) return SCPE_2MARG;
757 if (cmdp = find_cmd (gbuf)) {
758 printf (cmdp->help);
759 if (sim_log) fprintf (sim_log, cmdp->help);
760 }
761 else return SCPE_ARG;
762 }
763 else {
764 fprint_help (stdout);
765 if (sim_log) fprint_help (sim_log);
766 }
767 return SCPE_OK;
768 }
769
770 /* Spawn command */
771
772 t_stat spawn_cmd (int32 flag, char *cptr)
773 {
774 if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("SHELL");
775 if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("ComSpec");
776 #if defined (VMS)
777 if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = "SPAWN/INPUT=SYS$COMMAND:";
778 #endif
779 fflush(stdout); /* flush stdout */
780 if (sim_log) fflush (sim_log); /* flush log if enabled */
781 system (cptr);
782 #if defined (VMS)
783 printf ("\n");
784 #endif
785
786 return SCPE_OK;
787 }
788
789 /* Echo command */
790
791 t_stat echo_cmd (int32 flag, char *cptr)
792 {
793 puts (cptr);
794 if (sim_log) fprintf (sim_log, "%s\n", cptr);
795 return SCPE_OK;
796 }
797
798 /* Do command
799
800 Syntax: DO {-E} {-V} <filename> {<arguments>...}
801
802 -E causes all command errors to be fatal; without it, only EXIT and ASSERT
803 failure will stop a command file.
804
805 -V causes commands to be echoed before execution.
806
807 Note that SCPE_STEP ("Step expired") is considered a note and not an error
808 and so does not abort command execution when using -E.
809
810 Inputs:
811 flag = caller and nesting level indicator
812 fcptr = filename and optional arguments, space-separated
813 Outputs:
814 status = error status
815
816 The "flag" input value indicates the source of the call, as follows:
817
818 -1 = initialization file (no error if not found)
819 0 = command line file
820 1 = "DO" command
821 >1 = nested "DO" command
822 */
823
824 #define SCPE_DOFAILED 0040000 /* fail in DO, not subproc */
825
826 t_stat do_cmd (int32 flag, char *fcptr)
827 {
828 char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE], *c, quote, *do_arg[10];
829 FILE *fpin;
830 CTAB *cmdp;
831 int32 echo, nargs, errabort;
832 t_bool interactive, isdo, staying;
833 t_stat stat;
834 char *ocptr;
835
836 stat = SCPE_OK;
837 staying = TRUE;
838 interactive = (flag > 0); /* issued interactively? */
839 if (interactive) { GET_SWITCHES (fcptr); } /* get switches */
840 echo = sim_switches & SWMASK ('V'); /* -v means echo */
841 errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */
842
843 c = fcptr;
844 for (nargs = 0; nargs < 10; ) { /* extract arguments */
845 while (isspace (*c)) c++; /* skip blanks */
846 if (*c == 0) break; /* all done */
847 if (*c == '\'' || *c == '"') quote = *c++; /* quoted string? */
848 else quote = 0;
849 do_arg[nargs++] = c; /* save start */
850 while (*c && (quote? (*c != quote): !isspace (*c))) c++;
851 if (*c) *c++ = 0; /* term at quote/spc */
852 } /* end for */
853 if (nargs <= 0) return SCPE_2FARG; /* need at least 1 */
854 if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */
855 if (flag == 0) /* cmd line file? */
856 fprintf (stderr, "Can't open file %s\n", do_arg[0]);
857 if (flag > 1)
858 return SCPE_OPENERR | SCPE_DOFAILED; /* return failure with flag */
859 else
860 return SCPE_OPENERR; /* return failure */
861 }
862 if (flag < 1) flag = 1; /* start at level 1 */
863
864 do {
865 ocptr = cptr = sim_brk_getact (cbuf, CBUFSIZE); /* get bkpt action */
866 if (!ocptr) /* no pending action? */
867 ocptr = cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */
868 sub_args (cbuf, gbuf, CBUFSIZE, nargs, do_arg); /* substitute args */
869 if (cptr == NULL) { /* EOF? */
870 stat = SCPE_OK; /* set good return */
871 break;
872 }
873 if (*cptr == 0) continue; /* ignore blank */
874 if (echo) printf("do> %s\n", cptr); /* echo if -v */
875 if (echo && sim_log) fprintf (sim_log, "do> %s\n", cptr);
876 cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */
877 sim_switches = 0; /* init switches */
878 isdo = FALSE;
879 if (cmdp = find_cmd (gbuf)) { /* lookup command */
880 isdo = (cmdp->action == &do_cmd);
881 if (isdo) { /* DO command? */
882 if (flag >= DO_NEST_LVL) stat = SCPE_NEST; /* nest too deep? */
883 else stat = do_cmd (flag + 1, cptr); /* exec DO cmd */
884 }
885 else stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */
886 }
887 else stat = SCPE_UNK; /* bad cmd given */
888 staying = (stat != SCPE_EXIT) && /* decide if staying */
889 (stat != SCPE_AFAIL) &&
890 (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP));
891 if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */
892 (stat != SCPE_STEP)) {
893 if (!echo && !sim_quiet && /* report if not echoing */
894 (!isdo || (stat & SCPE_DOFAILED))) { /* and not from DO return */
895 printf("%s> %s\n", do_arg[0], ocptr);
896 if (sim_log)
897 fprintf (sim_log, "%s> %s\n", do_arg[0], ocptr);
898 }
899 stat = stat & ~SCPE_DOFAILED; /* remove possible flag */
900 }
901 if ((staying || !interactive) && /* report error if staying */
902 (stat >= SCPE_BASE)) { /* or in cmdline file */
903 printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
904 if (sim_log) fprintf (sim_log, "%s\n",
905 scp_error_messages[stat - SCPE_BASE]);
906 }
907 if (sim_vm_post != NULL) (*sim_vm_post) (TRUE);
908 } while (staying);
909
910 fclose (fpin); /* close file */
911 return stat;
912 }
913
914 /* Substitute_args - replace %n tokens in 'instr' with the do command's arguments
915
916 Calling sequence
917 instr = input string
918 tmpbuf = temp buffer
919 maxstr = min (len (instr), len (tmpbuf))
920 nargs = number of arguments
921 do_arg[10] = arguments
922 */
923
924 void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[])
925 {
926 char *ip, *op, *ap, *oend = tmpbuf + maxstr - 2;
927
928 for (ip = instr, op = tmpbuf; *ip && (op < oend); ) {
929 if ((*ip == '\\') && (ip[1] == '%')) { /* \% = literal % */
930 ip++; /* skip \ */
931 if (*ip) *op++ = *ip++; /* copy next */
932 }
933 else if ((*ip == '%') && /* %n = sub */
934 ((ip[1] >= '1') && (ip[1] <= ('0'+ nargs - 1)))) {
935 ap = do_arg[ip[1] - '0'];
936 ip = ip + 2;
937 while (*ap && (op < oend)) *op++ = *ap++; /* copy the argument */
938 }
939 else *op++ = *ip++; /* literal character */
940 }
941 *op = 0; /* term buffer */
942 strcpy (instr, tmpbuf);
943 return;
944 }
945
946 /* Assert command
947
948 Syntax: ASSERT {<dev>} <reg>{<logical-op><value>}<conditional-op><value>
949
950 If <dev> is not specified, CPU is assumed. <value> is expressed in the radix
951 specified for <reg>. <logical-op> and <conditional-op> are the same as that
952 allowed for examine and deposit search specifications. */
953
954 t_stat assert_cmd (int32 flag, char *cptr)
955 {
956 char gbuf[CBUFSIZE], *gptr, *aptr, *tptr;
957 REG *rptr;
958 uint32 idx;
959 t_value val;
960 t_stat r;
961
962 aptr = cptr; /* save assertion */
963 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, cptr, &r); /* get sw, default */
964 if (*cptr == 0) return SCPE_2FARG; /* must be more */
965 cptr = get_glyph (cptr, gbuf, 0); /* get register */
966 rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */
967 if (!rptr) return SCPE_NXREG; /* not there */
968 if (*gptr == '[') { /* subscript? */
969 if (rptr->depth <= 1) return SCPE_ARG; /* array register? */
970 idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
971 if ((gptr == tptr) || (*tptr++ != ']')) return SCPE_ARG;
972 gptr = tptr; /* update */
973 }
974 else idx = 0; /* not array */
975 if (idx >= rptr->depth) return SCPE_SUB; /* validate subscript */
976 if (*gptr != 0) get_glyph (gptr, gbuf, 0); /* more? must be search */
977 else {
978 if (*cptr == 0) return SCPE_2FARG; /* must be more */
979 cptr = get_glyph (cptr, gbuf, 0); /* get search cond */
980 }
981 if (*cptr != 0) return SCPE_2MARG; /* must be done */
982 if (!get_search (gbuf, rptr->radix, &sim_stab)) /* parse condition */
983 return SCPE_MISVAL;
984 val = get_rval (rptr, idx); /* get register value */
985 if (test_search (val, &sim_stab)) return SCPE_OK; /* test condition */
986 return SCPE_AFAIL; /* condition fails */
987 }
988
989 /* Set command */
990
991 t_stat set_cmd (int32 flag, char *cptr)
992 {
993 int32 lvl;
994 t_stat r;
995 char gbuf[CBUFSIZE], *cvptr, *svptr;
996 DEVICE *dptr;
997 UNIT *uptr;
998 MTAB *mptr;
999 CTAB *gcmdp;
1000 C1TAB *ctbr, *glbr;
1001
1002 static CTAB set_glob_tab[] = {
1003 { "CONSOLE", &sim_set_console, 0 },
1004 { "BREAK", &brk_cmd, SSH_ST },
1005 { "TELNET", &sim_set_telnet, 0 }, /* deprecated */
1006 { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */
1007 { "LOG", &sim_set_logon, 0 }, /* deprecated */
1008 { "NOLOG", &sim_set_logoff, 0 }, /* deprecated */
1009 { "DEBUG", &sim_set_debon, 0 }, /* deprecated */
1010 { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */
1011 { "THROTTLE", &sim_set_throt, 1 },
1012 { "NOTHROTTLE", &sim_set_throt, 0 },
1013 { NULL, NULL, 0 }
1014 };
1015
1016 static C1TAB set_dev_tab[] = {
1017 { "OCTAL", &set_dev_radix, 8 },
1018 { "DECIMAL", &set_dev_radix, 10 },
1019 { "HEX", &set_dev_radix, 16 },
1020 { "ENABLED", &set_dev_enbdis, 1 },
1021 { "DISABLED", &set_dev_enbdis, 0 },
1022 { "DEBUG", &set_dev_debug, 1 },
1023 { "NODEBUG", &set_dev_debug, 0 },
1024 { NULL, NULL, 0 }
1025 };
1026
1027 static C1TAB set_unit_tab[] = {
1028 { "ENABLED", &set_unit_enbdis, 1 },
1029 { "DISABLED", &set_unit_enbdis, 0 },
1030 { NULL, NULL, 0 }
1031 };
1032
1033 GET_SWITCHES (cptr); /* get switches */
1034 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1035 cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */
1036
1037 if (dptr = find_dev (gbuf)) { /* device match? */
1038 uptr = dptr->units; /* first unit */
1039 ctbr = set_dev_tab; /* global table */
1040 lvl = MTAB_VDV; /* device match */
1041 }
1042 else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */
1043 if (uptr == NULL) return SCPE_NXUN; /* invalid unit */
1044 ctbr = set_unit_tab; /* global table */
1045 lvl = MTAB_VUN; /* unit match */
1046 }
1047 else if (gcmdp = find_ctab (set_glob_tab, gbuf)) /* global? */
1048 return gcmdp->action (gcmdp->arg, cptr); /* do the rest */
1049 else return SCPE_NXDEV; /* no match */
1050 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1051
1052 while (*cptr != 0) { /* do all mods */
1053 cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */
1054 if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */
1055 for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
1056 if ((mptr->mstring) && /* match string */
1057 (MATCH_CMD (gbuf, mptr->mstring) == 0)) { /* matches option? */
1058 if (mptr->mask & MTAB_XTD) { /* extended? */
1059 if ((lvl & mptr->mask) == 0) return SCPE_ARG;
1060 if ((lvl & MTAB_VUN) && (uptr->flags & UNIT_DIS))
1061 return SCPE_UDIS; /* unit disabled? */
1062 if (mptr->valid) { /* validation rtn? */
1063 if (cvptr && (mptr->mask & MTAB_NC))
1064 get_glyph_nc (svptr, gbuf, ',');
1065 r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
1066 if (r != SCPE_OK) return r;
1067 }
1068 else if (!mptr->desc) break; /* value desc? */
1069 else if (mptr->mask & MTAB_VAL) { /* take a value? */
1070 if (!cvptr) return SCPE_MISVAL; /* none? error */
1071 r = dep_reg (0, cvptr, (REG *) mptr->desc, 0);
1072 if (r != SCPE_OK) return r;
1073 }
1074 else if (cvptr) return SCPE_ARG; /* = value? */
1075 else *((int32 *) mptr->desc) = mptr->match;
1076 } /* end if xtd */
1077 else { /* old style */
1078 if (cvptr) return SCPE_ARG; /* = value? */
1079 if (uptr->flags & UNIT_DIS) /* disabled? */
1080 return SCPE_UDIS;
1081 if ((mptr->valid) && ((r = mptr->valid
1082 (uptr, mptr->match, cvptr, mptr->desc))
1083 != SCPE_OK)) return r; /* invalid? */
1084 uptr->flags = (uptr->flags & ~(mptr->mask)) |
1085 (mptr->match & mptr->mask); /* set new value */
1086 } /* end else xtd */
1087 break; /* terminate for */
1088 } /* end if match */
1089 } /* end for */
1090 if (!mptr || (mptr->mask == 0)) { /* no match? */
1091 if (glbr = find_c1tab (ctbr, gbuf)) { /* global match? */
1092 r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */
1093 if (r != SCPE_OK) return r;
1094 }
1095 else if (!dptr->modifiers) return SCPE_NOPARAM; /* no modifiers? */
1096 else return SCPE_NXPAR;
1097 } /* end if no mat */
1098 } /* end while */
1099 return SCPE_OK; /* done all */
1100 }
1101
1102 /* Match CTAB/CTAB1 name */
1103
1104 CTAB *find_ctab (CTAB *tab, char *gbuf)
1105 {
1106 for (; tab->name != NULL; tab++) {
1107 if (MATCH_CMD (gbuf, tab->name) == 0) return tab;
1108 }
1109 return NULL;
1110 }
1111
1112 C1TAB *find_c1tab (C1TAB *tab, char *gbuf)
1113 {
1114 for (; tab->name != NULL; tab++) {
1115 if (MATCH_CMD (gbuf, tab->name) == 0) return tab;
1116 }
1117 return NULL;
1118 }
1119
1120 /* Set device data radix routine */
1121
1122 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1123 {
1124 if (cptr) return SCPE_ARG;
1125 dptr->dradix = flag & 037;
1126 return SCPE_OK;
1127 }
1128
1129 /* Set device enabled/disabled routine */
1130
1131 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1132 {
1133 UNIT *up;
1134 uint32 i;
1135
1136 if (cptr) return SCPE_ARG;
1137 if ((dptr->flags & DEV_DISABLE) == 0) return SCPE_NOFNC;/* allowed? */
1138 if (flag) { /* enable? */
1139 if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */
1140 return SCPE_OK;
1141 dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */
1142 }
1143 else {
1144 if (dptr->flags & DEV_DIS) return SCPE_OK; /* already dsb? ok */
1145 for (i = 0; i < dptr->numunits; i++) { /* check units */
1146 up = (dptr->units) + i; /* att or active? */
1147 if ((up->flags & UNIT_ATT) || sim_is_active (up))
1148 return SCPE_NOFNC; /* can't do it */
1149 }
1150 dptr->flags = dptr->flags | DEV_DIS; /* disable */
1151 }
1152 if (dptr->reset) return dptr->reset (dptr); /* reset device */
1153 else return SCPE_OK;
1154 }
1155
1156 /* Set unit enabled/disabled routine */
1157
1158 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1159 {
1160 if (cptr) return SCPE_ARG;
1161 if (!(uptr->flags & UNIT_DISABLE)) return SCPE_NOFNC; /* allowed? */
1162 if (flag) uptr->flags = uptr->flags & ~UNIT_DIS; /* enb? enable */
1163 else {
1164 if ((uptr->flags & UNIT_ATT) || /* dsb */
1165 sim_is_active (uptr)) return SCPE_NOFNC; /* more tests */
1166 uptr->flags = uptr->flags | UNIT_DIS; /* disable */
1167 }
1168 return SCPE_OK;
1169 }
1170
1171 /* Set device debug enabled/disabled routine */
1172
1173 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1174 {
1175 char gbuf[CBUFSIZE];
1176 DEBTAB *dep;
1177
1178 if ((dptr->flags & DEV_DEBUG) == 0) return SCPE_NOFNC;
1179 if (cptr == NULL) { /* no arguments? */
1180 dptr->dctrl = flag; /* disable/enable w/o table */
1181 if (flag && dptr->debflags) { /* enable with table? */
1182 for (dep = dptr->debflags; dep->name != NULL; dep++)
1183 dptr->dctrl = dptr->dctrl | dep->mask; /* set all */
1184 }
1185 return SCPE_OK;
1186 }
1187 if (dptr->debflags == NULL) return SCPE_ARG; /* must have table */
1188 while (*cptr) {
1189 cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */
1190 for (dep = dptr->debflags; dep->name != NULL; dep++) {
1191 if (strcmp (dep->name, gbuf) == 0) { /* match? */
1192 if (flag) dptr->dctrl = dptr->dctrl | dep->mask;
1193 else dptr->dctrl = dptr->dctrl & ~dep->mask;
1194 break;
1195 }
1196 } /* end for */
1197 if (dep->mask == 0) return SCPE_ARG; /* no match? */
1198 } /* end while */
1199 return SCPE_OK;
1200 }
1201
1202 /* Show command */
1203
1204 t_stat show_cmd (int32 flag, char *cptr)
1205 {
1206 t_stat r;
1207
1208 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r); /* get sw, ofile */
1209 if (!cptr) return r; /* error? */
1210 if (sim_ofile) { /* output file? */
1211 r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */
1212 fclose (sim_ofile);
1213 }
1214 else {
1215 r = show_cmd_fi (stdout, flag, cptr); /* no, stdout, log */
1216 if (sim_log) show_cmd_fi (sim_log, flag, cptr);
1217 }
1218 return r;
1219 }
1220
1221 t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr)
1222 {
1223 int32 lvl;
1224 char gbuf[CBUFSIZE], *cvptr;
1225 DEVICE *dptr;
1226 UNIT *uptr;
1227 MTAB *mptr;
1228 SHTAB *shtb, *shptr;
1229
1230 static SHTAB show_glob_tab[] = {
1231 { "CONFIGURATION", &show_config, 0 },
1232 { "DEVICES", &show_config, 1 },
1233 { "QUEUE", &show_queue, 0 },
1234 { "TIME", &show_time, 0 },
1235 { "MODIFIERS", &show_mod_names, 0 },
1236 { "NAMES", &show_log_names, 0 },
1237 { "VERSION", &show_version, 1 },
1238 { "CONSOLE", &sim_show_console, 0 },
1239 { "BREAK", &show_break, 0 },
1240 { "LOG", &sim_show_log, 0 }, /* deprecated */
1241 { "TELNET", &sim_show_telnet, 0 }, /* deprecated */
1242 { "DEBUG", &sim_show_debug, 0 }, /* deprecated */
1243 { "THROTTLE", &sim_show_throt, 0 },
1244 { NULL, NULL, 0 }
1245 };
1246
1247 static SHTAB show_dev_tab[] = {
1248 { "RADIX", &show_dev_radix, 0 },
1249 { "DEBUG", &show_dev_debug, 0 },
1250 { "MODIFIERS", &show_dev_modifiers, 0 },
1251 { "NAMES", &show_dev_logicals, 0 },
1252 { NULL, NULL, 0 }
1253 };
1254
1255 static SHTAB show_unit_tab[] = {
1256 { NULL, NULL, 0 }
1257 };
1258
1259 GET_SWITCHES (cptr); /* get switches */
1260 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1261 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1262 if (shptr = find_shtab (show_glob_tab, gbuf)) /* global? */
1263 return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
1264
1265 if (dptr = find_dev (gbuf)) { /* device match? */
1266 uptr = dptr->units; /* first unit */
1267 shtb = show_dev_tab; /* global table */
1268 lvl = MTAB_VDV; /* device match */
1269 }
1270 else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */
1271 if (uptr == NULL) return SCPE_NXUN; /* invalid unit */
1272 if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
1273 shtb = show_unit_tab; /* global table */
1274 lvl = MTAB_VUN; /* unit match */
1275 }
1276 else return SCPE_NXDEV; /* no match */
1277
1278 if (*cptr == 0) { /* now eol? */
1279 return (lvl == MTAB_VDV)?
1280 show_device (ofile, dptr, 0):
1281 show_unit (ofile, dptr, uptr, -1);
1282 }
1283 if (dptr->modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */
1284
1285 while (*cptr != 0) { /* do all mods */
1286 cptr = get_glyph (cptr, gbuf, ','); /* get modifier */
1287 if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */
1288 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
1289 if (((mptr->mask & MTAB_XTD)? /* right level? */
1290 (mptr->mask & lvl): (MTAB_VUN & lvl)) &&
1291 ((mptr->disp && mptr->pstring && /* named disp? */
1292 (MATCH_CMD (gbuf, mptr->pstring) == 0)) ||
1293 ((mptr->mask & MTAB_VAL) && /* named value? */
1294 mptr->mstring &&
1295 (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
1296 if (cvptr && !(mptr->mask & MTAB_SHP)) return SCPE_ARG;
1297 show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
1298 break;
1299 } /* end if */
1300 } /* end for */
1301 if (mptr->mask == 0) { /* no match? */
1302 if (shptr = find_shtab (shtb, gbuf)) /* global match? */
1303 shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
1304 else return SCPE_ARG;
1305 } /* end if */
1306 } /* end while */
1307 return SCPE_OK;
1308 }
1309
1310 SHTAB *find_shtab (SHTAB *tab, char *gbuf)
1311 {
1312 for (; tab->name != NULL; tab++) {
1313 if (MATCH_CMD (gbuf, tab->name) == 0) return tab;
1314 }
1315 return NULL;
1316 }
1317
1318 /* Show device and unit */
1319
1320 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
1321 {
1322 uint32 j, udbl, ucnt;
1323 UNIT *uptr;
1324
1325 fprintf (st, "%s", sim_dname (dptr)); /* print dev name */
1326 if (qdisable (dptr)) { /* disabled? */
1327 fprintf (st, ", disabled\n");
1328 return SCPE_OK;
1329 }
1330 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */
1331 uptr = dptr->units + j;
1332 if (uptr->flags & UNIT_DISABLE) udbl++;
1333 if (!(uptr->flags & UNIT_DIS)) ucnt++;
1334 }
1335 show_all_mods (st, dptr, dptr->units, MTAB_VDV); /* show dev mods */
1336 if (dptr->numunits == 0) fprintf (st, "\n");
1337 else {
1338 if (udbl && (ucnt == 0)) fprintf (st, ", all units disabled\n");
1339 else if (ucnt > 1) fprintf (st, ", %d units\n", ucnt);
1340 else if (flag) fprintf (st, "\n");
1341 }
1342 if (flag) return SCPE_OK; /* dev only? */
1343 for (j = 0; j < dptr->numunits; j++) { /* loop thru units */
1344 uptr = dptr->units + j;
1345 if ((uptr->flags & UNIT_DIS) == 0)
1346 show_unit (st, dptr, uptr, ucnt);
1347 }
1348 return SCPE_OK;
1349 }
1350
1351 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
1352 {
1353 int32 u = uptr - dptr->units;
1354
1355 if (flag > 1) fprintf (st, " %s%d", sim_dname (dptr), u);
1356 else if (flag < 0) fprintf (st, "%s%d", sim_dname (dptr), u);
1357 if (uptr->flags & UNIT_FIX) {
1358 fprintf (st, ", ");
1359 fprint_capac (st, dptr, uptr);
1360 }
1361 if (uptr->flags & UNIT_ATT) {
1362 fprintf (st, ", attached to %s", uptr->filename);
1363 if (uptr->flags & UNIT_RO) fprintf (st, ", read only");
1364 }
1365 else if (uptr->flags & UNIT_ATTABLE)
1366 fprintf (st, ", not attached");
1367 show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */
1368 fprintf (st, "\n");
1369 return SCPE_OK;
1370 }
1371
1372 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
1373 {
1374 t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000;
1375 t_addr mval = kval * kval;
1376 t_addr psize = uptr->capac;
1377 char scale, width;
1378
1379 if ((dptr->dwidth / dptr->aincr) > 8) width = 'W';
1380 else width = 'B';
1381 if (uptr->capac < (kval * 10)) scale = 0;
1382 else if (uptr->capac < (mval * 10)) {
1383 scale = 'K';
1384 psize = psize / kval;
1385 }
1386 else {
1387 scale = 'M';
1388 psize = psize / mval;
1389 }
1390 fprint_val (st, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
1391 if (scale) fputc (scale, st);
1392 fputc (width, st);
1393 return;
1394 }
1395
1396 /* Show <global name> processors */
1397
1398 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1399 {
1400 int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH, vdelt = SIM_DELTA;
1401
1402 if (cptr && (*cptr != 0)) return SCPE_2MARG;
1403 fprintf (st, "%s simulator V%d.%d-%d", sim_name, vmaj, vmin, vpat);
1404 if (vdelt) fprintf (st, "(%d)", vdelt);
1405 if (flag) fprintf (st, " [%s, %s, %s]", sim_si64, sim_sa64, sim_snet);
1406 fprintf (st, "\n");
1407 return SCPE_OK;
1408 }
1409
1410 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)
1411 {
1412 int32 i;
1413 DEVICE *dptr;
1414
1415 if (cptr && (*cptr != 0)) return SCPE_2MARG;
1416 fprintf (st, "%s simulator configuration\n\n", sim_name);
1417 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
1418 show_device (st, dptr, flag);
1419 return SCPE_OK;
1420 }
1421
1422 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)
1423 {
1424 int32 i;
1425 DEVICE *dptr;
1426
1427 if (cptr && (*cptr != 0)) return SCPE_2MARG;
1428 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
1429 show_dev_logicals (st, dptr, NULL, 1, cptr);
1430 return SCPE_OK;
1431 }
1432
1433 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1434 {
1435 if (dptr->lname) fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
1436 else if (!flag) fputs ("no logical name assigned\n", st);
1437 return SCPE_OK;
1438 }
1439
1440 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)
1441 {
1442 DEVICE *dptr;
1443 UNIT *uptr;
1444 int32 accum;
1445
1446 if (cptr && (*cptr != 0)) return SCPE_2MARG;
1447 if (sim_clock_queue == NULL) {
1448 fprintf (st, "%s event queue empty, time = %.0f\n",
1449 sim_name, sim_time);
1450 return SCPE_OK;
1451 }
1452 fprintf (st, "%s event queue status, time = %.0f\n",
1453 sim_name, sim_time);
1454 accum = 0;
1455 for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) {
1456 if (uptr == &sim_step_unit) fprintf (st, " Step timer");
1457 else if ((dptr = find_dev_from_unit (uptr)) != NULL) {
1458 fprintf (st, " %s", sim_dname (dptr));
1459 if (dptr->numunits > 1) fprintf (st, " unit %d",
1460 (int32) (uptr - dptr->units));
1461 }
1462 else fprintf (st, " Unknown");
1463 fprintf (st, " at %d\n", accum + uptr->time);
1464 accum = accum + uptr->time;
1465 }
1466 return SCPE_OK;
1467 }
1468
1469 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1470 {
1471 if (cptr && (*cptr != 0)) return SCPE_2MARG;
1472 fprintf (st, "Time:\t%.0f\n", sim_time);
1473 return SCPE_OK;
1474 }
1475
1476 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1477 {
1478 t_stat r;
1479
1480 if (cptr && (*cptr != 0)) r = ssh_break (st, cptr, 1); /* more? */
1481 else r = sim_brk_showall (st, sim_switches);
1482 return r;
1483 }
1484
1485 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1486 {
1487 fprintf (st, "Radix=%d\n", dptr->dradix);
1488 return SCPE_OK;
1489 }
1490
1491 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1492 {
1493 int32 any = 0;
1494 DEBTAB *dep;
1495
1496 if (dptr->flags & DEV_DEBUG) {
1497 if (dptr->dctrl == 0) fputs ("Debugging disabled", st);
1498 else if (dptr->debflags == NULL) fputs ("Debugging enabled", st);
1499 else {
1500 fputs ("Debug=", st);
1501 for (dep = dptr->debflags; dep->name != NULL; dep++) {
1502 if (dptr->dctrl & dep->mask) {
1503 if (any) fputc (';', st);
1504 fputs (dep->name, st);
1505 any = 1;
1506 }
1507 }
1508 }
1509 fputc ('\n', st);
1510 return SCPE_OK;
1511 }
1512 else return SCPE_NOFNC;
1513 }
1514
1515 /* Show modifiers */
1516
1517 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)
1518 {
1519 int32 i;
1520 DEVICE *dptr;
1521
1522 if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */
1523 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
1524 show_dev_modifiers (st, dptr, NULL, flag, cptr);
1525 return SCPE_OK;
1526 }
1527
1528 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
1529 {
1530 int any, enb;
1531 MTAB *mptr;
1532 DEBTAB *dep;
1533
1534 any = enb = 0;
1535 if (dptr->modifiers) {
1536 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
1537 if (mptr->mstring) {
1538 if (strcmp (mptr->mstring, "ENABLED") == 0) enb = 1;
1539 if (any++) fprintf (st, ", %s", mptr->mstring);
1540 else fprintf (st, "%s\t%s", sim_dname (dptr), mptr->mstring);
1541 }
1542 }
1543 }
1544 if (dptr->flags & DEV_DEBUG) {
1545 if (any++) fprintf (st, ", DEBUG, NODEBUG");
1546 else fprintf (st, "%s\tDEBUG, NODEBUG", sim_dname (dptr));
1547 }
1548 if (!enb && (dptr->flags & DEV_DISABLE)) {
1549 if (any++) fprintf (st, ", ENABLED, DISABLED");
1550 else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr));
1551 }
1552 if (any) fprintf (st, "\n");
1553 if ((dptr->flags & DEV_DEBUG) && dptr->debflags) {
1554 fprintf (st, "%s\tDEBUG=", sim_dname (dptr));
1555 for (dep = dptr->debflags; dep->name != NULL; dep++)
1556 fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ","), dep->name);
1557 fprintf (st, "\n");
1558 }
1559 return SCPE_OK;
1560 }
1561
1562 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
1563 {
1564 MTAB *mptr;
1565
1566 if (dptr->modifiers == NULL) return SCPE_OK;
1567 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
1568 if (mptr->pstring && ((mptr->mask & MTAB_XTD)?
1569 ((mptr->mask & flag) && !(mptr->mask & MTAB_NMO)):
1570 ((MTAB_VUN & flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
1571 fputs (", ", st);
1572 show_one_mod (st, dptr, uptr, mptr, NULL, 0);
1573 }
1574 }
1575 return SCPE_OK;
1576 }
1577
1578 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
1579 char *cptr, int32 flag)
1580 {
1581 t_value val;
1582
1583 if (mptr->disp) mptr->disp (st, uptr, mptr->match, cptr? cptr: mptr->desc);
1584 else if ((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_VAL)) {
1585 REG *rptr = (REG *) mptr->desc;
1586 fprintf (st, "%s=", mptr->pstring);
1587 val = get_rval (rptr, 0);
1588 fprint_val (st, val, rptr->radix, rptr->width,
1589 rptr->flags & REG_FMT);
1590 }
1591 else fputs (mptr->pstring, st);
1592 if (flag && !((mptr->mask & MTAB_XTD) &&
1593 (mptr->mask & MTAB_NMO))) fputc ('\n', st);
1594 return SCPE_OK;
1595 }
1596
1597 /* Breakpoint commands */
1598
1599 t_stat brk_cmd (int32 flg, char *cptr)
1600 {
1601 GET_SWITCHES (cptr); /* get switches */
1602 return ssh_break (NULL, cptr, flg); /* call common code */
1603 }
1604
1605 t_stat ssh_break (FILE *st, char *cptr, int32 flg)
1606 {
1607 char gbuf[CBUFSIZE], *tptr, *t1ptr, *aptr;
1608 DEVICE *dptr = sim_dflt_dev;
1609 UNIT *uptr = dptr->units;
1610 t_stat r;
1611 t_addr lo, hi, max = uptr->capac - 1;
1612 int32 cnt;
1613
1614 if (sim_brk_types == 0) return SCPE_NOFNC;
1615 if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;
1616 if (aptr = strchr (cptr, ';')) { /* ;action? */
1617 if (flg != SSH_ST) return SCPE_ARG; /* only on SET */
1618 *aptr++ = 0; /* separate strings */
1619 }
1620 if (*cptr == 0) { /* no argument? */
1621 lo = (t_addr) get_rval (sim_PC, 0); /* use PC */
1622 return ssh_break_one (st, flg, lo, 0, aptr);
1623 }
1624 while (*cptr) {
1625 cptr = get_glyph (cptr, gbuf, ',');
1626 tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
1627 if (tptr == NULL) return SCPE_ARG;
1628 if (*tptr == '[') {
1629 cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
1630 if ((tptr == t1ptr) || (*t1ptr != ']') ||
1631 (flg != SSH_ST)) return SCPE_ARG;
1632 tptr = t1ptr + 1;
1633 }
1634 else cnt = 0;
1635 if (*tptr != 0) return SCPE_ARG;
1636 if ((lo == 0) && (hi == max)) {
1637 if (flg == SSH_CL) sim_brk_clrall (sim_switches);
1638 else if (flg == SSH_SH) sim_brk_showall (st, sim_switches);
1639 else return SCPE_ARG;
1640 }
1641 else {
1642 for ( ; lo <= hi; lo = lo + 1) {
1643 r = ssh_break_one (st, flg, lo, cnt, aptr);
1644 if (r != SCPE_OK) return r;
1645 }
1646 }
1647 }
1648 return SCPE_OK;
1649 }
1650
1651 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr)
1652 {
1653 switch (flg) {
1654
1655 case SSH_ST:
1656 return sim_brk_set (lo, sim_switches, cnt, aptr);
1657 break;
1658
1659 case SSH_CL:
1660 return sim_brk_clr (lo, sim_switches);
1661 break;
1662
1663 case SSH_SH:
1664 return sim_brk_show (st, lo, sim_switches);
1665 break;
1666
1667 default:
1668 return SCPE_ARG;
1669 }
1670 }
1671
1672 /* Reset command and routines
1673
1674 re[set] reset all devices
1675 re[set] all reset all devices
1676 re[set] device reset specific device
1677 */
1678
1679 t_stat reset_cmd (int32 flag, char *cptr)
1680 {
1681 char gbuf[CBUFSIZE];
1682 DEVICE *dptr;
1683
1684 GET_SWITCHES (cptr); /* get switches */
1685 if (*cptr == 0) return (reset_all (0)); /* reset(cr) */
1686 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1687 if (*cptr != 0) return SCPE_2MARG; /* now eol? */
1688 if (strcmp (gbuf, "ALL") == 0) return (reset_all (0));
1689 dptr = find_dev (gbuf); /* locate device */
1690 if (dptr == NULL) return SCPE_NXDEV; /* found it? */
1691 if (dptr->reset != NULL) return dptr->reset (dptr);
1692 else return SCPE_OK;
1693 }
1694
1695 /* Reset devices start..end
1696
1697 Inputs:
1698 start = number of starting device
1699 Outputs:
1700 status = error status
1701 */
1702
1703 t_stat reset_all (uint32 start)
1704 {
1705 DEVICE *dptr;
1706 uint32 i;
1707 t_stat reason;
1708
1709 for (i = 0; i < start; i++) {
1710 if (sim_devices[i] == NULL) return SCPE_IERR;
1711 }
1712 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
1713 if (dptr->reset != NULL) {
1714 reason = dptr->reset (dptr);
1715 if (reason != SCPE_OK) return reason;
1716 }
1717 }
1718 return SCPE_OK;
1719 }
1720
1721 /* Reset to powerup state
1722
1723 Inputs:
1724 start = number of starting device
1725 Outputs:
1726 status = error status
1727 */
1728
1729 t_stat reset_all_p (uint32 start)
1730 {
1731 t_stat r;
1732 int32 old_sw = sim_switches;
1733
1734 sim_switches = SWMASK ('P');
1735 r = reset_all (start);
1736 sim_switches = old_sw;
1737 return r;
1738 }
1739
1740 /* Load and dump commands
1741
1742 lo[ad] filename {arg} load specified file
1743 du[mp] filename {arg} dump to specified file
1744 */
1745
1746 t_stat load_cmd (int32 flag, char *cptr)
1747 {
1748 char gbuf[CBUFSIZE];
1749 FILE *loadfile;
1750 t_stat reason;
1751
1752 GET_SWITCHES (cptr); /* get switches */
1753 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1754 cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */
1755 loadfile = sim_fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */
1756 if (loadfile == NULL) return SCPE_OPENERR;
1757 GET_SWITCHES (cptr); /* get switches */
1758 reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */
1759 fclose (loadfile);
1760 return reason;
1761 }
1762
1763 /* Attach command
1764
1765 at[tach] unit file attach specified unit to file
1766 */
1767
1768 t_stat attach_cmd (int32 flag, char *cptr)
1769 {
1770 char gbuf[CBUFSIZE];
1771 DEVICE *dptr;
1772 UNIT *uptr;
1773 t_stat r;
1774
1775 GET_SWITCHES (cptr); /* get switches */
1776 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1777 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1778 GET_SWITCHES (cptr); /* get switches */
1779 if (*cptr == 0) return SCPE_2FARG; /* now eol? */
1780 dptr = find_unit (gbuf, &uptr); /* locate unit */
1781 if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
1782 if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
1783 if (uptr->flags & UNIT_ATT) { /* already attached? */
1784 r = scp_detach_unit (dptr, uptr); /* detach it */
1785 if (r != SCPE_OK) return r; /* error? */
1786 }
1787 sim_trim_endspc (cptr); /* trim trailing spc */
1788 return scp_attach_unit (dptr, uptr, cptr); /* attach */
1789 }
1790
1791 /* Call device-specific or file-oriented attach unit routine */
1792
1793 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr)
1794 {
1795 if (dptr->attach != NULL) /* device routine? */
1796 return dptr->attach (uptr, cptr); /* call it */
1797 return attach_unit (uptr, cptr); /* no, std routine */
1798 }
1799
1800 /* Attach unit to file */
1801
1802 t_stat attach_unit (UNIT *uptr, char *cptr)
1803 {
1804 DEVICE *dptr;
1805
1806 if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
1807 if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* not attachable? */
1808 if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT;
1809 if (dptr->flags & DEV_RAWONLY) return SCPE_NOFNC; /* raw mode only? */
1810 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
1811 if (uptr->filename == NULL) return SCPE_MEM;
1812 strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */
1813 if (sim_switches & SWMASK ('R')) { /* read only? */
1814 if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */
1815 return attach_err (uptr, SCPE_NORO); /* no, error */
1816 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
1817 if (uptr->fileref == NULL) /* open fail? */
1818 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
1819 uptr->flags = uptr->flags | UNIT_RO; /* set rd only */
1820 if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr));
1821 }
1822 else { /* normal */
1823 uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */
1824 if (uptr->fileref == NULL) { /* open fail? */
1825 if ((errno == EROFS) || (errno == EACCES)) { /* read only? */
1826 if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */
1827 return attach_err (uptr, SCPE_NORO); /* no error */
1828 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
1829 if (uptr->fileref == NULL) /* open fail? */
1830 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
1831 uptr->flags = uptr->flags | UNIT_RO; /* set rd only */
1832 if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr));
1833 }
1834 else { /* doesn't exist */
1835 if (sim_switches & SWMASK ('E')) /* must exist? */
1836 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
1837 uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */
1838 if (uptr->fileref == NULL) /* open fail? */
1839 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
1840 if (!sim_quiet) printf ("%s: creating new file\n", sim_dname (dptr));
1841 }
1842 } /* end if null */
1843 } /* end else */
1844 if (uptr->flags & UNIT_BUFABLE) { /* buffer? */
1845 uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */
1846 if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */
1847 uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */
1848 if (uptr->filebuf == NULL) /* no buffer? */
1849 return attach_err (uptr, SCPE_MEM); /* error */
1850 if (!sim_quiet) printf ("%s: buffering file in memory\n", sim_dname (dptr));
1851 uptr->hwmark = sim_fread (uptr->filebuf, /* read file */
1852 SZ_D (dptr), cap, uptr->fileref);
1853 uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */
1854 }
1855 uptr->flags = uptr->flags | UNIT_ATT;
1856 uptr->pos = 0;
1857 return SCPE_OK;
1858 }
1859
1860 t_stat attach_err (UNIT *uptr, t_stat stat)
1861 {
1862 free (uptr->filename);
1863 uptr->filename = NULL;
1864 return stat;
1865 }
1866
1867 /* Detach command
1868
1869 det[ach] all detach all units
1870 det[ach] unit detach specified unit
1871 */
1872
1873 t_stat detach_cmd (int32 flag, char *cptr)
1874 {
1875 char gbuf[CBUFSIZE];
1876 DEVICE *dptr;
1877 UNIT *uptr;
1878
1879 GET_SWITCHES (cptr); /* get switches */
1880 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1881 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1882 if (*cptr != 0) return SCPE_2MARG; /* now eol? */
1883 if (strcmp (gbuf, "ALL") == 0) return (detach_all (0, FALSE));
1884 dptr = find_unit (gbuf, &uptr); /* locate unit */
1885 if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
1886 if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
1887 return scp_detach_unit (dptr, uptr); /* detach */
1888 }
1889
1890 /* Detach devices start..end
1891
1892 Inputs:
1893 start = number of starting device
1894 shutdown = TRUE if simulator shutting down
1895 Outputs:
1896 status = error status
1897
1898 Note that during shutdown, detach routines for non-attachable devices
1899 will be called. These routines can implement simulator shutdown.
1900 */
1901
1902 t_stat detach_all (int32 start, t_bool shutdown)
1903 {
1904 uint32 i, j;
1905 DEVICE *dptr;
1906 UNIT *uptr;
1907 t_stat r;
1908
1909 if ((start < 0) || (start > 1)) return SCPE_IERR;
1910 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
1911 for (j = 0; j < dptr->numunits; j++) { /* loop thru units */
1912 uptr = (dptr->units) + j;
1913 if ((uptr->flags & UNIT_ATT) || /* attached? */
1914 (shutdown && dptr->detach && /* shutdown, spec rtn, */
1915 !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */
1916 r = scp_detach_unit (dptr, uptr); /* detach unit */
1917 if (r != SCPE_OK) return r;
1918 }
1919 }
1920 }
1921 return SCPE_OK;
1922 }
1923
1924 /* Call device-specific or file-oriented detach unit routine */
1925
1926 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
1927 {
1928 if (dptr->detach != NULL) return dptr->detach (uptr); /* device routine? */
1929 return detach_unit (uptr); /* no, standard */
1930 }
1931
1932 /* Detach unit from file */
1933
1934 t_stat detach_unit (UNIT *uptr)
1935 {
1936 DEVICE *dptr;
1937
1938 if (uptr == NULL) return SCPE_IERR;
1939 if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* attachable? */
1940 if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */
1941 if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK;
1942 if (uptr->flags & UNIT_BUF) {
1943 uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
1944 if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
1945 if (!sim_quiet) printf ("%s: writing buffer to file\n", sim_dname (dptr));
1946 rewind (uptr->fileref);
1947 sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
1948 if (ferror (uptr->fileref)) perror ("I/O error");
1949 }
1950 if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */
1951 free (uptr->filebuf); /* free buf */
1952 uptr->filebuf = NULL;
1953 }
1954 uptr->flags = uptr->flags & ~UNIT_BUF;
1955 }
1956 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
1957 free (uptr->filename);
1958 uptr->filename = NULL;
1959 if (fclose (uptr->fileref) == EOF) return SCPE_IOERR;
1960 return SCPE_OK;
1961 }
1962
1963 /* Assign command
1964
1965 as[sign] device name assign logical name to device
1966 */
1967
1968 t_stat assign_cmd (int32 flag, char *cptr)
1969 {
1970 char gbuf[CBUFSIZE];
1971 DEVICE *dptr;
1972
1973 GET_SWITCHES (cptr); /* get switches */
1974 if (*cptr == 0) return SCPE_2FARG; /* must be more */
1975 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1976 GET_SWITCHES (cptr); /* get switches */
1977 if (*cptr == 0) return SCPE_2FARG; /* now eol? */
1978 dptr = find_dev (gbuf); /* locate device */
1979 if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
1980 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
1981 if (*cptr != 0) return SCPE_2MARG; /* must be eol */
1982 if (find_dev (gbuf)) return SCPE_ARG; /* name in use */
1983 deassign_device (dptr); /* release current */
1984 return assign_device (dptr, gbuf);
1985 }
1986
1987 t_stat assign_device (DEVICE *dptr, char *cptr)
1988 {
1989 dptr->lname = (char *) calloc (CBUFSIZE, sizeof (char));
1990 if (dptr->lname == NULL) return SCPE_MEM;
1991 strncpy (dptr->lname, cptr, CBUFSIZE);
1992 return SCPE_OK;
1993 }
1994
1995 /* Deassign command
1996
1997 dea[ssign] device deassign logical name
1998 */
1999
2000 t_stat deassign_cmd (int32 flag, char *cptr)
2001 {
2002 char gbuf[CBUFSIZE];
2003 DEVICE *dptr;
2004
2005 GET_SWITCHES (cptr); /* get switches */
2006 if (*cptr == 0) return SCPE_2FARG; /* must be more */
2007 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
2008 if (*cptr != 0) return SCPE_2MARG; /* now eol? */
2009 dptr = find_dev (gbuf); /* locate device */
2010 if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
2011 return deassign_device (dptr);
2012 }
2013
2014 t_stat deassign_device (DEVICE *dptr)
2015 {
2016 if (dptr->lname) free (dptr->lname);
2017 dptr->lname = NULL;
2018 return SCPE_OK;
2019 }
2020
2021 /* Get device display name */
2022
2023 char *sim_dname (DEVICE *dptr)
2024 {
2025 return (dptr->lname? dptr->lname: dptr->name);
2026 }
2027
2028 /* Save command
2029
2030 sa[ve] filename save state to specified file
2031 */
2032
2033 t_stat save_cmd (int32 flag, char *cptr)
2034 {
2035 FILE *sfile;
2036 t_stat r;
2037 GET_SWITCHES (cptr); /* get switches */
2038 if (*cptr == 0) return SCPE_2FARG; /* must be more */
2039 sim_trim_endspc (cptr);
2040 if ((sfile = sim_fopen (cptr, "wb")) == NULL) return SCPE_OPENERR;
2041 r = sim_save (sfile);
2042 fclose (sfile);
2043 return r;
2044 }
2045
2046 t_stat sim_save (FILE *sfile)
2047 {
2048 void *mbuf;
2049 int32 l, t;
2050 uint32 i, j;
2051 t_addr k, high;
2052 t_value val;
2053 t_stat r;
2054 t_bool zeroflg;
2055 size_t sz;
2056 DEVICE *dptr;
2057 UNIT *uptr;
2058 REG *rptr;
2059
2060 #define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile)
2061
2062 fprintf (sfile, "%s\n%s\n%s\n%s\n%s\n%.0f\n",
2063 save_vercur, /* [V2.5] save format */
2064 sim_name, /* sim name */
2065 sim_si64, sim_sa64, sim_snet, /* [V3.5] options */
2066 sim_time); /* [V3.2] sim time */
2067 WRITE_I (sim_rtime); /* [V2.6] sim rel time */
2068
2069 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */
2070 fputs (dptr->name, sfile); /* device name */
2071 fputc ('\n', sfile);
2072 if (dptr->lname) fputs (dptr->lname, sfile); /* [V3.0] logical name */
2073 fputc ('\n', sfile);
2074 WRITE_I (dptr->flags); /* [V2.10] flags */
2075 for (j = 0; j < dptr->numunits; j++) {
2076 uptr = dptr->units + j;
2077 t = sim_is_active (uptr);
2078 WRITE_I (j); /* unit number */
2079 WRITE_I (t); /* activation time */
2080 WRITE_I (uptr->u3); /* unit specific */
2081 WRITE_I (uptr->u4);
2082 WRITE_I (uptr->u5); /* [V3.0] more unit */
2083 WRITE_I (uptr->u6);
2084 WRITE_I (uptr->flags); /* [V2.10] flags */
2085 WRITE_I (uptr->capac); /* [V3.5] capacity */
2086 if (uptr->flags & UNIT_ATT) fputs (uptr->filename, sfile);
2087 fputc ('\n', sfile);
2088 if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&
2089 (dptr->examine != NULL) &&
2090 ((high = uptr->capac) != 0)) { /* memory-like unit? */
2091 WRITE_I (high); /* [V2.5] write size */
2092 sz = SZ_D (dptr);
2093 if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {
2094 fclose (sfile);
2095 return SCPE_MEM;
2096 }
2097 for (k = 0; k < high; ) { /* loop thru mem */
2098 zeroflg = TRUE;
2099 for (l = 0; (l < SRBSIZ) && (k < high); l++,
2100 k = k + (dptr->aincr)) { /* check for 0 block */
2101 r = dptr->examine (&val, k, uptr, SIM_SW_REST);
2102 if (r != SCPE_OK) return r;
2103 if (val) zeroflg = FALSE;
2104 SZ_STORE (sz, val, mbuf, l);
2105 } /* end for l */
2106 if (zeroflg) { /* all zero's? */
2107 l = -l; /* invert block count */
2108 WRITE_I (l); /* write only count */
2109 }
2110 else {
2111 WRITE_I (l); /* block count */
2112 sim_fwrite (mbuf, sz, l, sfile);
2113 }
2114 } /* end for k */
2115 free (mbuf); /* dealloc buffer */
2116 } /* end if mem */
2117 else { /* no memory */
2118 high = 0; /* write 0 */
2119 WRITE_I (high);
2120 } /* end else mem */
2121 } /* end unit loop */
2122 t = -1; /* end units */
2123 WRITE_I (t); /* write marker */
2124 for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */
2125 (rptr->name != NULL); rptr++) {
2126 fputs (rptr->name, sfile); /* name */
2127 fputc ('\n', sfile);
2128 WRITE_I (rptr->depth); /* [V2.10] depth */
2129 for (j = 0; j < rptr->depth; j++) { /* loop thru values */
2130 val = get_rval (rptr, j); /* get value */
2131 WRITE_I (val); /* store */
2132 }
2133 }
2134 fputc ('\n', sfile); /* end registers */
2135 }
2136 fputc ('\n', sfile); /* end devices */
2137 return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */
2138 }
2139
2140 /* Restore command
2141
2142 re[store] filename restore state from specified file
2143 */
2144
2145 t_stat restore_cmd (int32 flag, char *cptr)
2146 {
2147 FILE *rfile;
2148 t_stat r;
2149
2150 GET_SWITCHES (cptr); /* get switches */
2151 if (*cptr == 0) return SCPE_2FARG; /* must be more */
2152 sim_trim_endspc (cptr);
2153 if ((rfile = sim_fopen (cptr, "rb")) == NULL) return SCPE_OPENERR;
2154 r = sim_rest (rfile);
2155 fclose (rfile);
2156 return r;
2157 }
2158
2159 t_stat sim_rest (FILE *rfile)
2160 {
2161 char buf[CBUFSIZE];
2162 void *mbuf;
2163 int32 j, blkcnt, limit, unitno, time, flg;
2164 uint32 us, depth;
2165 t_addr k, high, old_capac;
2166 t_value val, mask;
2167 t_stat r;
2168 size_t sz;
2169 t_bool v35, v32;
2170 DEVICE *dptr;
2171 UNIT *uptr;
2172 REG *rptr;
2173
2174 #define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \
2175 return SCPE_IOERR;
2176 #define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
2177 return SCPE_IOERR;
2178
2179 READ_S (buf); /* [V2.5+] read version */
2180 v35 = v32 = FALSE;
2181 if (strcmp (buf, save_vercur) == 0) v35 = v32 = TRUE; /* version 3.5? */
2182 else if (strcmp (buf, save_ver32) == 0) v32 = TRUE; /* version 3.2? */
2183 else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */
2184 printf ("Invalid file version: %s\n", buf);
2185 return SCPE_INCOMP;
2186 }
2187 READ_S (buf); /* read sim name */
2188 if (strcmp (buf, sim_name)) { /* name match? */
2189 printf ("Wrong system type: %s\n", buf);
2190 return SCPE_INCOMP;
2191 }
2192 if (v35) { /* [V3.5+] options */
2193 READ_S (buf); /* integer size */
2194 if (strcmp (buf, sim_si64) != 0) {
2195 printf ("Incompatible integer size, save file = %s\n", buf);
2196 return SCPE_INCOMP;
2197 }
2198 READ_S (buf); /* address size */
2199 if (strcmp (buf, sim_sa64) != 0) {
2200 printf ("Incompatible address size, save file = %s\n", buf);
2201 return SCPE_INCOMP;
2202 }
2203 READ_S (buf); /* Ethernet */
2204 }
2205 if (v32) { /* [V3.2+] time as string */
2206 READ_S (buf);
2207 sscanf (buf, "%lf", &sim_time);
2208 }
2209 else READ_I (sim_time); /* sim time */
2210 READ_I (sim_rtime); /* [V2.6+] sim rel time */
2211
2212 for ( ;; ) { /* device loop */
2213 READ_S (buf); /* read device name */
2214 if (buf[0] == 0) break; /* last? */
2215 if ((dptr = find_dev (buf)) == NULL) { /* locate device */
2216 printf ("Invalid device name: %s\n", buf);
2217 return SCPE_INCOMP;
2218 }
2219 READ_S (buf); /* [V3.0+] logical name */
2220 deassign_device (dptr); /* delete old name */
2221 if ((buf[0] != 0) &&
2222 ((r = assign_device (dptr, buf)) != SCPE_OK)) return r;
2223 READ_I (flg); /* [V2.10+] ctlr flags */
2224 if (!v32) flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) |
2225 (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */
2226 dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */
2227 (flg & DEV_RFLAGS);
2228 for ( ;; ) { /* unit loop */
2229 sim_switches = SIM_SW_REST; /* flag rstr, clr RO */
2230 READ_I (unitno); /* unit number */
2231 if (unitno < 0) break; /* end units? */
2232 if ((uint32) unitno >= dptr->numunits) { /* too big? */
2233 printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno);
2234 return SCPE_INCOMP;
2235 }
2236 READ_I (time); /* event time */
2237 uptr = (dptr->units) + unitno;
2238 sim_cancel (uptr);
2239 if (time > 0) sim_activate (uptr, time - 1);
2240 READ_I (uptr->u3); /* device specific */
2241 READ_I (uptr->u4);
2242 READ_I (uptr->u5); /* [V3.0+] more dev spec */
2243 READ_I (uptr->u6);
2244 READ_I (flg); /* [V2.10+] unit flags */
2245 old_capac = uptr->capac; /* save current capacity */
2246 if (v35) { /* [V3.5+] capacity */
2247 READ_I (uptr->capac);
2248 }
2249 if (!v32) flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) |
2250 (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */
2251 uptr->flags = (uptr->flags & ~UNIT_RFLAGS) |
2252 (flg & UNIT_RFLAGS); /* restore */
2253 READ_S (buf); /* attached file */
2254 if ((uptr->flags & UNIT_ATTABLE) && /* if attachable and */
2255 (!(dptr->flags & DEV_NET) || /* not net dev or */
2256 !(uptr->flags & UNIT_ATT) || /* not currently att */
2257 (buf[0] == 0))) { /* or will not be att */
2258 r = scp_detach_unit (dptr, uptr); /* detach old */
2259 if (r != SCPE_OK) return r;
2260 if (buf[0] != 0) { /* any file? */
2261 uptr->flags = uptr->flags & ~UNIT_DIS;
2262 if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */
2263 sim_switches |= SWMASK ('R'); /* RO attach */
2264 r = scp_attach_unit (dptr, uptr, buf);
2265 if (r != SCPE_OK) return r;
2266 }
2267 }
2268 READ_I (high); /* memory capacity */
2269 if (high > 0) { /* [V2.5+] any memory? */
2270 if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||
2271 (dptr->deposit == NULL)) {
2272 printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno);
2273 return SCPE_INCOMP;
2274 }
2275 if (high != old_capac) { /* size change? */
2276 uptr->capac = old_capac; /* temp restore old */
2277 if ((dptr->flags & DEV_DYNM) &&
2278 ((dptr->msize == NULL) ||
2279 (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) {
2280 printf ("Can't change memory size: %s%d\n",
2281 sim_dname (dptr), unitno);
2282 return SCPE_INCOMP;
2283 }
2284 uptr->capac = high; /* new memory size */
2285 printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno);
2286 fprint_capac (stdout, dptr, uptr);
2287 printf ("\n");
2288 }
2289 sz = SZ_D (dptr); /* allocate buffer */
2290 if ((mbuf = calloc (SRBSIZ, sz)) == NULL)
2291 return SCPE_MEM;
2292 for (k = 0; k < high; ) { /* loop thru mem */
2293 READ_I (blkcnt); /* block count */
2294 if (blkcnt < 0) limit = -blkcnt; /* compressed? */
2295 else limit = sim_fread (mbuf, sz, blkcnt, rfile);
2296 if (limit <= 0) return SCPE_IOERR; /* invalid or err? */
2297 for (j = 0; j < limit; j++, k = k + (dptr->aincr)) {
2298 if (blkcnt < 0) val = 0; /* compressed? */
2299 else SZ_LOAD (sz, val, mbuf, j); /* saved value */
2300 r = dptr->deposit (val, k, uptr, SIM_SW_REST);
2301 if (r != SCPE_OK) return r;
2302 } /* end for j */
2303 } /* end for k */
2304 free (mbuf); /* dealloc buffer */
2305 } /* end if high */
2306 } /* end unit loop */
2307 for ( ;; ) { /* register loop */
2308 READ_S (buf); /* read reg name */
2309 if (buf[0] == 0) break; /* last? */
2310 READ_I (depth); /* [V2.10+] depth */
2311 if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {
2312 printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf);
2313 for (us = 0; us < depth; us++) { /* skip values */
2314 READ_I (val);
2315 }
2316 continue;
2317 }
2318 if (depth != rptr->depth) /* [V2.10+] mismatch? */
2319 printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n",
2320 sim_dname (dptr), buf, depth, rptr->depth);
2321 mask = width_mask[rptr->width]; /* get mask */
2322 for (us = 0; us < depth; us++) { /* loop thru values */
2323 READ_I (val); /* read value */
2324 if (val > mask) /* value ok? */
2325 printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf);
2326 else if (us < rptr->depth) /* in range? */
2327 put_rval (rptr, us, val);
2328 }
2329 }
2330 } /* end device loop */
2331 return SCPE_OK;
2332 }
2333
2334 /* Run, go, cont, step commands
2335
2336 ru[n] [new PC] reset and start simulation
2337 go [new PC] start simulation
2338 co[nt] start simulation
2339 s[tep] [step limit] start simulation for 'limit' instructions
2340 b[oot] device bootstrap from device and start simulation
2341 */
2342
2343 t_stat run_cmd (int32 flag, char *cptr)
2344 {
2345 char *tptr, gbuf[CBUFSIZE];
2346 uint32 i, j;
2347 int32 unitno;
2348 t_value pcv;
2349 t_stat r;
2350 DEVICE *dptr;
2351 UNIT *uptr;
2352 void int_handler (int signal);
2353
2354 GET_SWITCHES (cptr); /* get switches */
2355 sim_step = 0;
2356 if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */
2357 if (*cptr != 0) { /* argument? */
2358 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
2359 if (*cptr != 0) return SCPE_2MARG; /* should be end */
2360 if (sim_vm_parse_addr) /* address parser? */
2361 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
2362 else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
2363 if ((tptr == gbuf) || (*tptr != 0) || /* error? */
2364 (pcv > width_mask[sim_PC->width])) return SCPE_ARG;
2365 put_rval (sim_PC, 0, pcv);
2366 }
2367 if ((flag == RU_RUN) && /* run? */
2368 ((r = run_boot_prep ()) != SCPE_OK)) return r; /* reset sim */
2369 }
2370
2371 else if (flag == RU_STEP) { /* step */
2372 if (*cptr != 0) { /* argument? */
2373 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
2374 if (*cptr != 0) return SCPE_2MARG; /* should be end */
2375 sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
2376 if ((r != SCPE_OK) || (sim_step <= 0)) /* error? */
2377 return SCPE_ARG;
2378 }
2379 else sim_step = 1;
2380 }
2381
2382 else if (flag == RU_BOOT) { /* boot */
2383 if (*cptr == 0) return SCPE_2FARG; /* must be more */
2384 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
2385 if (*cptr != 0) return SCPE_2MARG; /* should be end */
2386 dptr = find_unit (gbuf, &uptr); /* locate unit */
2387 if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
2388 if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
2389 if (dptr->boot == NULL) return SCPE_NOFNC; /* can it boot? */
2390 if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
2391 if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */
2392 !(uptr->flags & UNIT_ATT)) return SCPE_UNATT;
2393 unitno = (int32) (uptr - dptr->units); /* recover unit# */
2394 if ((r = run_boot_prep ()) != SCPE_OK) return r; /* reset sim */
2395 if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */
2396 return r;
2397 }
2398
2399 else if (flag != RU_CONT) return SCPE_IERR; /* must be cont */
2400
2401 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */
2402 for (j = 0; j < dptr->numunits; j++) { /* seq devices */
2403 uptr = dptr->units + j;
2404 if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) ==
2405 (UNIT_ATT + UNIT_SEQ))
2406 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
2407 }
2408 }
2409 stop_cpu = 0;
2410 if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */
2411 return SCPE_SIGERR;
2412 }
2413 if (sim_ttrun () != SCPE_OK) { /* set console mode */
2414 sim_ttcmd ();
2415 return SCPE_TTYERR;
2416 }
2417 if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */
2418 sim_ttcmd ();
2419 return r;
2420 }
2421 if (sim_step) sim_activate (&sim_step_unit, sim_step); /* set step timer */
2422 sim_throt_sched (); /* set throttle */
2423 sim_is_running = 1; /* flag running */
2424 sim_brk_clract (); /* defang actions */
2425 sim_rtcn_init_all (); /* re-init clocks */
2426 r = sim_instr();
2427
2428 sim_is_running = 0; /* flag idle */
2429 sim_ttcmd (); /* restore console */
2430 signal (SIGINT, SIG_DFL); /* cancel WRU */
2431 sim_cancel (&sim_step_unit); /* cancel step timer */
2432 sim_throt_cancel (); /* cancel throttle */
2433 if (sim_clock_queue != NULL) { /* update sim time */
2434 UPDATE_SIM_TIME (sim_clock_queue->time);
2435 }
2436 else {
2437 UPDATE_SIM_TIME (noqueue_time);
2438 }
2439 if (sim_log) fflush (sim_log); /* flush console log */
2440 if (sim_deb) fflush (sim_deb); /* flush debug log */
2441 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */
2442 for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */
2443 uptr = dptr->units + j;
2444 if ((uptr->flags & UNIT_ATT) && /* attached, */
2445 !(uptr->flags & UNIT_BUF) && /* not buffered, */
2446 (uptr->fileref) && /* real file, */
2447 !(uptr->flags & UNIT_RAW) && /* not raw, */
2448 !(uptr->flags & UNIT_RO)) /* not read only? */
2449 fflush (uptr->fileref);
2450 }
2451 }
2452 #if defined (VMS)
2453 printf ("\n");
2454 #endif
2455 fprint_stopped (stdout, r); /* print msg */
2456 if (sim_log) fprint_stopped (sim_log, r); /* log if enabled */
2457 return SCPE_OK;
2458 }
2459
2460 /* Common setup for RUN or BOOT */
2461
2462 t_stat run_boot_prep (void)
2463 {
2464 sim_interval = 0; /* reset queue */
2465 sim_time = sim_rtime = 0;
2466 noqueue_time = 0;
2467 sim_clock_queue = NULL;
2468 return reset_all_p (0);
2469 }
2470
2471 /* Print stopped message */
2472
2473 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
2474 {
2475 int32 i;
2476 t_stat r = 0;
2477 t_addr k;
2478 t_value pcval;
2479
2480 if (v >= SCPE_BASE) fprintf (st, "\n%s, %s: ",
2481 scp_error_messages[v - SCPE_BASE], pc->name);
2482 else fprintf (st, "\n%s, %s: ", sim_stop_messages[v], pc->name);
2483 pcval = get_rval (pc, 0);
2484 if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, (t_addr) pcval);
2485 else fprint_val (st, pcval, pc->radix, pc->width,
2486 pc->flags & REG_FMT);
2487 if ((dptr != NULL) && (dptr->examine != NULL)) {
2488 for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
2489 for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
2490 if ((r = dptr->examine (&sim_eval[i], k, dptr->units,
2491 SWMASK ('V'))) != SCPE_OK) break;
2492 }
2493 if ((r == SCPE_OK) || (i > 0)) {
2494 fprintf (st, " (");
2495 if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
2496 fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
2497 fprintf (st, ")");
2498 }
2499 }
2500 fprintf (st, "\n");
2501 return;
2502 }
2503
2504 void fprint_stopped (FILE *st, t_stat v)
2505 {
2506 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
2507 return;
2508 }
2509
2510 /* Unit service for step timeout, originally scheduled by STEP n command
2511 Return step timeout SCP code, will cause simulation to stop */
2512
2513 t_stat step_svc (UNIT *uptr)
2514 {
2515 return SCPE_STEP;
2516 }
2517
2518 /* Cancel scheduled step service */
2519
2520 t_stat sim_cancel_step (void)
2521 {
2522 return sim_cancel (&sim_step_unit);
2523 }
2524
2525 /* Signal handler for ^C signal - set stop simulation flag */
2526
2527 void int_handler (int sig)
2528 {
2529 stop_cpu = 1;
2530 return;
2531 }
2532
2533 /* Examine/deposit commands
2534
2535 ex[amine] [modifiers] list examine
2536 de[posit] [modifiers] list val deposit
2537 ie[xamine] [modifiers] list interactive examine
2538 id[eposit] [modifiers] list interactive deposit
2539
2540 modifiers
2541 @filename output file
2542 -letter(s) switches
2543 devname'n device name and unit number
2544 [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification
2545
2546 list list of addresses and registers
2547 addr[:addr|-addr] address range
2548 ALL all addresses
2549 register[:register|-register] register range
2550 STATE all registers
2551 */
2552
2553 t_stat exdep_cmd (int32 flag, char *cptr)
2554 {
2555 char gbuf[CBUFSIZE], *gptr, *tptr;
2556 int32 opt;
2557 t_addr low, high;
2558 t_stat reason;
2559 DEVICE *tdptr;
2560 REG *lowr, *highr;
2561 FILE *ofile;
2562
2563 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */
2564 if (flag == EX_E) opt = opt | CMD_OPT_OF; /* extra for EX */
2565 cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */
2566 if (!cptr) return reason; /* error? */
2567 if (*cptr == 0) return SCPE_2FARG; /* must be more */
2568 if (sim_dfunit == NULL) return SCPE_NXUN; /* got a unit? */
2569 cptr = get_glyph (cptr, gbuf, 0); /* get list */
2570 if ((flag == EX_D) && (*cptr == 0)) return SCPE_2FARG; /* deposit needs more */
2571 ofile = sim_ofile? sim_ofile: stdout; /* no ofile? use stdout */
2572
2573 for (gptr = gbuf, reason = SCPE_OK;
2574 (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
2575 tdptr = sim_dfdev; /* working dptr */
2576 if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
2577 tptr = gptr + strlen ("STATE");
2578 if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
2579 if ((lowr = sim_dfdev->registers) == NULL) return SCPE_NXREG;
2580 for (highr = lowr; highr->name != NULL; highr++) ;
2581 sim_switches = sim_switches | SIM_SW_HIDE;
2582 reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr,
2583 lowr, --highr, 0, 0);
2584 continue;
2585 }
2586
2587 if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */
2588 (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */
2589 (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
2590 low = high = 0;
2591 if ((*tptr == '-') || (*tptr == ':')) {
2592 highr = find_reg (tptr + 1, &tptr, tdptr);
2593 if (highr == NULL) return SCPE_NXREG;
2594 }
2595 else {
2596 highr = lowr;
2597 if (*tptr == '[') {
2598 if (lowr->depth <= 1) return SCPE_ARG;
2599 tptr = get_range (NULL, tptr + 1, &low, &high,
2600 10, lowr->depth - 1, ']');
2601 if (tptr == NULL) return SCPE_ARG;
2602 }
2603 }
2604 if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
2605 reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr,
2606 lowr, highr, (uint32) low, (uint32) high);
2607 continue;
2608 }
2609
2610 tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
2611 (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
2612 sim_dfunit->capac - sim_dfdev->aincr), 0);
2613 if (tptr == NULL) return SCPE_ARG;
2614 if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
2615 reason = exdep_addr_loop (ofile, sim_schptr, flag, cptr, low, high,
2616 sim_dfdev, sim_dfunit);
2617 } /* end for */
2618 if (sim_ofile) fclose (sim_ofile); /* close output file */
2619 return reason;
2620 }
2621
2622 /* Loop controllers for examine/deposit
2623
2624 exdep_reg_loop examine/deposit range of registers
2625 exdep_addr_loop examine/deposit range of addresses
2626 */
2627
2628 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,
2629 REG *lowr, REG *highr, uint32 lows, uint32 highs)
2630 {
2631 t_stat reason;
2632 uint32 idx;
2633 t_value val;
2634 REG *rptr;
2635
2636 if ((lowr == NULL) || (highr == NULL)) return SCPE_IERR;
2637 if (lowr > highr) return SCPE_ARG;
2638 for (rptr = lowr; rptr <= highr; rptr++) {
2639 if ((sim_switches & SIM_SW_HIDE) &&
2640 (rptr->flags & REG_HIDDEN)) continue;
2641 for (idx = lows; idx <= highs; idx++) {
2642 if (idx >= rptr->depth) return SCPE_SUB;
2643 val = get_rval (rptr, idx);
2644 if (schptr && !test_search (val, schptr)) continue;
2645 if (flag != EX_D) {
2646 reason = ex_reg (ofile, val, flag, rptr, idx);
2647 if (reason != SCPE_OK) return reason;
2648 if (sim_log && (ofile == stdout))
2649 ex_reg (sim_log, val, flag, rptr, idx);
2650 }
2651 if (flag != EX_E) {
2652 reason = dep_reg (flag, cptr, rptr, idx);
2653 if (reason != SCPE_OK) return reason;
2654 }
2655 }
2656 }
2657 return SCPE_OK;
2658 }
2659
2660 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,
2661 t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
2662 {
2663 t_addr i, mask;
2664 t_stat reason;
2665
2666 if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
2667 mask = (t_addr) width_mask[dptr->awidth];
2668 if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG;
2669 for (i = low; i <= high; ) { /* all paths must incr!! */
2670 reason = get_aval (i, dptr, uptr); /* get data */
2671 if (reason != SCPE_OK) return reason; /* return if error */
2672 if (schptr && !test_search (sim_eval[0], schptr))
2673 i = i + dptr->aincr; /* sch fails, incr */
2674 else { /* no sch or success */
2675 if (flag != EX_D) { /* ex, ie, or id? */
2676 reason = ex_addr (ofile, flag, i, dptr, uptr);
2677 if (reason > SCPE_OK) return reason;
2678 if (sim_log && (ofile == stdout))
2679 ex_addr (sim_log, flag, i, dptr, uptr);
2680 }
2681 else reason = 1 - dptr->aincr; /* no, dflt incr */
2682 if (flag != EX_E) { /* ie, id, or d? */
2683 reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
2684 if (reason > SCPE_OK) return reason;
2685 }
2686 i = i + (1 - reason); /* incr */
2687 }
2688 }
2689 return SCPE_OK;
2690 }
2691
2692 /* Examine register routine
2693
2694 Inputs:
2695 ofile = output stream
2696 val = current register value
2697 flag = type of ex/mod command (ex, iex, idep)
2698 rptr = pointer to register descriptor
2699 idx = index
2700 Outputs:
2701 return = error status
2702 */
2703
2704 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
2705 {
2706 int32 rdx;
2707
2708 if (rptr == NULL) return SCPE_IERR;
2709 if (rptr->depth > 1) fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
2710 else fprintf (ofile, "%s:\t", rptr->name);
2711 if (!(flag & EX_E)) return SCPE_OK;
2712 GET_RADIX (rdx, rptr->radix);
2713 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr)
2714 sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
2715 else if (!(rptr->flags & REG_VMIO) ||
2716 (fprint_sym (ofile, rdx, &val, NULL, sim_switches | SIM_SW_REG) > 0))
2717 fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
2718 if (flag & EX_I) fprintf (ofile, "\t");
2719 else fprintf (ofile, "\n");
2720 return SCPE_OK;
2721 }
2722
2723 /* Get register value
2724
2725 Inputs:
2726 rptr = pointer to register descriptor
2727 idx = index
2728 Outputs:
2729 return = register value
2730 */
2731
2732 t_value get_rval (REG *rptr, uint32 idx)
2733 {
2734 size_t sz;
2735 t_value val;
2736 UNIT *uptr;
2737
2738 sz = SZ_R (rptr);
2739 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
2740 idx = idx + rptr->qptr;
2741 if (idx >= rptr->depth) idx = idx - rptr->depth;
2742 }
2743 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
2744 uptr = ((UNIT *) rptr->loc) + idx;
2745 #if defined (USE_INT64)
2746 if (sz <= sizeof (uint32)) val = *((uint32 *) uptr);
2747 else val = *((t_uint64 *) uptr);
2748 #else
2749 val = *((uint32 *) uptr);
2750 #endif
2751 }
2752 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
2753 (sz == sizeof (uint8)))
2754 val = *(((uint8 *) rptr->loc) + idx);
2755 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
2756 (sz == sizeof (uint16)))
2757 val = *(((uint16 *) rptr->loc) + idx);
2758 #if defined (USE_INT64)
2759 else if (sz <= sizeof (uint32))
2760 val = *(((uint32 *) rptr->loc) + idx);
2761 else val = *(((t_uint64 *) rptr->loc) + idx);
2762 #else
2763 else val = *(((uint32 *) rptr->loc) + idx);
2764 #endif
2765 val = (val >> rptr->offset) & width_mask[rptr->width];
2766 return val;
2767 }
2768
2769 /* Deposit register routine
2770
2771 Inputs:
2772 flag = type of deposit (normal/interactive)
2773 cptr = pointer to input string
2774 rptr = pointer to register descriptor
2775 idx = index
2776 Outputs:
2777 return = error status
2778 */
2779
2780 t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx)
2781 {
2782 t_stat r;
2783 t_value val, mask;
2784 int32 rdx;
2785 char *tptr, gbuf[CBUFSIZE];
2786
2787 if ((cptr == NULL) || (rptr == NULL)) return SCPE_IERR;
2788 if (rptr->flags & REG_RO) return SCPE_RO;
2789 if (flag & EX_I) {
2790 cptr = read_line (gbuf, CBUFSIZE, stdin);
2791 if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);
2792 if (cptr == NULL) return 1; /* force exit */
2793 if (*cptr == 0) return SCPE_OK; /* success */
2794 }
2795 mask = width_mask[rptr->width];
2796 GET_RADIX (rdx, rptr->radix);
2797 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr) { /* address form? */
2798 val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
2799 if ((tptr == cptr) || (*tptr != 0) ||
2800 (val > mask)) return SCPE_ARG;
2801 }
2802 else if (!(rptr->flags & REG_VMIO) || /* dont use sym? */
2803 (parse_sym (cptr, rdx, NULL, &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
2804 val = get_uint (cptr, rdx, mask, &r);
2805 if (r != SCPE_OK) return SCPE_ARG;
2806 }
2807 if ((rptr->flags & REG_NZ) && (val == 0)) return SCPE_ARG;
2808 put_rval (rptr, idx, val);
2809 return SCPE_OK;
2810 }
2811
2812 /* Put register value
2813
2814 Inputs:
2815 rptr = pointer to register descriptor
2816 idx = index
2817 val = new value
2818 mask = mask
2819 Outputs:
2820 none
2821 */
2822
2823 void put_rval (REG *rptr, uint32 idx, t_value val)
2824 {
2825 size_t sz;
2826 t_value mask;
2827 UNIT *uptr;
2828
2829 #define PUT_RVAL(sz,rp,id,v,m) \
2830 *(((sz *) rp->loc) + id) = \
2831 (*(((sz *) rp->loc) + id) & \
2832 ~((m) << (rp)->offset)) | ((v) << (rp)->offset)
2833
2834 if (rptr == sim_PC) sim_brk_npc (0);
2835 sz = SZ_R (rptr);
2836 mask = width_mask[rptr->width];
2837 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
2838 idx = idx + rptr->qptr;
2839 if (idx >= rptr->depth) idx = idx - rptr->depth;
2840 }
2841 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
2842 uptr = ((UNIT *) rptr->loc) + idx;
2843 #if defined (USE_INT64)
2844 if (sz <= sizeof (uint32))
2845 *((uint32 *) uptr) = (*((uint32 *) uptr) &
2846 ~(((uint32) mask) << rptr->offset)) |
2847 (((uint32) val) << rptr->offset);
2848 else *((t_uint64 *) uptr) = (*((t_uint64 *) uptr)
2849 & ~(mask << rptr->offset)) | (val << rptr->offset);
2850 #else
2851 *((uint32 *) uptr) = (*((uint32 *) uptr) &
2852 ~(((uint32) mask) << rptr->offset)) |
2853 (((uint32) val) << rptr->offset);
2854 #endif
2855 }
2856 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
2857 (sz == sizeof (uint8)))
2858 PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
2859 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
2860 (sz == sizeof (uint16)))
2861 PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
2862 #if defined (USE_INT64)
2863 else if (sz <= sizeof (uint32))
2864 PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
2865 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
2866 #else
2867 else PUT_RVAL (uint32, rptr, idx, val, mask);
2868 #endif
2869 return;
2870 }
2871
2872 /* Examine address routine
2873
2874 Inputs: (sim_eval is an implicit argument)
2875 ofile = output stream
2876 flag = type of ex/mod command (ex, iex, idep)
2877 addr = address to examine
2878 dptr = pointer to device
2879 uptr = pointer to unit
2880 Outputs:
2881 return = if > 0, error status
2882 if <= 0,-number of extra addr units retired
2883 */
2884
2885 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
2886 {
2887 t_stat reason;
2888 int32 rdx;
2889
2890 if (sim_vm_fprint_addr) sim_vm_fprint_addr (ofile, dptr, addr);
2891 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
2892 fprintf (ofile, ":\t");
2893 if (!(flag & EX_E)) return (1 - dptr->aincr);
2894
2895 GET_RADIX (rdx, dptr->dradix);
2896 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
2897 fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
2898 reason = 1 - dptr->aincr;
2899 }
2900 if (flag & EX_I) fprintf (ofile, "\t");
2901 else fprintf (ofile, "\n");
2902 return reason;
2903 }
2904
2905 /* Get address routine
2906
2907 Inputs:
2908 flag = type of ex/mod command (ex, iex, idep)
2909 addr = address to examine
2910 dptr = pointer to device
2911 uptr = pointer to unit
2912 Outputs: (sim_eval is an implicit output)
2913 return = error status
2914 */
2915
2916 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
2917 {
2918 int32 i;
2919 t_value mask;
2920 t_addr j, loc;
2921 size_t sz;
2922 t_stat reason = SCPE_OK;
2923
2924 if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;
2925 mask = width_mask[dptr->dwidth];
2926 for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
2927 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
2928 if (dptr->examine != NULL) {
2929 reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
2930 if (reason != SCPE_OK) break;
2931 }
2932 else {
2933 if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;
2934 if (uptr->flags & UNIT_RAW) return SCPE_NOFNC;
2935 if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
2936 reason = SCPE_NXM;
2937 break;
2938 }
2939 sz = SZ_D (dptr);
2940 loc = j / dptr->aincr;
2941 if (uptr->flags & UNIT_BUF) {
2942 SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
2943 }
2944 else {
2945 sim_fseek (uptr->fileref, sz * loc, SEEK_SET);
2946 sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
2947 if ((feof (uptr->fileref)) &&
2948 !(uptr->flags & UNIT_FIX)) {
2949 reason = SCPE_EOF;
2950 break;
2951 }
2952 else if (ferror (uptr->fileref)) {
2953 clearerr (uptr->fileref);
2954 reason = SCPE_IOERR;
2955 break;
2956 }
2957 }
2958 }
2959 sim_eval[i] = sim_eval[i] & mask;
2960 }
2961 if ((reason != SCPE_OK) && (i == 0)) return reason;
2962 return SCPE_OK;
2963 }
2964
2965 /* Deposit address routine
2966
2967 Inputs:
2968 flag = type of deposit (normal/interactive)
2969 cptr = pointer to input string
2970 addr = address to examine
2971 dptr = pointer to device
2972 uptr = pointer to unit
2973 dfltinc = value to return on cr input
2974 Outputs:
2975 return = if > 0, error status
2976 if <= 0, -number of extra address units retired
2977 */
2978
2979 t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr,
2980 UNIT *uptr, int32 dfltinc)
2981 {
2982 int32 i, count, rdx;
2983 t_addr j, loc;
2984 t_stat r, reason;
2985 t_value mask;
2986 size_t sz;
2987 char gbuf[CBUFSIZE];
2988
2989 if (dptr == NULL) return SCPE_IERR;
2990 if (flag & EX_I) {
2991 cptr = read_line (gbuf, CBUFSIZE, stdin);
2992 if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);
2993 if (cptr == NULL) return 1; /* force exit */
2994 if (*cptr == 0) return dfltinc; /* success */
2995 }
2996 if (uptr->flags & UNIT_RO) return SCPE_RO; /* read only? */
2997 mask = width_mask[dptr->dwidth];
2998
2999 GET_RADIX (rdx, dptr->dradix);
3000 if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
3001 sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
3002 if (reason != SCPE_OK) return reason;
3003 }
3004 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
3005
3006 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
3007 sim_eval[i] = sim_eval[i] & mask;
3008 if (dptr->deposit != NULL) {
3009 r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
3010 if (r != SCPE_OK) return r;
3011 }
3012 else {
3013 if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;
3014 if (uptr->flags & UNIT_RAW) return SCPE_NOFNC;
3015 if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
3016 return SCPE_NXM;
3017 sz = SZ_D (dptr);
3018 loc = j / dptr->aincr;
3019 if (uptr->flags & UNIT_BUF) {
3020 SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
3021 if (loc >= uptr->hwmark) uptr->hwmark = (uint32) loc + 1;
3022 }
3023 else {
3024 sim_fseek (uptr->fileref, sz * loc, SEEK_SET);
3025 sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
3026 if (ferror (uptr->fileref)) {
3027 clearerr (uptr->fileref);
3028 return SCPE_IOERR;
3029 }
3030 }
3031 }
3032 }
3033 return reason;
3034 }
3035
3036 /* Evaluate command */
3037
3038 t_stat eval_cmd (int32 flg, char *cptr)
3039 {
3040 DEVICE *dptr = sim_dflt_dev;
3041 int32 i, rdx, a, lim;
3042 t_stat r;
3043
3044 GET_SWITCHES (cptr);
3045 GET_RADIX (rdx, dptr->dradix);
3046 for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
3047 if (*cptr == 0) return SCPE_2FARG;
3048 if ((r = parse_sym (cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
3049 sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
3050 if (r != SCPE_OK) return r;
3051 }
3052 lim = 1 - r;
3053 for (i = a = 0; a < lim; ) {
3054 printf ("%d:\t", a);
3055 if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
3056 r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
3057 printf ("\n");
3058 if (sim_log) {
3059 fprintf (sim_log, "%d\t", i);
3060 if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
3061 r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
3062 fprintf (sim_log, "\n");
3063 }
3064 if (r < 0) a = a + 1 - r;
3065 else a = a + dptr->aincr;
3066 i = a / dptr->aincr;
3067 }
3068 return SCPE_OK;
3069 }
3070
3071 /* String processing routines
3072
3073 read_line read line
3074
3075 Inputs:
3076 cptr = pointer to buffer
3077 size = maximum size
3078 stream = pointer to input stream
3079 Outputs:
3080 optr = pointer to first non-blank character
3081 NULL if EOF
3082 */
3083
3084 char *read_line (char *cptr, int32 size, FILE *stream)
3085 {
3086 char *tptr;
3087
3088 cptr = fgets (cptr, size, stream); /* get cmd line */
3089 if (cptr == NULL) {
3090 clearerr (stream); /* clear error */
3091 return NULL; /* ignore EOF */
3092 }
3093 for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */
3094 if ((*tptr == '\n') || (*tptr == '\r') ||
3095 (tptr == (cptr + size - 1))) { /* str max length? */
3096 *tptr = 0; /* terminate */
3097 break;
3098 }
3099 }
3100 while (isspace (*cptr)) cptr++; /* trim leading spc */
3101 if (*cptr == ';') *cptr = 0; /* ignore comment */
3102
3103
3104 return cptr;
3105 }
3106
3107 /* get_glyph get next glyph (force upper case)
3108 get_glyph_nc get next glyph (no conversion)
3109 get_glyph_gen get next glyph (general case)
3110
3111 Inputs:
3112 iptr = pointer to input string
3113 optr = pointer to output string
3114 mchar = optional end of glyph character
3115 flag = TRUE for convert to upper case (_gen only)
3116 Outputs
3117 result = pointer to next character in input string
3118 */
3119
3120 char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc)
3121 {
3122 while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) {
3123 if (islower (*iptr) && uc) *optr = toupper (*iptr);
3124 else *optr = *iptr;
3125 iptr++; optr++;
3126 }
3127 *optr = 0;
3128 if (mchar && (*iptr == mchar)) iptr++; /* skip terminator */
3129 while (isspace (*iptr)) iptr++; /* absorb spaces */
3130 return iptr;
3131 }
3132
3133 char *get_glyph (char *iptr, char *optr, char mchar)
3134 {
3135 return get_glyph_gen (iptr, optr, mchar, TRUE);
3136 }
3137
3138 char *get_glyph_nc (char *iptr, char *optr, char mchar)
3139 {
3140 return get_glyph_gen (iptr, optr, mchar, FALSE);
3141 }
3142
3143 /* Trim trailing spaces from a string
3144
3145 Inputs:
3146 cptr = pointer to string
3147 Outputs:
3148 cptr = pointer to string
3149 */
3150
3151 char *sim_trim_endspc (char *cptr)
3152 {
3153 char *tptr;
3154
3155 tptr = cptr + strlen (cptr);
3156 while ((--tptr >= cptr) && isspace (*tptr)) *tptr = 0;
3157 return cptr;
3158 }
3159
3160 /* get_yn yes/no question
3161
3162 Inputs:
3163 cptr = pointer to question
3164 deflt = default answer
3165 Outputs:
3166 result = true if yes, false if no
3167 */
3168
3169 t_stat get_yn (char *ques, t_stat deflt)
3170 {
3171 char cbuf[CBUFSIZE], *cptr;
3172
3173 printf ("%s ", ques);
3174 cptr = read_line (cbuf, CBUFSIZE, stdin);
3175 if ((cptr == NULL) || (*cptr == 0)) return deflt;
3176 if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE;
3177 return FALSE;
3178 }
3179
3180 /* get_uint unsigned number
3181
3182 Inputs:
3183 cptr = pointer to input string
3184 radix = input radix
3185 max = maximum acceptable value
3186 *status = pointer to error status
3187 Outputs:
3188 val = value
3189 */
3190
3191 t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status)
3192 {
3193 t_value val;
3194 char *tptr;
3195
3196 *status = SCPE_OK;
3197 val = strtotv (cptr, &tptr, radix);
3198 if ((cptr == tptr) || (val > max)) *status = SCPE_ARG;
3199 else {
3200 while (isspace (*tptr)) tptr++;
3201 if (*tptr != 0) *status = SCPE_ARG;
3202 }
3203 return val;
3204 }
3205
3206 /* get_range range specification
3207
3208 Inputs:
3209 dptr = pointer to device (NULL if none)
3210 cptr = pointer to input string
3211 *lo = pointer to low result
3212 *hi = pointer to high result
3213 aradix = radix
3214 max = default high value
3215 term = terminating character, 0 if none
3216 Outputs:
3217 tptr = input pointer after processing
3218 NULL if error
3219 */
3220
3221 char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi,
3222 uint32 rdx, t_addr max, char term)
3223 {
3224 char *tptr;
3225
3226 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */
3227 tptr = cptr + strlen ("ALL");
3228 *lo = 0;
3229 *hi = max;
3230 }
3231 else {
3232 if (dptr && sim_vm_parse_addr) /* get low */
3233 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
3234 else *lo = (t_addr) strtotv (cptr, &tptr, rdx);
3235 if (cptr == tptr) return NULL; /* error? */
3236 if ((*tptr == '-') || (*tptr == ':')) { /* range? */
3237 cptr = tptr + 1;
3238 if (dptr && sim_vm_parse_addr) /* get high */
3239 *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
3240 else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
3241 if (cptr == tptr) return NULL;
3242 if (*lo > *hi) return NULL;
3243 }
3244 else if (*tptr == '/') { /* relative? */
3245 cptr = tptr + 1;
3246 *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */
3247 if ((cptr == tptr) || (*hi == 0)) return NULL;
3248 *hi = *lo + *hi - 1;
3249 }
3250 else *hi = *lo;
3251 }
3252 if (term && (*tptr++ != term)) return NULL;
3253 return tptr;
3254 }
3255
3256 /* get_ipaddr IP address:port
3257
3258 Inputs:
3259 cptr = pointer to input string
3260 Outputs:
3261 ipa = pointer to IP address (may be NULL), 0 = none
3262 ipp = pointer to IP port (may be NULL), 0 = none
3263 result = status
3264 */
3265
3266 t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp)
3267 {
3268 char gbuf[CBUFSIZE];
3269 char *addrp, *portp, *octetp;
3270 uint32 i, addr, port, octet;
3271 t_stat r;
3272
3273 if ((cptr == NULL) || (*cptr == 0))
3274 return SCPE_ARG;
3275 strncpy (gbuf, cptr, CBUFSIZE);
3276 addrp = gbuf; /* default addr */
3277 if (portp = strchr (gbuf, ':')) *portp++ = 0; /* x:y? split */
3278 else if (strchr (gbuf, '.')) portp = NULL; /* x.y...? */
3279 else {
3280 portp = gbuf; /* port only */
3281 addrp = NULL; /* no addr */
3282 }
3283 if (portp) { /* port string? */
3284 if (ipp == NULL) return SCPE_ARG; /* not wanted? */
3285 port = (int32) get_uint (portp, 10, 65535, &r);
3286 if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG;
3287 }
3288 else port = 0;
3289 if (addrp) { /* addr string? */
3290 if (ipa == NULL) return SCPE_ARG; /* not wanted? */
3291 for (i = addr = 0; i < 4; i++) { /* four octets */
3292 octetp = strchr (addrp, '.'); /* find octet end */
3293 if (octetp != NULL) *octetp++ = 0; /* split string */
3294 else if (i < 3) return SCPE_ARG; /* except last */
3295 octet = (int32) get_uint (addrp, 10, 255, &r);
3296 if (r != SCPE_OK) return SCPE_ARG;
3297 addr = (addr << 8) | octet;
3298 addrp = octetp;
3299 }
3300 if (((addr & 0377) == 0) || ((addr & 0377) == 255))
3301 return SCPE_ARG;
3302 }
3303 else addr = 0;
3304 if (ipp) *ipp = port; /* return req values */
3305 if (ipa) *ipa = addr;
3306 return SCPE_OK;
3307 }
3308
3309 /* Find_device find device matching input string
3310
3311 Inputs:
3312 cptr = pointer to input string
3313 Outputs:
3314 result = pointer to device
3315 */
3316
3317 DEVICE *find_dev (char *cptr)
3318 {
3319 int32 i;
3320 DEVICE *dptr;
3321
3322 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
3323 if ((strcmp (cptr, dptr->name) == 0) ||
3324 (dptr->lname &&
3325 (strcmp (cptr, dptr->lname) == 0))) return dptr;
3326 }
3327 return NULL;
3328 }
3329
3330 /* Find_unit find unit matching input string
3331
3332 Inputs:
3333 cptr = pointer to input string
3334 uptr = pointer to unit pointer
3335 Outputs:
3336 result = pointer to device (null if no dev)
3337 *iptr = pointer to unit (null if nx unit)
3338 */
3339
3340 DEVICE *find_unit (char *cptr, UNIT **uptr)
3341 {
3342 uint32 i, u;
3343 char *nptr, *tptr;
3344 t_stat r;
3345 DEVICE *dptr;
3346
3347 if (uptr == NULL) return NULL; /* arg error? */
3348 if (dptr = find_dev (cptr)) { /* exact match? */
3349 if (qdisable (dptr)) return NULL; /* disabled? */
3350 *uptr = dptr->units; /* unit 0 */
3351 return dptr;
3352 }
3353
3354 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */
3355 if (dptr->numunits && /* any units? */
3356 (((nptr = dptr->name) &&
3357 (strncmp (cptr, nptr, strlen (nptr)) == 0)) ||
3358 ((nptr = dptr->lname) &&
3359 (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
3360 tptr = cptr + strlen (nptr);
3361 if (isdigit (*tptr)) {
3362 if (qdisable (dptr)) return NULL; /* disabled? */
3363 u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
3364 if (r != SCPE_OK) *uptr = NULL; /* error? */
3365 else *uptr = dptr->units + u;
3366 return dptr;
3367 }
3368 }
3369 }
3370 return NULL;
3371 }
3372
3373 /* Find_dev_from_unit find device for unit
3374
3375 Inputs:
3376 uptr = pointer to unit
3377 Outputs:
3378 result = pointer to device
3379 */
3380
3381 DEVICE *find_dev_from_unit (UNIT *uptr)
3382 {
3383 DEVICE *dptr;
3384 uint32 i, j;
3385
3386 if (uptr == NULL) return NULL;
3387 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
3388 for (j = 0; j < dptr->numunits; j++) {
3389 if (uptr == (dptr->units + j)) return dptr;
3390 }
3391 }
3392 return NULL;
3393 }
3394
3395 /* Test for disabled device */
3396
3397 t_bool qdisable (DEVICE *dptr)
3398 {
3399 return (dptr->flags & DEV_DIS? TRUE: FALSE);
3400 }
3401
3402 /* find_reg_glob find globally unique register
3403
3404 Inputs:
3405 cptr = pointer to input string
3406 optr = pointer to output pointer (can be null)
3407 gdptr = pointer to global device
3408 Outputs:
3409 result = pointer to register, NULL if error
3410 *optr = pointer to next character in input string
3411 *gdptr = pointer to device where found
3412 */
3413
3414 REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr)
3415 {
3416 int32 i;
3417 DEVICE *dptr;
3418 REG *rptr, *srptr = NULL;
3419
3420 for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */
3421 if (dptr->flags & DEV_DIS) continue; /* skip disabled */
3422 if (rptr = find_reg (cptr, optr, dptr)) { /* found? */
3423 if (srptr) return NULL; /* ambig? err */
3424 srptr = rptr; /* save reg */
3425 *gdptr = dptr; /* save unit */
3426 }
3427 }
3428 return srptr;
3429 }
3430
3431 /* find_reg find register matching input string
3432
3433 Inputs:
3434 cptr = pointer to input string
3435 optr = pointer to output pointer (can be null)
3436 dptr = pointer to device
3437 Outputs:
3438 result = pointer to register, NULL if error
3439 *optr = pointer to next character in input string
3440 */
3441
3442 REG *find_reg (char *cptr, char **optr, DEVICE *dptr)
3443 {
3444 char *tptr;
3445 REG *rptr;
3446 uint32 slnt;
3447
3448 if ((cptr == NULL) || (dptr == NULL) ||
3449 (dptr->registers == NULL)) return NULL;
3450 tptr = cptr;
3451 do {
3452 tptr++;
3453 } while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_'));
3454 slnt = tptr - cptr;
3455 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
3456 if ((slnt == strlen (rptr->name)) &&
3457 (strncmp (cptr, rptr->name, slnt) == 0)) {
3458 if (optr != NULL) *optr = tptr;
3459 return rptr;
3460 }
3461 }
3462 return NULL;
3463 }
3464
3465 /* get_switches get switches from input string
3466
3467 Inputs:
3468 cptr = pointer to input string
3469 Outputs:
3470 sw = switch bit mask
3471 0 if no switches, -1 if error
3472 */
3473
3474 int32 get_switches (char *cptr)
3475 {
3476 int32 sw;
3477
3478 if (*cptr != '-') return 0;
3479 sw = 0;
3480 for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
3481 if (isalpha (*cptr) == 0) return -1;
3482 sw = sw | SWMASK (toupper (*cptr));
3483 }
3484 return sw;
3485 }
3486
3487 /* get_sim_sw accumulate sim_switches
3488
3489 Inputs:
3490 cptr = pointer to input string
3491 Outputs:
3492 ptr = pointer to first non-string glyph
3493 NULL if error
3494 */
3495
3496 char *get_sim_sw (char *cptr)
3497 {
3498 int32 lsw;
3499 char gbuf[CBUFSIZE];
3500
3501 while (*cptr == '-') { /* while switches */
3502 cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */
3503 lsw = get_switches (gbuf); /* parse */
3504 if (lsw <= 0) return NULL; /* invalid? */
3505 sim_switches = sim_switches | lsw; /* accumulate */
3506 }
3507 return cptr;
3508 }
3509
3510 /* get_sim_opt get simulator command options
3511
3512 Inputs:
3513 opt = command options
3514 cptr = pointer to input string
3515 Outputs:
3516 ptr = pointer to next glypsh, NULL if error
3517 *stat = error status
3518 */
3519
3520 char *get_sim_opt (int32 opt, char *cptr, t_stat *st)
3521 {
3522 int32 t;
3523 char *svptr, gbuf[CBUFSIZE];
3524 DEVICE *tdptr;
3525 UNIT *tuptr;
3526
3527 sim_switches = 0; /* no switches */
3528 sim_ofile = NULL; /* no output file */
3529 sim_schptr = NULL; /* no search */
3530 sim_stab.logic = SCH_OR; /* default search params */
3531 sim_stab.boolop = SCH_GE;
3532 sim_stab.mask = 0;
3533 sim_stab.comp = 0;
3534 sim_dfdev = sim_dflt_dev;
3535 sim_dfunit = sim_dfdev->units;
3536 sim_opt_out = 0; /* no options yet */
3537 *st = SCPE_OK;
3538 while (*cptr) { /* loop through modifiers */
3539 svptr = cptr; /* save current position */
3540 if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */
3541 if (sim_ofile) { /* already got one? */
3542 fclose (sim_ofile); /* one per customer */
3543 *st = SCPE_ARG;
3544 return NULL;
3545 }
3546 cptr = get_glyph_nc (cptr + 1, gbuf, 0);
3547 sim_ofile = sim_fopen (gbuf, "a"); /* open for append */
3548 if (sim_ofile == NULL) { /* open failed? */
3549 *st = SCPE_OPENERR;
3550 return NULL;
3551 }
3552 sim_opt_out |= CMD_OPT_OF; /* got output file */
3553 continue;
3554 }
3555 cptr = get_glyph (cptr, gbuf, 0);
3556 if ((t = get_switches (gbuf)) != 0) { /* try for switches */
3557 if (t < 0) { /* err if bad switch */
3558 *st = SCPE_INVSW;
3559 return NULL;
3560 }
3561 sim_switches = sim_switches | t; /* or in new switches */
3562 }
3563 else if ((opt & CMD_OPT_SCH) && /* if allowed, */
3564 get_search (gbuf, sim_dfdev->dradix, &sim_stab)) { /* try for search */
3565 sim_schptr = &sim_stab; /* set search */
3566 sim_opt_out |= CMD_OPT_SCH; /* got search */
3567 }
3568 else if ((opt & CMD_OPT_DFT) && /* default allowed? */
3569 ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */
3570 (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */
3571 (tuptr != NULL)) {
3572 sim_dfdev = tdptr; /* set as default */
3573 sim_dfunit = tuptr;
3574 sim_opt_out |= CMD_OPT_DFT; /* got default */
3575 }
3576 else return svptr; /* not rec, break out */
3577 }
3578 return cptr;
3579 }
3580
3581 /* Match file extension
3582
3583 Inputs:
3584 fnam = file name
3585 ext = extension, without period
3586 Outputs:
3587 cp = pointer to final '.' if match, NULL if not
3588 */
3589
3590 char *match_ext (char *fnam, char *ext)
3591 {
3592 char *pptr, *fptr, *eptr;
3593
3594 if ((fnam == NULL) || (ext == NULL)) /* bad arguments? */
3595 return NULL;
3596 pptr = strrchr (fnam, '.'); /* find last . */
3597 if (pptr) { /* any? */
3598 for (fptr = pptr + 1, eptr = ext; /* match characters */
3599 #if defined (VMS) /* VMS: stop at ; or null */
3600 (*fptr != 0) && (*fptr != ';');
3601 #else
3602 *fptr != 0; /* others: stop at null */
3603 #endif
3604 fptr++, eptr++) {
3605 if (toupper (*fptr) != toupper (*eptr))
3606 return NULL;
3607 }
3608 if (*eptr != 0) return NULL; /* ext exhausted? */
3609 }
3610 return pptr;
3611 }
3612
3613 /* Get search specification
3614
3615 Inputs:
3616 cptr = pointer to input string
3617 radix = radix for numbers
3618 schptr = pointer to search table
3619 Outputs:
3620 return = NULL if error
3621 schptr if valid search specification
3622 */
3623
3624 SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr)
3625 {
3626 int32 c, logop, cmpop;
3627 t_value logval, cmpval;
3628 char *sptr, *tptr;
3629 const char logstr[] = "|&^", cmpstr[] = "=!><";
3630
3631 logval = cmpval = 0;
3632 if (*cptr == 0) return NULL; /* check for clause */
3633 for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */
3634 if (sptr = strchr (logstr, c)) { /* check for mask */
3635 logop = sptr - logstr;
3636 logval = strtotv (cptr, &tptr, radix);
3637 if (cptr == tptr) return NULL;
3638 cptr = tptr;
3639 }
3640 else if (sptr = strchr (cmpstr, c)) { /* check for boolop */
3641 cmpop = sptr - cmpstr;
3642 if (*cptr == '=') {
3643 cmpop = cmpop + strlen (cmpstr);
3644 cptr++;
3645 }
3646 cmpval = strtotv (cptr, &tptr, radix);
3647 if (cptr == tptr) return NULL;
3648 cptr = tptr;
3649 }
3650 else return NULL;
3651 } /* end for */
3652 if (logop >= 0) {
3653 schptr->logic = logop;
3654 schptr->mask = logval;
3655 }
3656 if (cmpop >= 0) {
3657 schptr->boolop = cmpop;
3658 schptr->comp = cmpval;
3659 }
3660 return schptr;
3661 }
3662
3663 /* Test value against search specification
3664
3665 Inputs:
3666 val = value to test
3667 schptr = pointer to search table
3668 Outputs:
3669 return = 1 if value passes search criteria, 0 if not
3670 */
3671
3672 int32 test_search (t_value val, SCHTAB *schptr)
3673 {
3674 if (schptr == NULL) return 0;
3675
3676 switch (schptr->logic) { /* case on logical */
3677
3678 case SCH_OR:
3679 val = val | schptr->mask;
3680 break;
3681
3682 case SCH_AND:
3683 val = val & schptr->mask;
3684 break;
3685
3686 case SCH_XOR:
3687 val = val ^ schptr->mask;
3688 break;
3689 }
3690
3691 switch (schptr->boolop) { /* case on comparison */
3692
3693 case SCH_E: case SCH_EE:
3694 return (val == schptr->comp);
3695
3696 case SCH_N: case SCH_NE:
3697 return (val != schptr->comp);
3698
3699 case SCH_G:
3700 return (val > schptr->comp);
3701
3702 case SCH_GE:
3703 return (val >= schptr->comp);
3704
3705 case SCH_L:
3706 return (val < schptr->comp);
3707
3708 case SCH_LE:
3709 return (val <= schptr->comp);
3710 }
3711
3712 return 0;
3713 }
3714
3715 /* Radix independent input/output package
3716
3717 strtotv - general radix input routine
3718
3719 Inputs:
3720 inptr = string to convert
3721 endptr = pointer to first unconverted character
3722 radix = radix for input
3723 Outputs:
3724 value = converted value
3725
3726 On an error, the endptr will equal the inptr.
3727 */
3728
3729 t_value strtotv (char *inptr, char **endptr, uint32 radix)
3730 {
3731 int32 nodigit;
3732 t_value val;
3733 uint32 c, digit;
3734
3735 *endptr = inptr; /* assume fails */
3736 if ((radix < 2) || (radix > 36)) return 0;
3737 while (isspace (*inptr)) inptr++; /* bypass white space */
3738 val = 0;
3739 nodigit = 1;
3740 for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */
3741 if (islower (c)) c = toupper (c);
3742 if (isdigit (c)) digit = c - (uint32) '0'; /* digit? */
3743 else if (radix <= 10) break; /* stop if not expected */
3744 else digit = c + 10 - (uint32) 'A'; /* convert letter */
3745 if (digit >= radix) return 0; /* valid in radix? */
3746 val = (val * radix) + digit; /* add to value */
3747 nodigit = 0;
3748 }
3749 if (nodigit) return 0; /* no digits? */
3750 *endptr = inptr; /* result pointer */
3751 return val;
3752 }
3753
3754 /* fprint_val - general radix printing routine
3755
3756 Inputs:
3757 stream = stream designator
3758 val = value to print
3759 radix = radix to print
3760 width = width to print
3761 format = leading zeroes format
3762 Outputs:
3763 status = error status
3764 */
3765
3766 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
3767 uint32 width, uint32 format)
3768 {
3769 #define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value)))
3770 t_value owtest, wtest;
3771 int32 d, digit, ndigits;
3772 char dbuf[MAX_WIDTH + 1];
3773
3774 for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' ';
3775 dbuf[MAX_WIDTH] = 0;
3776 d = MAX_WIDTH;
3777 do {
3778 d = d - 1;
3779 digit = (int32) (val % radix);
3780 val = val / radix;
3781 dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10);
3782 } while ((d > 0) && (val != 0));
3783
3784 if (format != PV_LEFT) {
3785 wtest = owtest = radix;
3786 ndigits = 1;
3787 while ((wtest < width_mask[width]) && (wtest >= owtest)) {
3788 owtest = wtest;
3789 wtest = wtest * radix;
3790 ndigits = ndigits + 1;
3791 }
3792 if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits;
3793 }
3794 if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR;
3795 return SCPE_OK;
3796 }
3797
3798 /* Event queue package
3799
3800 sim_activate add entry to event queue
3801 sim_cancel remove entry from event queue
3802 sim_process_event process entries on event queue
3803 sim_is_active see if entry is on event queue
3804 sim_atime return absolute time for an entry
3805 sim_gtime return global time
3806 sim_qcount return event queue entry count
3807
3808 Asynchronous events are set up by queueing a unit data structure
3809 to the event queue with a timeout (in simulator units, relative
3810 to the current time). Each simulator 'times' these events by
3811 counting down interval counter sim_interval. When this reaches
3812 zero the simulator calls sim_process_event to process the event
3813 and to see if further events need to be processed, or sim_interval
3814 reset to count the next one.
3815
3816 The event queue is maintained in clock order; entry timeouts are
3817 RELATIVE to the time in the previous entry.
3818
3819 sim_process_event - process event
3820
3821 Inputs:
3822 none
3823 Outputs:
3824 reason = reason code returned by any event processor,
3825 or 0 (SCPE_OK) if no exceptions
3826 */
3827
3828 t_stat sim_process_event (void)
3829 {
3830 UNIT *uptr;
3831 t_stat reason;
3832
3833 if (stop_cpu) return SCPE_STOP; /* stop CPU? */
3834 if (sim_clock_queue == NULL) { /* queue empty? */
3835 UPDATE_SIM_TIME (noqueue_time); /* update sim time */
3836 sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
3837 return SCPE_OK;
3838 }
3839 UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */
3840 do {
3841 uptr = sim_clock_queue; /* get first */
3842 sim_clock_queue = uptr->next; /* remove first */
3843 uptr->next = NULL; /* hygiene */
3844 uptr->time = 0;
3845 if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time;
3846 else sim_interval = noqueue_time = NOQUEUE_WAIT;
3847 if (uptr->action != NULL) reason = uptr->action (uptr);
3848 else reason = SCPE_OK;
3849 } while ((reason == SCPE_OK) && (sim_interval == 0));
3850
3851 /* Empty queue forces sim_interval != 0 */
3852
3853 return reason;
3854 }
3855
3856 /* sim_activate - activate (queue) event
3857
3858 Inputs:
3859 uptr = pointer to unit
3860 event_time = relative timeout
3861 Outputs:
3862 reason = result (SCPE_OK if ok)
3863 */
3864
3865 t_stat sim_activate (UNIT *uptr, int32 event_time)
3866 {
3867 UNIT *cptr, *prvptr;
3868 int32 accum;
3869
3870 if (event_time < 0) return SCPE_IERR;
3871 if (sim_is_active (uptr)) return SCPE_OK; /* already active? */
3872 if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
3873 else { UPDATE_SIM_TIME (sim_clock_queue->time); } /* update sim time */
3874
3875 prvptr = NULL;
3876 accum = 0;
3877 for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
3878 if (event_time < (accum + cptr->time)) break;
3879 accum = accum + cptr->time;
3880 prvptr = cptr;
3881 }
3882 if (prvptr == NULL) { /* insert at head */
3883 cptr = uptr->next = sim_clock_queue;
3884 sim_clock_queue = uptr;
3885 }
3886 else {
3887 cptr = uptr->next = prvptr->next; /* insert at prvptr */
3888 prvptr->next = uptr;
3889 }
3890 uptr->time = event_time - accum;
3891 if (cptr != NULL) cptr->time = cptr->time - uptr->time;
3892 sim_interval = sim_clock_queue->time;
3893 return SCPE_OK;
3894 }
3895
3896 /* sim_activate_abs - activate (queue) event even if event already scheduled
3897
3898 Inputs:
3899 uptr = pointer to unit
3900 event_time = relative timeout
3901 Outputs:
3902 reason = result (SCPE_OK if ok)
3903 */
3904
3905 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
3906 {
3907 sim_cancel (uptr);
3908 return sim_activate (uptr, event_time);
3909 }
3910
3911 /* sim_cancel - cancel (dequeue) event
3912
3913 Inputs:
3914 uptr = pointer to unit
3915 Outputs:
3916 reason = result (SCPE_OK if ok)
3917
3918 */
3919
3920 t_stat sim_cancel (UNIT *uptr)
3921 {
3922 UNIT *cptr, *nptr;
3923
3924 if (sim_clock_queue == NULL) return SCPE_OK;
3925 UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */
3926 nptr = NULL;
3927 if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr->next;
3928 else {
3929 for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
3930 if (cptr->next == uptr) {
3931 nptr = cptr->next = uptr->next;
3932 break; /* end queue scan */
3933 }
3934 }
3935 }
3936 if (nptr != NULL) nptr->time = nptr->time + uptr->time;
3937 uptr->next = NULL; /* hygiene */
3938 uptr->time = 0;
3939 if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time;
3940 else sim_interval = noqueue_time = NOQUEUE_WAIT;
3941 return SCPE_OK;
3942 }
3943
3944 /* sim_is_active - test for entry in queue, return activation time
3945
3946 Inputs:
3947 uptr = pointer to unit
3948 Outputs:
3949 result = absolute activation time + 1, 0 if inactive
3950 */
3951
3952 int32 sim_is_active (UNIT *uptr)
3953 {
3954 UNIT *cptr;
3955 int32 accum;
3956
3957 accum = 0;
3958 for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
3959 if (cptr == sim_clock_queue) {
3960 if (sim_interval > 0) accum = accum + sim_interval;
3961 }
3962 else accum = accum + cptr->time;
3963 if (cptr == uptr) return accum + 1;
3964 }
3965 return 0;
3966 }
3967
3968 /* sim_gtime - return global time
3969 sim_grtime - return global time with rollover
3970
3971 Inputs: none
3972 Outputs:
3973 time = global time
3974 */
3975
3976 double sim_gtime (void)
3977 {
3978 if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
3979 else { UPDATE_SIM_TIME (sim_clock_queue->time); }
3980 return sim_time;
3981 }
3982
3983 uint32 sim_grtime (void)
3984 {
3985 if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
3986 else { UPDATE_SIM_TIME (sim_clock_queue->time); }
3987 return sim_rtime;
3988 }
3989
3990 /* sim_qcount - return queue entry count
3991
3992 Inputs: none
3993 Outputs:
3994 count = number of entries on the queue
3995 */
3996
3997 int32 sim_qcount (void)
3998 {
3999 int32 cnt;
4000 UNIT *uptr;
4001
4002 cnt = 0;
4003 for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) cnt++;
4004 return cnt;
4005 }
4006
4007 /* Breakpoint package. This module replaces the VM-implemented one
4008 instruction breakpoint capability.
4009
4010 Breakpoints are stored in table sim_brk_tab, which is ordered by address for
4011 efficient binary searching. A breakpoint consists of a four entry structure:
4012
4013 addr address of the breakpoint
4014 type types of breakpoints set on the address
4015 a bit mask representing letters A-Z
4016 cnt number of iterations before breakp is taken
4017 action pointer command string to be executed
4018 when break is taken
4019
4020 sim_brk_summ is a summary of the types of breakpoints that are currently set (it
4021 is the bitwise OR of all the type fields). A simulator need only check for
4022 a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum.
4023
4024 The package contains the following public routines:
4025
4026 sim_brk_init initialize
4027 sim_brk_set set breakpoint
4028 sim_brk_clr clear breakpoint
4029 sim_brk_clrall clear all breakpoints
4030 sim_brk_show show breakpoint
4031 sim_brk_showall show all breakpoints
4032 sim_brk_test test for breakpoint
4033 sim_brk_npc PC has been changed
4034 sim_brk_getact get next action
4035 sim_brk_clract clear pending actions
4036
4037 Initialize breakpoint system.
4038 */
4039
4040 t_stat sim_brk_init (void)
4041 {
4042 sim_brk_lnt = SIM_BRK_INILNT;
4043 sim_brk_tab = (BRKTAB *) calloc (sim_brk_lnt, sizeof (BRKTAB));
4044 if (sim_brk_tab == NULL) return SCPE_MEM;
4045 sim_brk_ent = sim_brk_ins = 0;
4046 sim_brk_act = NULL;
4047 sim_brk_npc (0);
4048 return SCPE_OK;
4049 }
4050
4051 /* Search for a breakpoint in the sorted breakpoint table */
4052
4053 BRKTAB *sim_brk_fnd (t_addr loc)
4054 {
4055 int32 lo, hi, p;
4056 BRKTAB *bp;
4057
4058 if (sim_brk_ent == 0) { /* table empty? */
4059 sim_brk_ins = 0; /* insrt at head */
4060 return NULL; /* sch fails */
4061 }
4062 lo = 0; /* initial bounds */
4063 hi = sim_brk_ent - 1;
4064 do {
4065 p = (lo + hi) >> 1; /* probe */
4066 bp = sim_brk_tab + p; /* table addr */
4067 if (loc == bp->addr) return bp; /* match? */
4068 else if (loc < bp->addr) hi = p - 1; /* go down? p is upper */
4069 else lo = p + 1; /* go up? p is lower */
4070 } while (lo <= hi);
4071 if (loc < bp->addr) sim_brk_ins = p; /* insrt before or */
4072 else sim_brk_ins = p + 1; /* after last sch */
4073 return NULL;
4074 }
4075
4076 /* Insert a breakpoint */
4077
4078 BRKTAB *sim_brk_new (t_addr loc)
4079 {
4080 int32 i, t;
4081 BRKTAB *bp, *newp;
4082
4083 if (sim_brk_ins < 0) return NULL;
4084 if (sim_brk_ent >= sim_brk_lnt) { /* out of space? */
4085 t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */
4086 newp = (BRKTAB *) calloc (t, sizeof (BRKTAB)); /* new table */
4087 if (newp == NULL) return NULL; /* can't extend */
4088 for (i = 0; i < sim_brk_lnt; i++) /* copy table */
4089 *(newp + i) = *(sim_brk_tab + i);
4090 free (sim_brk_tab); /* free old table */
4091 sim_brk_tab = newp; /* new base, lnt */
4092 sim_brk_lnt = t;
4093 }
4094 if (sim_brk_ins != sim_brk_ent) { /* move needed? */
4095 for (bp = sim_brk_tab + sim_brk_ent;
4096 bp > sim_brk_tab + sim_brk_ins; bp--)
4097 *bp = *(bp - 1);
4098 }
4099 bp = sim_brk_tab + sim_brk_ins;
4100 bp->addr = loc;
4101 bp->typ = 0;
4102 bp->cnt = 0;
4103 bp->act = NULL;
4104 sim_brk_ent = sim_brk_ent + 1;
4105 return bp;
4106 }
4107
4108 /* Set a breakpoint of type sw */
4109
4110 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act)
4111 {
4112 BRKTAB *bp;
4113
4114 if (sw == 0) sw = sim_brk_dflt;
4115 if ((sim_brk_types & sw) == 0) return SCPE_NOFNC;
4116 bp = sim_brk_fnd (loc); /* present? */
4117 if (!bp) bp = sim_brk_new (loc); /* no, allocate */
4118 if (!bp) return SCPE_MEM; /* still no? mem err */
4119 bp->typ = sw; /* set type */
4120 bp->cnt = ncnt; /* set count */
4121 if ((bp->act != NULL) && (act != NULL)) { /* replace old action? */
4122 free (bp->act); /* deallocate */
4123 bp->act = NULL; /* now no action */
4124 }
4125 if ((act != NULL) && (*act != 0)) { /* new action? */
4126 char *newp = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc buf */
4127 if (newp == NULL) return SCPE_MEM; /* mem err? */
4128 strncpy (newp, act, CBUFSIZE); /* copy action */
4129 bp->act = newp; /* set pointer */
4130 }
4131 sim_brk_summ = sim_brk_summ | sw;
4132 return SCPE_OK;
4133 }
4134
4135 /* Clear a breakpoint */
4136
4137 t_stat sim_brk_clr (t_addr loc, int32 sw)
4138 {
4139 BRKTAB *bp = sim_brk_fnd (loc);
4140
4141 if (!bp) return SCPE_OK; /* not there? ok */
4142 if (sw == 0) sw = SIM_BRK_ALLTYP;
4143 bp->typ = bp->typ & ~sw;
4144 if (bp->typ) return SCPE_OK; /* clear all types? */
4145 if (bp->act != NULL) free (bp->act); /* deallocate action */
4146 for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++) /* erase entry */
4147 *bp = *(bp + 1);
4148 sim_brk_ent = sim_brk_ent - 1; /* decrement count */
4149 sim_brk_summ = 0; /* recalc summary */
4150 for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++)
4151 sim_brk_summ = sim_brk_summ | bp->typ;
4152 return SCPE_OK;
4153 }
4154
4155 /* Clear all breakpoints */
4156
4157 t_stat sim_brk_clrall (int32 sw)
4158 {
4159 BRKTAB *bp;
4160
4161 if (sw == 0) sw = SIM_BRK_ALLTYP;
4162 for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); ) {
4163 if (bp->typ & sw) sim_brk_clr (bp->addr, sw);
4164 else bp++;
4165 }
4166 return SCPE_OK;
4167 }
4168
4169 /* Show a breakpoint */
4170
4171 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
4172 {
4173 BRKTAB *bp = sim_brk_fnd (loc);
4174 DEVICE *dptr;
4175 int32 i, any;
4176
4177 if (sw == 0) sw = SIM_BRK_ALLTYP;
4178 if (!bp || (!(bp->typ & sw))) return SCPE_OK;
4179 dptr = sim_dflt_dev;
4180 if (dptr == NULL) return SCPE_OK;
4181 if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, loc);
4182 else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
4183 fprintf (st, ":\t");
4184 for (i = any = 0; i < 26; i++) {
4185 if ((bp->typ >> i) & 1) {
4186 if (any) fprintf (st, ", ");
4187 fputc (i + 'A', st);
4188 any = 1;
4189 }
4190 }
4191 if (bp->cnt > 0) fprintf (st, " [%d]", bp->cnt);
4192 if (bp->act != NULL) fprintf (st, "; %s", bp->act);
4193 fprintf (st, "\n");
4194 return SCPE_OK;
4195 }
4196
4197 /* Show all breakpoints */
4198
4199 t_stat sim_brk_showall (FILE *st, int32 sw)
4200 {
4201 BRKTAB *bp;
4202
4203 if (sw == 0) sw = SIM_BRK_ALLTYP;
4204 for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) {
4205 if (bp->typ & sw) sim_brk_show (st, bp->addr, sw);
4206 }
4207 return SCPE_OK;
4208 }
4209
4210 /* Test for breakpoint */
4211
4212 uint32 sim_brk_test (t_addr loc, uint32 btyp)
4213 {
4214 BRKTAB *bp;
4215 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
4216
4217 if ((bp = sim_brk_fnd (loc)) && (btyp & bp->typ)) { /* in table, type match? */
4218 if ((sim_brk_pend[spc] && (loc == sim_brk_ploc[spc])) || /* previous location? */
4219 (--bp->cnt > 0)) return 0; /* count > 0? */
4220 bp->cnt = 0; /* reset count */
4221 sim_brk_ploc[spc] = loc; /* save location */
4222 sim_brk_pend[spc] = TRUE; /* don't do twice */
4223 sim_brk_act = bp->act; /* set up actions */
4224 return (btyp & bp->typ);
4225 }
4226 sim_brk_pend[spc] = FALSE;
4227 return 0;
4228 }
4229
4230 /* Get next pending action, if any */
4231
4232 char *sim_brk_getact (char *buf, int32 size)
4233 {
4234 char *ep;
4235 size_t lnt;
4236
4237 if (sim_brk_act == NULL) return NULL; /* any action? */
4238 while (isspace (*sim_brk_act)) sim_brk_act++; /* skip spaces */
4239 if (*sim_brk_act == 0) return (sim_brk_act = NULL); /* now empty? */
4240 if (ep = strchr (sim_brk_act, ';')) { /* cmd delimiter? */
4241 lnt = ep - sim_brk_act; /* cmd length */
4242 memcpy (buf, sim_brk_act, lnt + 1); /* copy with ; */
4243 buf[lnt] = 0; /* erase ; */
4244 sim_brk_act = sim_brk_act + lnt + 1; /* adv ptr */
4245 }
4246 else {
4247 strncpy (buf, sim_brk_act, size); /* copy action */
4248 sim_brk_act = NULL; /* no more */
4249 }
4250 return buf;
4251 }
4252
4253 /* Clear pending actions */
4254
4255 void sim_brk_clract (void)
4256 {
4257 sim_brk_act = NULL;
4258 }
4259
4260 /* New PC */
4261
4262 void sim_brk_npc (uint32 cnt)
4263 {
4264 uint32 i;
4265
4266 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC)) cnt = SIM_BKPT_N_SPC;
4267 for (i = 0; i < cnt; i++) {
4268 sim_brk_pend[i] = FALSE;
4269 sim_brk_ploc[i] = 0;
4270 }
4271 return;
4272 }
4273
4274 /* Clear breakpoint space */
4275
4276 void sim_brk_clrspc (uint32 spc)
4277 {
4278 if (spc < SIM_BKPT_N_SPC) {
4279 sim_brk_pend[spc] = FALSE;
4280 sim_brk_ploc[spc] = 0;
4281 }
4282 return;
4283 }
4284
4285 /* Debug printout routines, from Dave Hittner */
4286
4287 const char* debug_bstates = "01_^";
4288 const char* debug_fmt = "DBG> %s %s: ";
4289 int32 debug_unterm = 0;
4290
4291 /* Finds debug phrase matching bitmask from from device DEBTAB table */
4292
4293 static char* get_dbg_verb (uint32 dbits, DEVICE* dptr)
4294 {
4295 static char* debtab_none = "DEBTAB_ISNULL";
4296 static char* debtab_nomatch = "DEBTAB_NOMATCH";
4297 int32 offset = 0;
4298
4299 if (dptr->debflags == 0) return debtab_none;
4300
4301 /* Find matching words for bitmask */
4302
4303 while (dptr->debflags[offset].name && (offset < 32)) {
4304 if (dptr->debflags[offset].mask & dbits)
4305 return dptr->debflags[offset].name;
4306 offset++;
4307 }
4308 return debtab_nomatch;
4309 }
4310
4311 /* Prints standard debug prefix unless previous call unterminated */
4312
4313 static void sim_debug_prefix (uint32 dbits, DEVICE* dptr)
4314 {
4315 if (!debug_unterm) {
4316 char* debug_type = get_dbg_verb (dbits, dptr);
4317 fprintf(sim_deb, debug_fmt, dptr->name, debug_type);
4318 }
4319 }
4320
4321 /* Prints state of a register: bit translation + state (0,1,_,^)
4322 indicating the state and transition of the bit. States:
4323 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
4324
4325 void sim_debug_u16(uint32 dbits, DEVICE* dptr, const char* const* bitdefs,
4326 uint16 before, uint16 after, int terminate)
4327 {
4328 if (sim_deb && (dptr->dctrl & dbits)) {
4329 int32 i;
4330
4331 sim_debug_prefix(dbits, dptr); /* print prefix if required */
4332 for (i = 15; i >= 0; i--) { /* print xlation, transition */
4333 int off = ((after >> i) & 1) + (((before ^ after) >> i) & 1) * 2;
4334 fprintf(sim_deb, "%s%c ", bitdefs[i], debug_bstates[off]);
4335 }
4336 if (terminate) fprintf(sim_deb, "\r\n");
4337 debug_unterm = terminate ? 0 : 1; /* set unterm for next */
4338 }
4339 }
4340
4341 #if defined (_WIN32)
4342 #define vsnprintf _vsnprintf
4343 #endif
4344 #if defined (__DECC) && defined (__VMS)
4345 #define NO_vsnprintf
4346 #endif
4347 #if defined( NO_vsnprintf)
4348 #define STACKBUFSIZE 16384
4349 #else
4350 #define STACKBUFSIZE 2048
4351 #endif
4352
4353 /* Inline debugging - will print debug message if debug file is
4354 set and the bitmask matches the current device debug options.
4355 Extra returns are added for un*x systems, since the output
4356 device is set into 'raw' mode when the cpu is booted,
4357 and the extra returns don't hurt any other systems. */
4358
4359 void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...)
4360 {
4361 if (sim_deb && (dptr->dctrl & dbits)) {
4362
4363 char stackbuf[STACKBUFSIZE];
4364 int32 bufsize = sizeof(stackbuf);
4365 char *buf = stackbuf;
4366 va_list arglist;
4367 int32 i, j, len;
4368
4369 buf[bufsize-1] = '\0';
4370 sim_debug_prefix(dbits, dptr); /* print prefix if required */
4371
4372 while (1) { /* format passed string, args */
4373 va_start (arglist, fmt);
4374 #if defined(NO_vsnprintf)
4375 #if defined(HAS_vsprintf_void)
4376
4377 /* Note, this could blow beyond the buffer, and we couldn't tell */
4378 /* That is a limitation of the C runtime library available on this platform */
4379
4380 vsprintf (buf, fmt, arglist);
4381 for (len = 0; len < bufsize-1; len++)
4382 if (buf[len] == 0) break;
4383 #else
4384 len = vsprintf (buf, fmt, arglist);
4385 #endif /* HAS_vsprintf_void */
4386 #else /* NO_vsnprintf */
4387 #if defined(HAS_vsnprintf_void)
4388 vsnprintf (buf, bufsize-1, fmt, arglist);
4389 for (len = 0; len < bufsize-1; len++)
4390 if (buf[len] == 0) break;
4391 #else
4392 len = vsnprintf (buf, bufsize-1, fmt, arglist);
4393 #endif /* HAS_vsnprintf_void */
4394 #endif /* NO_vsnprintf */
4395 va_end (arglist);
4396
4397 /* If it didn't fit into the buffer, then grow it and try again */
4398
4399 if ((len < 0) || (len >= bufsize-1)) {
4400 if (buf != stackbuf) free (buf);
4401 bufsize = bufsize * 2;
4402 buf = (char *) malloc (bufsize);
4403 if (buf == NULL) return; /* out of memory */
4404 buf[bufsize-1] = '\0';
4405 continue;
4406 }
4407 break;
4408 }
4409
4410 /* Output the formatted data expanding newlines where they exist */
4411
4412 for (i = j = 0; i < len; ++i) {
4413 if ('\n' == buf[i]) {
4414 if (i > j) fwrite (&buf[j], 1, i-j, sim_deb);
4415 j = i;
4416 fputc('\r', sim_deb);
4417 }
4418 }
4419 if (i > j) fwrite (&buf[j], 1, i-j, sim_deb);
4420
4421 /* Set unterminated flag for next time */
4422
4423 debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1;
4424 if (buf != stackbuf) free (buf);
4425 }
4426 return;
4427 }