1 /* scp.c: simulator control program
2 
3    Copyright (c) 1993-2016 Robert M Supnik
4    Copyright (c) 2021 Jeffrey H. Johnson <trnsz@pobox.com>
5    Copyright (c) 2021 The DPS8M Development Team
6 
7    Permission is hereby granted, free of charge, to any person obtaining a
8    copy of this software and associated documentation files (the "Software"),
9    to deal in the Software without restriction, including without limitation
10    the rights to use, copy, modify, merge, publish, distribute, sublicense,
11    and/or sell copies of the Software, and to permit persons to whom the
12    Software is furnished to do so, subject to the following conditions:
13 
14    The above copyright notice and this permission notice shall be included in
15    all copies or substantial portions of the Software.
16 
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20    ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24    Except as contained in this notice, the name of Robert M Supnik shall not be
25    used in advertising or otherwise to promote the sale, use or other dealings
26    in this Software without prior written authorization from Robert M Supnik.
27 */
28 
29 /* Macros and data structures */
30 
31 #include "sim_defs.h"
32 #include "sim_disk.h"
33 #include "sim_tape.h"
34 #include "sim_sock.h"
35 #include <signal.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <math.h>
39 #if defined(_WIN32)
40 # ifndef WIN32_LEAN_AND_MEAN
41 #  define WIN32_LEAN_AND_MEAN
42 # endif
43 # if defined(_MSC_VER)
44 #  pragma warning(push, 3)
45 # endif
46 # include <direct.h>
47 # include <io.h>
48 # include <fcntl.h>
49 #else
50 # include <unistd.h>
51 #endif
52 #include <sys/stat.h>
53 #include <setjmp.h>
54 #include <limits.h>
55 
56 #include "linehistory.h"
57 
58 #if defined(__APPLE__)
59 # include <sys/types.h>
60 # include <sys/sysctl.h>
61 #endif
62 
63 #include <uv.h>
64 
65 #define DBG_CTR 0
66 
67 #include "../dps8/dps8.h"
68 #include "../dps8/dps8_cpu.h"
69 #include "../dps8/ver.h"
70 #include "../dps8/sysdefs.h"
71 
72 #include "../decNumber/decContext.h"
73 #include "../decNumber/decNumberLocal.h"
74 
75 #include "dispatch.h"
76 
77 #ifndef MAX
78 # define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
79 #endif
80 
81 /* search logical and boolean ops */
82 
83 #define SCH_OR          0                               /* search logicals */
84 #define SCH_AND         1
85 #define SCH_XOR         2
86 #define SCH_E           0                               /* search booleans */
87 #define SCH_N           1
88 #define SCH_G           2
89 #define SCH_L           3
90 #define SCH_EE          4
91 #define SCH_NE          5
92 #define SCH_GE          6
93 #define SCH_LE          7
94 
95 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
96 #define SRBSIZ          1024                            /* save/restore buffer */
97 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
98 #define SIM_BRK_ALLTYP  0xFFFFFFFB
99 #define UPDATE_SIM_TIME                                         \
100     if (1) {                                                    \
101         int32 _x;                                               \
102         if (sim_clock_queue == QUEUE_LIST_END)                  \
103             _x = noqueue_time;                                  \
104         else                                                    \
105             _x = sim_clock_queue->time;                         \
106         sim_time = sim_time + (_x - sim_interval);              \
107         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
108         if (sim_clock_queue == QUEUE_LIST_END)                  \
109             noqueue_time = sim_interval;                        \
110         else                                                    \
111             sim_clock_queue->time = sim_interval;               \
112         }                                                       \
113     else                                                        \
114         (void)0
115 
116 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
117 #define SZ_R(rp) \
118     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
119 #define SZ_LOAD(sz,v,mb,j) \
120     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \
121     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
122     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
123     else v = *(((t_uint64 *) mb) + ((uint32) j));
124 #define SZ_STORE(sz,v,mb,j) \
125     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \
126     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
127     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
128     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
129 #define GET_SWITCHES(cp) \
130     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
131 #define GET_RADIX(val,dft) \
132     if (sim_switches & SWMASK ('O')) val = 8; \
133     else if (sim_switches & SWMASK ('D')) val = 10; \
134     else if (sim_switches & SWMASK ('H')) val = 16; \
135     else val = dft;
136 
137 /* Asynch I/O support */
138 t_bool sim_asynch_enabled = FALSE;
139 
140 /* The per-simulator init routine is a weak global that defaults to NULL
141    The other per-simulator pointers can be overrriden by the init routine */
142 
143 extern void (*sim_vm_init) (void);
144 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
145 void (*sim_vm_post) (t_bool from_scp) = NULL;
146 CTAB *sim_vm_cmd = NULL;
147 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
148 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
149 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
150 t_value (*sim_vm_pc_value) (void) = NULL;
151 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
152 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
153 
154 /* Prototypes */
155 
156 /* Set and show command processors */
157 
158 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
159 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
160 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
161 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
162 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
163 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
164 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
165 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
166 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
167 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
168 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
169 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
170 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
171 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
172 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
173 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
174 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
175 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
176 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
177 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
178 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
179 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
180 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
181 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
182 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
183 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
184 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
185 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
186 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
187 t_stat sim_save (FILE *sfile);
188 t_stat sim_rest (FILE *rfile);
189 
190 /* Breakpoint package */
191 
192 t_stat sim_brk_init (void);
193 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
194 t_stat sim_brk_clr (t_addr loc, int32 sw);
195 t_stat sim_brk_clrall (int32 sw);
196 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
197 t_stat sim_brk_showall (FILE *st, int32 sw);
198 CONST char *sim_brk_getact (char *buf, int32 size);
199 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
200 char *sim_brk_clract (void);
201 
202 FILE *stdnul;
203 
204 /* Command support routines */
205 
206 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
207 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
208 int32 test_search (t_value *val, SCHTAB *schptr);
209 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
210 int32 get_switches (const char *cptr);
211 CONST char *get_sim_sw (CONST char *cptr);
212 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
213 t_value get_rval (REG *rptr, uint32 idx);
214 void put_rval (REG *rptr, uint32 idx, t_value val);
215 void fprint_help (FILE *st);
216 void fprint_stopped (FILE *st, t_stat r);
217 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
218 void fprint_sep (FILE *st, int32 *tokens);
219 char *read_line (char *ptr, int32 size, FILE *stream);
220 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
221 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
222 char *sim_trim_endspc (char *cptr);
223 
224 /* Forward references */
225 
226 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
227 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
228 t_bool qdisable (DEVICE *dptr);
229 t_stat attach_err (UNIT *uptr, t_stat stat);
230 t_stat detach_all (int32 start_device, t_bool shutdown);
231 t_stat assign_device (DEVICE *dptr, const char *cptr);
232 t_stat deassign_device (DEVICE *dptr);
233 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
234 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
235     REG *lowr, REG *highr, uint32 lows, uint32 highs);
236 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
237 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
238 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
239     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
240 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
241 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
242     UNIT *uptr, int32 dfltinc);
243 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
244 t_stat step_svc (UNIT *ptr);
245 t_stat expect_svc (UNIT *ptr);
246 t_stat set_on (int32 flag, CONST char *cptr);
247 t_stat set_verify (int32 flag, CONST char *cptr);
248 t_stat set_message (int32 flag, CONST char *cptr);
249 t_stat set_quiet (int32 flag, CONST char *cptr);
250 t_stat set_asynch (int32 flag, CONST char *cptr);
251 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
252 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
253 void int_handler (int signal);
254 t_stat set_prompt (int32 flag, CONST char *cptr);
255 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
256 t_stat sim_set_environment (int32 flag, CONST char *cptr);
257 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
258 
259 /* Global data */
260 
261 DEVICE *sim_dflt_dev = NULL;
262 UNIT *sim_clock_queue = QUEUE_LIST_END;
263 int32 sim_interval = 0;
264 int32 sim_switches = 0;
265 FILE *sim_ofile = NULL;
266 SCHTAB *sim_schrptr = FALSE;
267 SCHTAB *sim_schaptr = FALSE;
268 DEVICE *sim_dfdev = NULL;
269 UNIT *sim_dfunit = NULL;
270 DEVICE **sim_internal_devices = NULL;
271 uint32 sim_internal_device_count = 0;
272 int32 sim_opt_out = 0;
273 int32 sim_is_running = 0;
274 t_bool sim_processing_event = FALSE;
275 uint32 sim_brk_summ = 0;
276 uint32 sim_brk_types = 0;
277 BRKTYPTAB *sim_brk_type_desc = NULL;                  /* type descriptions */
278 uint32 sim_brk_dflt = 0;
279 uint32 sim_brk_match_type;
280 t_addr sim_brk_match_addr;
281 char *sim_brk_act[MAX_DO_NEST_LVL];
282 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
283 BRKTAB **sim_brk_tab = NULL;
284 int32 sim_brk_ent = 0;
285 int32 sim_brk_lnt = 0;
286 int32 sim_brk_ins = 0;
287 int32 sim_iglock = 0;
288 int32 sim_nolock = 0;
289 int32 sim_quiet = 0;
290 int32 sim_randstate = 0;
291 int32 sim_randompst = 0;
292 int32 sim_step = 0;
293 static double sim_time;
294 static uint32 sim_rtime;
295 static int32 noqueue_time;
296 volatile int32 stop_cpu = 0;
297 static char **sim_argv;
298 t_value *sim_eval = NULL;
299 static t_value sim_last_val;
300 static t_addr sim_last_addr;
301 FILE *sim_log = NULL;                                   /* log file */
302 FILEREF *sim_log_ref = NULL;                            /* log file file reference */
303 FILE *sim_deb = NULL;                                   /* debug file */
304 FILEREF *sim_deb_ref = NULL;                            /* debug file file reference */
305 int32 sim_deb_switches = 0;                             /* debug switches */
306 struct timespec sim_deb_basetime;                       /* debug timestamp relative base time */
307 char *sim_prompt = NULL;                                /* prompt string */
308 static FILE *sim_gotofile;                              /* the currently open do file */
309 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];          /* the current line number in the currently open do file */
310 static int32 sim_do_echo = 0;                           /* the echo status of the currently open do file */
311 static int32 sim_show_message = 1;                      /* the message display status of the currently open do file */
312 static int32 sim_on_inherit = 0;                        /* the inherit status of on state and conditions when executing do files */
313 static int32 sim_do_depth = 0;
314 
315 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
316 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
317 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
318 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
319 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
320 
321 t_stat sim_last_cmd_stat;                               /* Command Status */
322 
323 static SCHTAB sim_stabr;                                /* Register search specifier */
324 static SCHTAB sim_staba;                                /* Memory search specifier */
325 
326 static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0)  };
327 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
328 static const char *sim_si64 = "64b data";
329 static const char *sim_sa64 = "32b addresses";
330 const char *sim_savename = sim_name;      /* simulator Name used in SAVE/RESTORE images */
331 
332 /* Tables and strings */
333 
334 const char save_vercur[] = "V4.1";
335 const char save_ver40[] = "V4.0";
336 const char save_ver35[] = "V3.5";
337 const char save_ver32[] = "V3.2";
338 const char save_ver30[] = "V3.0";
339 const struct scp_error {
340     const char *code;
341     const char *message;
342     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
343         {{"NXM",     "Address space exceeded"},
344          {"UNATT",   "Unit not attached"},
345          {"IOERR",   "I/O error"},
346          {"CSUM",    "Checksum error"},
347          {"FMT",     "Format error"},
348          {"NOATT",   "Unit not attachable"},
349          {"OPENERR", "File open error"},
350          {"MEM",     "Memory exhausted"},
351          {"ARG",     "Invalid argument"},
352          {"STEP",    "Step expired"},
353          {"UNK",     "Unknown command"},
354          {"RO",      "Read only argument"},
355          {"INCOMP",  "Command not completed"},
356          {"STOP",    "Simulation stopped"},
357          {"EXIT",    "Goodbye"},
358          {"TTIERR",  "Console input I/O error"},
359          {"TTOERR",  "Console output I/O error"},
360          {"EOF",     "End of file"},
361          {"REL",     "Relocation error"},
362          {"NOPARAM", "No settable parameters"},
363          {"ALATT",   "Unit already attached"},
364          {"TIMER",   "Hardware timer error"},
365          {"SIGERR",  "Signal handler setup error"},
366          {"TTYERR",  "Console terminal setup error"},
367          {"SUB",     "Subscript out of range"},
368          {"NOFNC",   "Command not allowed"},
369          {"UDIS",    "Unit disabled"},
370          {"NORO",    "Read only operation not allowed"},
371          {"INVSW",   "Invalid switch"},
372          {"MISVAL",  "Missing value"},
373          {"2FARG",   "Too few arguments"},
374          {"2MARG",   "Too many arguments"},
375          {"NXDEV",   "Non-existent device"},
376          {"NXUN",    "Non-existent unit"},
377          {"NXREG",   "Non-existent register"},
378          {"NXPAR",   "Non-existent parameter"},
379          {"NEST",    "Nested DO command limit exceeded"},
380          {"IERR",    "Internal error"},
381          {"MTRLNT",  "Invalid magtape record length"},
382          {"LOST",    "Console Telnet connection lost"},
383          {"TTMO",    "Console Telnet connection timed out"},
384          {"STALL",   "Console Telnet output stall"},
385          {"AFAIL",   "Assertion failed"},
386          {"INVREM",  "Invalid remote console command"},
387          {"NOTATT",  "Not attached"},
388          {"EXPECT",  "Expect matched"},
389          {"REMOTE",  "Remote console command"},
390     };
391 
392 const size_t size_map[] = { sizeof (int8),
393     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
394     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
395 };
396 
397 const t_value width_mask[] = { 0,
398     0x1, 0x3, 0x7, 0xF,
399     0x1F, 0x3F, 0x7F, 0xFF,
400     0x1FF, 0x3FF, 0x7FF, 0xFFF,
401     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
402     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
403     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
404     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
405     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
406     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
407     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
408     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
409     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
410     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
411     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
412     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
413     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
414     };
415 
416 static const char simh_help[] =
417        /***************** 80 character line width template *************************/
418       "1Commands\n"
419 #define HLP_RESET       "*Commands Resetting Devices"
420        /***************** 80 character line width template *************************/
421       "2Resetting Devices\n"
422       " The RESET command (abbreviation RE) resets a device or the entire simulator\n"
423       " to a predefined condition.  If switch -p is specified, the device is reset\n"
424       " to its power-up state:\n\n"
425       "++RESET                  reset all devices\n"
426       "++RESET -p               powerup all devices\n"
427       "++RESET ALL              reset all devices\n"
428       "++RESET <device>         reset specified device\n\n"
429       " Typically, RESET stops any in-progress I/O operation, clears any interrupt\n"
430       " request, and returns the device to a quiescent state.  It does not clear\n"
431       " main memory or affect I/O connections.\n"
432 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
433 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
434 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
435 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
436        /***************** 80 character line width template *************************/
437       "2Examining and Changing State\n"
438       " There are four commands to examine and change state:\n\n"
439       "++EXAMINE (abbreviated E) examines state\n"
440       "++DEPOSIT (abbreviated D) changes state\n"
441       "++IEXAMINE (interactive examine, abbreviated IE) examines state and allows\n"
442       "++++the user to interactively change it\n"
443       "++IDEPOSIT (interactive deposit, abbreviated ID) allows the user to\n"
444       "++++interactively change state\n\n"
445       " All four commands take the form\n\n"
446       "++command {modifiers} <object list>\n\n"
447       " Deposit must also include a deposit value at the end of the command.\n\n"
448       " There are four kinds of modifiers: switches, device/unit name, search\n"
449       " specifier, and for EXAMINE, output file.  Switches have been described\n"
450       " previously.  A device/unit name identifies the device and unit whose\n"
451       " address space is to be examined or modified.  If no device is specified,\n"
452       " the CPU (main memory)is selected; if a device but no unit is specified,\n"
453       " unit 0 of the device is selected.\n\n"
454       " The search specifier provides criteria for testing addresses or registers\n"
455       " to see if they should be processed.  A specifier consists of a logical\n"
456       " operator, a relational operator, or both, optionally separated by spaces.\n\n"
457       "++{<logical op> <value>} <relational op> <value>\n\n"
458        /***************** 80 character line width template *************************/
459       " where the logical operator is & (and), | (or), or ^ (exclusive or), and the\n"
460       " relational operator is = or == (equal), ! or != (not equal), >= (greater\n"
461       " than or equal), > (greater than), <= (less than or equal), or < (less than).\n"
462       " If a logical operator is specified without a relational operator, it is\n"
463       " ignored.  If a relational operator is specified without a logical operator,\n"
464       " no logical operation is performed.  All comparisons are unsigned.\n\n"
465       " The output file modifier redirects command output to a file instead of the\n"
466       " console.  An output file modifier consists of @ followed by a valid file\n"
467       " name.\n\n"
468       " Modifiers may be specified in any order.  If multiple modifiers of the\n"
469       " same type are specified, later modifiers override earlier modifiers.  Note\n"
470       " that if the device/unit name comes after the search specifier, the search\n"
471       " values will interpreted in the radix of the CPU, rather than of the\n"
472       " device/unit.\n\n"
473       " The \"object list\" consists of one or more of the following, separated by\n"
474       " commas:\n\n"
475        /***************** 80 character line width template *************************/
476       "++register                the specified register\n"
477       "++register[sub1-sub2]     the specified register array locations,\n"
478       "++++++++                  starting at location sub1 up to and\n"
479       "++++++++                  including location sub2\n"
480       "++register[sub1/length]   the specified register array locations,\n"
481       "++++++++                  starting at location sub1 up to but\n"
482       "++++++++                  not including sub1+length\n"
483       "++register[ALL]           all locations in the specified register\n"
484       "++++++++                  array\n"
485       "++register1-register2     all the registers starting at register1\n"
486       "++++++++                  up to and including register2\n"
487       "++address                 the specified location\n"
488       "++address1-address2       all locations starting at address1 up to\n"
489       "++++++++                  and including address2\n"
490       "++address/length          all location starting at address up to\n"
491       "++++++++                  but not including address+length\n"
492       "++STATE                   all registers in the device\n"
493       "++ALL                     all locations in the unit\n"
494       "++$                       the last value displayed by an EXAMINE\n"
495       "++++++++                  command interpreted as an address\n"
496       "3Switches\n"
497       " Switches can be used to control the format of display information:\n\n"
498        /***************** 80 character line width template *************************/
499       "++-a                 display as ASCII\n"
500       "++-c                 display as character string\n"
501       "++-m                 display as instruction mnemonics\n"
502       "++-o                 display as octal\n"
503       "++-d                 display as decimal\n"
504       "++-h                 display as hexadecimal\n\n"
505       " The simulators typically accept symbolic input (see documentation with each\n"
506       " simulator).\n\n"
507       "3Examples\n"
508       " Examples:\n\n"
509       "++ex 1000-1100                examine 1000 to 1100\n"
510       "++de PC 1040                  set PC to 1040\n"
511       "++ie 40-50                    interactively examine 40:50\n"
512       "++ie >1000 40-50              interactively examine the subset\n"
513       "+++++++++                     of locations 40:50 that are >1000\n"
514       "++ex rx0 50060                examine 50060, RX unit 0\n"
515       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\n"
516       "++de all 0                    set main memory to 0\n"
517       "++de &77>0 0                  set all addresses whose low order\n"
518       "+++++++++                     bits are non-zero to 0\n"
519       "++ex -m @memdump.txt 0-7777   dump memory to file\n\n"
520       " Note: to terminate an interactive command, simply type a bad value\n"
521       "       (eg, XYZ) when input is requested.\n"
522 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
523        /***************** 80 character line width template *************************/
524       "2Evaluating Instructions\n"
525       " The EVAL command evaluates a symbolic expression and returns the equivalent\n"
526       " numeric value.  This is useful for obtaining numeric arguments for a search\n"
527       " command:\n\n"
528       "++EVAL <expression>\n"
529        /***************** 80 character line width template *************************/
530       "2Saving and Restoring State\n"
531 #define HLP_SAVE        "*Commands Saving_and_Restoring_State SAVE"
532       "3SAVE\n"
533       " The SAVE command (abbreviation SA) save the complete state of the simulator\n"
534       " to a file.  This includes the contents of main memory and all registers,\n"
535       " and the I/O connections of devices:\n\n"
536       "++SAVE <filename>\n\n"
537 #define HLP_RESTORE     "*Commands Saving_and_Restoring_State RESTORE"
538       "3RESTORE\n"
539       " The RESTORE command (abbreviation REST, alternately GET) restores a\n"
540       " previously saved simulator state:\n\n"
541       "++RESTORE <filename>\n"
542       "4Switches\n"
543       " Switches can influence the output and behavior of the RESTORE command\n\n"
544       "++-Q      Suppresses version warning messages\n"
545       "++-D      Suppress detaching and attaching devices during a restore\n"
546       "++-F      Overrides timestamp validation during restore\n"
547       "\n"
548       "4Notes:\n"
549       " 1) SAVE file format compresses zeroes to minimize file size.\n"
550       " 2) The simulator can't restore active incoming telnet sessions to\n"
551       " multiplexer devices, but the listening ports will be restored across a\n"
552       " save/restore.\n"
553        /***************** 80 character line width template *************************/
554       "2Running A Simulated Program\n"
555 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
556       "3RUN\n"
557       " The RUN command (abbreviated RU) resets all devices, deposits its argument\n"
558       " (if given) in the PC, and starts execution.  If no argument is given,\n"
559       " execution starts at the current PC.\n"
560 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
561       "3GO\n"
562       " The GO command does not reset devices, deposits its argument (if given)\n"
563       " in the PC, and starts execution.  If no argument is given, execution\n"
564       " starts at the current PC.\n"
565 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program CONTINUE"
566       "3CONTINUE\n"
567       " The CONT command (abbreviated CO) does not reset devices and resumes\n"
568       " execution at the current PC.\n"
569 #define HLP_STEP        "*Commands Running_A_Simulated_Program STEP"
570       "3STEP\n"
571       " The STEP command (abbreviated S) resumes execution at the current PC for\n"
572       " the number of instructions given by its argument.  If no argument is\n"
573       " supplied, one instruction is executed.\n"
574       "4Switches\n"
575       " If the STEP command is invoked with the -T switch, the step command will\n"
576       " cause execution to run for microseconds rather than instructions.\n"
577 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
578       "3NEXT\n"
579       " The NEXT command (abbreviated N) resumes execution at the current PC for\n"
580       " one instruction, attempting to execute through a subroutine calls.\n"
581       " If the next instruction to be executed is not a subroutine call,\n"
582       " one instruction is executed.\n"
583 #define HLP_BOOT        "*Commands Running_A_Simulated_Program BOOT"
584       "3BOOT\n"
585       " The BOOT command (abbreviated BO) resets all devices and bootstraps the\n"
586       " device and unit given by its argument.  If no unit is supplied, unit 0 is\n"
587       " bootstrapped.  The specified unit must be attached.\n"
588        /***************** 80 character line width template *************************/
589       "2Stopping The Simulator\n"
590       " Programs run until the simulator detects an error or stop condition, or\n"
591       " until the user forces a stop condition.\n"
592       "3Simulator Detected Stop Conditions\n"
593       " These simulator-detected conditions stop simulation:\n\n"
594       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\n"
595       "++-  Breakpoint.  The simulator may support breakpoints (see below).\n"
596       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\n"
597       "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
598       "+++usually stops.\n\n"
599       "++-  Processor condition.  Certain processor conditions can stop\n"
600       "+++simulation; these are described with the individual simulators.\n"
601       "3User Specified Stop Conditions\n"
602       " Typing the interrupt character stops simulation.  The interrupt character\n"
603       " is defined by the WRU (where are you) console option and is initially set\n"
604       " to 005 (^E).\n\n"
605        /***************** 80 character line width template *************************/
606 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
607 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
608       "4Breakpoints\n"
609       " A simulator may offer breakpoint capability.  A simulator may define\n"
610       " breakpoints of different types, identified by letter (for example, E for\n"
611       " execution, R for read, W for write, etc).  At the moment, most simulators\n"
612       " support only E (execution) breakpoints.\n\n"
613       " Associated with a breakpoint are a count and, optionally, one or more\n"
614       " actions.  Each time the breakpoint is taken, the associated count is\n"
615       " decremented.  If the count is less than or equal to 0, the breakpoint\n"
616       " occurs; otherwise, it is deferred.  When the breakpoint occurs, the\n"
617       " optional actions are automatically executed.\n\n"
618       " A breakpoint is set by the BREAK or the SET BREAK commands:\n\n"
619       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n"
620       "++SET BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
621       " If no type is specified, the simulator-specific default breakpoint type\n"
622       " (usually E for execution) is used.  If no address range is specified, the\n"
623       " current PC is used.  As with EXAMINE and DEPOSIT, an address range may be a\n"
624       " single address, a range of addresses low-high, or a relative range of\n"
625       " address/length.\n"
626        /***************** 80 character line width template *************************/
627       "5Displaying Breakpoints\n"
628       " Currently set breakpoints can be displayed with the SHOW BREAK command:\n\n"
629       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
630       " Locations with breakpoints of the specified type are displayed.\n\n"
631       " The -C switch displays the selected breakpoint(s) formatted as commands\n"
632       " which may be subsequently used to establish the same breakpoint(s).\n\n"
633       "5Removing Breakpoints\n"
634       " Breakpoints can be cleared by the NOBREAK or the SET NOBREAK commands.\n"
635       "5Examples\n"
636       "++BREAK                      set E break at current PC\n"
637       "++BREAK -e 200               set E break at 200\n"
638       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\n"
639       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\n"
640       "+++++++++D MQ 0\n"
641       "++BREAK 100;                 delete action on break at 100\n\n"
642        /***************** 80 character line width template *************************/
643       "2Connecting and Disconnecting Devices\n"
644       " Except for main memory and network devices, units are simulated as\n"
645       " unstructured binary disk files in the host file system.  Before using a\n"
646       " simulated unit, the user must specify the file to be accessed by that unit.\n"
647 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices ATTACH"
648       "3ATTACH\n"
649       " The ATTACH (abbreviation AT) command associates a unit and a file:\n"
650       "++ATTACH <unit> <filename>\n\n"
651       " Some devices have more detailed or specific help available with:\n\n"
652       "++HELP <device> ATTACH\n\n"
653       "4Switches\n"
654       "5-n\n"
655       " If the -n switch is specified when an attach is executed, a new file is\n"
656       " created, and an appropriate message is printed.\n"
657       "5-e\n"
658       " If the file does not exist, and the -e switch was not specified, a new\n"
659       " file is created, and an appropriate message is printed.  If the -e switch\n"
660       " was specified, a new file is not created, and an error message is printed.\n"
661       "5-r\n"
662       " If the -r switch is specified, or the file is write protected, ATTACH tries\n"
663       " to open the file read only.  If the file does not exist, or the unit does\n"
664       " not support read only operation, an error occurs.  Input-only devices, such\n"
665       " as paper-tape readers, and devices with write lock switches, such as disks\n"
666       " and tapes, support read only operation; other devices do not.  If a file is\n"
667       " attached read only, its contents can be examined but not modified.\n"
668       "5-q\n"
669       " If the -q switch is specified when creating a new file (-n) or opening one\n"
670       " read only (-r), the message announcing this fact is suppressed.\n"
671       "5-f\n"
672       " For simulated magnetic tapes, the ATTACH command can specify the format of\n"
673       " the attached tape image file:\n\n"
674       "++ATTACH -f <tape_unit> <format> <filename>\n\n"
675       " The currently supported tape image file formats are:\n\n"
676       "++SIMH                   SIMH simulator format\n"
677       "++E11                    E11 simulator format\n"
678       "++TPC                    TPC format\n"
679       "++P7B                    Pierce simulator 7-track format\n\n"
680        /***************** 80 character line width template *************************/
681       " For some simulated disk devices, the ATTACH command can specify the format\n"
682       " of the attached disk image file:\n\n"
683       "++ATTACH -f <disk_unit> <format> <filename>\n\n"
684       " The currently supported disk image file formats are:\n\n"
685       "++SIMH                   SIMH simulator format\n"
686       "++RAW                    platform specific access to physical disk or\n"
687       "++                       CDROM drives\n"
688       " The disk format can also be set with the SET command prior to ATTACH:\n\n"
689       "++SET <disk_unit> FORMAT=<format>\n"
690       "++ATT <disk_unit> <filename>\n\n"
691        /***************** 80 character line width template *************************/
692       " The format of an attached tape or disk file can be displayed with the SHOW\n"
693       " command:\n"
694       "++SHOW <unit> FORMAT\n"
695       " For Telnet-based terminal emulation devices, the ATTACH command associates\n"
696       " the master unit with a TCP/IP listening port:\n\n"
697       "++ATTACH <unit> <port>\n\n"
698       " The port is a decimal number between 1 and 65535 that is not already used\n"
699       " other TCP/IP applications.\n"
700        /***************** 80 character line width template *************************/
701 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices DETACH"
702       "3DETACH\n"
703       " The DETACH (abbreviation DET) command breaks the association between a unit\n"
704       " and a file, port, or network device:\n\n"
705       "++DETACH ALL             detach all units\n"
706       "++DETACH <unit>          detach specified unit\n"
707       " The EXIT command performs an automatic DETACH ALL.\n"
708 #define HLP_SET         "*Commands SET"
709       "2SET\n"
710        /***************** 80 character line width template *************************/
711 #define HLP_SET_CONSOLE "*Commands SET CONSOLE"
712       "3Console\n"
713       "+set console arg{,arg...}    set console options\n"
714       "+set console WRU             specify console drop to simh character\n"
715       "+set console BRK             specify console Break character\n"
716       "+set console DEL             specify console delete character\n"
717       "+set console PCHAR           specify console printable characters\n"
718       "+set console SPEED=speed{*factor}\n"
719       "++++++++                     specify console input data rate\n"
720       "+set console TELNET=port     specify console telnet port\n"
721       "+set console TELNET=LOG=log_file\n"
722       "++++++++                     specify console telnet logging to the\n"
723       "++++++++                     specified destination {LOG,STDOUT,STDERR,\n"
724       "++++++++                     DEBUG or filename)\n"
725       "+set console TELNET=NOLOG    disables console telnet logging\n"
726       "+set console TELNET=BUFFERED[=bufsize]\n"
727       "++++++++                     specify console telnet buffering\n"
728       "+set console TELNET=NOBUFFERED\n"
729       "++++++++                     disables console telnet buffering\n"
730       "+set console TELNET=UNBUFFERED\n"
731       "++++++++                     disables console telnet buffering\n"
732       "+set console NOTELNET        disable console telnet\n"
733        /***************** 80 character line width template *************************/
734 #define HLP_SET_REMOTE "*Commands SET REMOTE"
735       "3Remote\n"
736       "+set remote TELNET=port      specify remote console telnet port\n"
737       "+set remote NOTELNET         disables remote console\n"
738       "+set remote CONNECTIONS=n    specify number of concurrent remote\n"
739       "++++++++                     console sessions\n"
740       "+set remote TIMEOUT=n        specify number of seconds without input\n"
741       "++++++++                     before automatic continue\n"
742 #define HLP_SET_LOG    "*Commands SET Log"
743       "3Log\n"
744       " Interactions with the simulator session (at the \"sim>\" prompt\n"
745       " can be recorded to a log file\n\n"
746       "+set log log_file            specify the log destination\n"
747       "++++++++                     (STDOUT,DEBUG or filename)\n"
748       "+set nolog                   disables any currently active logging\n"
749       "4Switches\n"
750       " By default, log output is written at the end of the specified log file.\n"
751       " A new log file can created if the -N switch is used on the command line.\n\n"
752       " By default, log output is written in text mode.  The log file can be\n"
753       " opened for binary mode writing if the -B switch is used on the command line.\n"
754 #define HLP_SET_DEBUG  "*Commands SET Debug"
755        /***************** 80 character line width template *************************/
756       "3Debug\n"
757       "+set debug debug_file        specify the debug destination\n"
758       "++++++++                     (STDOUT,STDERR,LOG or filename)\n"
759       "+set nodebug                 disables any currently active debug output\n"
760       "4Switches\n"
761       " Debug message output contains a timestamp which indicates the number of\n"
762       " simulated instructions which have been executed prior to the debug event.\n\n"
763       " Debug message output can be enhanced to contain additional, potentially\n"
764       " useful information.\n"
765       "5-T\n"
766       " The -T switch causes debug output to contain a time of day displayed\n"
767       " as hh:mm:ss.msec.\n"
768       "5-A\n"
769       " The -A switch causes debug output to contain a time of day displayed\n"
770       " as seconds.msec.\n"
771       "5-R\n"
772       " The -R switch causes the time of day displayed due to the -T or -A\n"
773       " switches to be relative to the start time of debugging.  If neither\n"
774       " -T or -A is explicitly specified, -T is implied.\n"
775       "5-P\n"
776       " The -P switch adds the output of the PC (Program Counter) to each debug\n"
777       " message.\n"
778       "5-N\n"
779       " The -N switch causes a new/empty file to be written to.  The default\n"
780       " is to append to an existing debug log file.\n"
781       "5-D\n"
782       " The -D switch causes data blob output to also display the data as\n"
783       " RADIX-50 characters.\n"
784       "5-E\n"
785       " The -E switch causes data blob output to also display the data as\n"
786       " EBCDIC characters.\n"
787 #define HLP_SET_BREAK  "*Commands SET Breakpoints"
788       "3Breakpoints\n"
789       "+set break <list>            set breakpoints\n"
790       "+set nobreak <list>          clear breakpoints\n"
791        /***************** 80 character line width template *************************/
792 #define HLP_SET_ENVIRON "*Commands SET Asynch"
793       "3Environment\n"
794       "+set environment name=val    set environment variable\n"
795       "+set environment name        clear environment variable\n"
796 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
797       "3Command Status Trap Dispatching\n"
798       "+set on                      enables error checking after command\n"
799       "++++++++                     execution\n"
800       "+set noon                    disables error checking after command\n"
801       "++++++++                     execution\n"
802       "+set on inherit              enables inheritance of ON state and\n"
803       "++++++++                     actions into do command files\n"
804       "+set on noinherit            disables inheritance of ON state and\n"
805       "++++++++                     actions into do command files\n"
806 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
807 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
808       "3Command Execution Display\n"
809       "+set verify                  re-enables display of command file\n"
810       "++++++++                     processed commands\n"
811       "+set verbose                 re-enables display of command file\n"
812       "++++++++                     processed commands\n"
813       "+set noverify                disables display of command file processed\n"
814       "++++++++                     commands\n"
815       "+set noverbose               disables display of command file processed\n"
816       "++++++++                     commands\n"
817 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
818       "3Command Error Status Display\n"
819       "+set message                 re-enables display of command file error\n"
820       "++++++++                     messages\n"
821       "+set nomessage               disables display of command file error\n"
822       "++++++++                     messages\n"
823 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
824       "3Command Output Display\n"
825       "+set quiet                   disables suppression of some output and\n"
826       "++++++++                     messages\n"
827       "+set noquiet                 re-enables suppression of some output and\n"
828       "++++++++                     messages\n"
829 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
830       "3Command Prompt\n"
831       "+set prompt \"string\"        sets an alternate simulator prompt string\n"
832       "3Device and Unit\n"
833       "+set <dev> OCT|DEC|HEX       set device display radix\n"
834       "+set <dev> ENABLED           enable device\n"
835       "+set <dev> DISABLED          disable device\n"
836       "+set <dev> DEBUG{=arg}       set device debug flags\n"
837       "+set <dev> NODEBUG={arg}     clear device debug flags\n"
838       "+set <dev> arg{,arg...}      set device parameters (see show modifiers)\n"
839       "+set <unit> ENABLED          enable unit\n"
840       "+set <unit> DISABLED         disable unit\n"
841       "+set <unit> arg{,arg...}     set unit parameters (see show modifiers)\n"
842       "+help <dev> set              displays the device specific set commands\n"
843       "++++++++                     available\n"
844        /***************** 80 character line width template *************************/
845 #define HLP_SHOW        "*Commands SHOW"
846       "2SHOW\n"
847       "+sh{ow} {-c} br{eak} <list>  show breakpoints\n"
848       "+sh{ow} con{figuration}      show configuration\n"
849       "+sh{ow} cons{ole} {arg}      show console options\n"
850       "+sh{ow} dev{ices}            show devices\n"
851       "+sh{ow} fea{tures}           show system devices with descriptions\n"
852       "+sh{ow} m{odifiers}          show modifiers for all devices\n"
853       "+sh{ow} s{how}               show SHOW commands for all devices\n"
854       "+sh{ow} n{ames}              show logical names\n"
855       "+sh{ow} q{ueue}              show event queue\n"
856       "+sh{ow} ti{me}               show simulated time\n"
857       "+sh{ow} b{uildinfo}          show build compilation information\n"
858       "+sh{ow} ve{rsion}            show simulator version\n"
859       "+sh{ow} p{rom}               show PROM initialization data\n"
860       "+sh{ow} <dev> RADIX          show device display radix\n"
861       "+sh{ow} <dev> DEBUG          show device debug flags\n"
862       "+sh{ow} <dev> MODIFIERS      show device modifiers\n"
863       "+sh{ow} <dev> NAMES          show device logical name\n"
864       "+sh{ow} <dev> SHOW           show device SHOW commands\n"
865       "+sh{ow} <dev> {arg,...}      show device parameters\n"
866       "+sh{ow} <unit> {arg,...}     show unit parameters\n"
867       "+sh{ow} clocks               show calibrated timers\n"
868       "+sh{ow} on                   show on condition actions\n"
869       "+h{elp} <dev> show           displays the device specific show commands\n"
870       "++++++++                     available\n"
871 #define HLP_SHOW_CONFIG         "*Commands SHOW"
872 #define HLP_SHOW_DEVICES        "*Commands SHOW"
873 #define HLP_SHOW_FEATURES       "*Commands SHOW"
874 #define HLP_SHOW_QUEUE          "*Commands SHOW"
875 #define HLP_SHOW_TIME           "*Commands SHOW"
876 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
877 #define HLP_SHOW_NAMES          "*Commands SHOW"
878 #define HLP_SHOW_SHOW           "*Commands SHOW"
879 #define HLP_SHOW_VERSION        "*Commands SHOW"
880 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
881 #define HLP_SHOW_PROM           "*Commands SHOW"
882 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
883 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
884 #define HLP_SHOW_REMOTE         "*Commands SHOW"
885 #define HLP_SHOW_BREAK          "*Commands SHOW"
886 #define HLP_SHOW_LOG            "*Commands SHOW"
887 #define HLP_SHOW_DEBUG          "*Commands SHOW"
888 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
889 #define HLP_SHOW_ON             "*Commands SHOW"
890 #define HLP_SHOW_SEND           "*Commands SHOW"
891 #define HLP_SHOW_EXPECT         "*Commands SHOW"
892 #define HLP_HELP                "*Commands HELP"
893        /***************** 80 character line width template *************************/
894       "2HELP\n"
895       "+h{elp}                      type this message\n"
896       "+h{elp} <command>            type help for command\n"
897       "+h{elp} <dev>                type help for device\n"
898       "+h{elp} <dev> registers      type help for device register variables\n"
899       "+h{elp} <dev> attach         type help for device specific ATTACH command\n"
900       "+h{elp} <dev> set            type help for device specific SET commands\n"
901       "+h{elp} <dev> show           type help for device specific SHOW commands\n"
902       "+h{elp} <dev> <command>      type help for device specific <command> command\n"
903        /***************** 80 character line width template *************************/
904       "2Altering The Simulated Configuration\n"
905       " In most simulators, the SET <device> DISABLED command removes the\n"
906       " specified device from the configuration.  A DISABLED device is invisible\n"
907       " to running programs.  The device can still be RESET, but it cannot be\n"
908       " ATTAChed, DETACHed, or BOOTed.  SET <device> ENABLED restores a disabled\n"
909       " device to a configuration.\n\n"
910       " Most multi-unit devices allow units to be enabled or disabled:\n\n"
911       "++SET <unit> ENABLED\n"
912       "++SET <unit> DISABLED\n\n"
913       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
914 #define HLP_ASSIGN      "*Commands Logical_Names"
915 #define HLP_DEASSIGN    "*Commands Logical_Names"
916       "2Logical Names\n"
917       " The standard device names can be supplemented with logical names.  Logical\n"
918       " names must be unique within a simulator (that is, they cannot be the same\n"
919       " as an existing device name).  To assign a logical name to a device:\n\n"
920       "++ASSIGN <device> <log-name>      assign log-name to device\n\n"
921       " To remove a logical name:\n\n"
922       "++DEASSIGN <device>               remove logical name\n\n"
923       " To show the current logical name assignment:\n\n"
924       "++SHOW <device> NAMES            show logical name, if any\n\n"
925       " To show all logical names:\n\n"
926       "++SHOW NAMES\n\n"
927        /***************** 80 character line width template *************************/
928 #define HLP_DO          "*Commands Executing_Command_Files"
929       "2Executing Command Files\n"
930       " The simulator can execute command files with the DO command:\n\n"
931       "++DO <filename> {arguments...}       execute commands in file\n\n"
932       " The DO command allows command files to contain substitutable arguments.\n"
933       " The string %%n, where n is between 1 and 9, is replaced with argument n\n"
934       " from the DO command line. The string %%0 is replaced with <filename>.\n"
935       " The sequences \\%% and \\\\ are replaced with the literal characters %% and \\,\n"
936       " respectively.  Arguments with spaces can be enclosed in matching single\n"
937       " or double quotation marks.\n\n"
938       " DO commands may be nested up to ten invocations deep.\n\n"
939       "3Switches\n"
940       " If the switch -v is specified, the commands in the file are echoed before\n"
941       " they are executed.\n\n"
942       " If the switch -e is specified, command processing (including nested command\n"
943       " invocations) will be aborted if a command error is encountered.\n"
944       " (Simulation stop never abort processing; use ASSERT to catch unexpected\n"
945       " stops.)  Without the switch, all errors except ASSERT failures will be\n"
946       " ignored, and command processing will continue.\n\n"
947       " If the switch -o is specified, the on conditions and actions from the\n"
948       " calling command file will be inherited in the command file being invoked.\n"
949       " If the switch -q is specified, the quiet mode will be explicitly enabled\n"
950       " for the called command file, otherwise quiet mode is inherited from the\n"
951       " calling context.\n"
952        /***************** 80 character line width template *************************/
953 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
954       "3GOTO\n"
955       " Commands in a command file execute in sequence until either an error\n"
956       " trap occurs (when a command completes with an error status), or when an\n"
957       " explict request is made to start command execution elsewhere with the\n"
958       " GOTO command:\n\n"
959       "++GOTO <label>\n\n"
960       " Labels are lines in a command file which the first non whitespace\n"
961       " character is a \":\".  The target of a goto is the first matching label\n"
962       " in the current do command file which is encountered.  Since labels\n"
963       " don't do anything else besides being the targets of goto's, they could\n"
964       " also be used to provide comments in do command files.\n\n"
965       "4Examples\n\n"
966       "++:: This is a comment\n"
967       "++echo Some Message to Output\n"
968       "++:Target\n"
969       "++:: This is a comment\n"
970       "++GOTO Target\n\n"
971 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
972        /***************** 80 character line width template *************************/
973       "3RETURN\n"
974       " The RETURN command causes the current procedure call to be restored to the\n"
975       " calling context, possibly returning a specific return status.\n"
976       " If no return status is specified, the return status from the last command\n"
977       " executed will be returned.  The calling context may have ON traps defined\n"
978       " which may redirect command flow in that context.\n\n"
979       "++return                   return from command file with last command status\n"
980       "++return {-Q} <status>     return from command file with specific status\n\n"
981       " The status return can be any numeric value or one of the standard SCPE_\n"
982       " condition names.\n\n"
983       " The -Q switch on the RETURN command will cause the specified status to\n"
984       " be returned, but normal error status message printing to be suppressed.\n"
985       "4Condition Names\n"
986       " The available standard SCPE_ condition names are\n"
987       "5 NXM\n"
988       " Address space exceeded\n"
989       "5 UNATT\n"
990       " Unit not attached\n"
991       "5 IOERR\n"
992       " I/O error\n"
993       "5 CSUM\n"
994       " Checksum error\n"
995       "5 FMT\n"
996       " Format error\n"
997       "5 NOATT\n"
998       " Unit not attachable\n"
999       "5 OPENERR\n"
1000       " File open error\n"
1001       "5 MEM\n"
1002       " Memory exhausted\n"
1003       "5 ARG\n"
1004       " Invalid argument\n"
1005       "5 STEP\n"
1006       " Step expired\n"
1007       "5 UNK\n"
1008       " Unknown command\n"
1009       "5 RO\n"
1010       " Read only argument\n"
1011       "5 INCOMP\n"
1012       " Command not completed\n"
1013       "5 STOP\n"
1014       " Simulation stopped\n"
1015       "5 EXIT\n"
1016       " Goodbye\n"
1017       "5 TTIERR\n"
1018       " Console input I/O error\n"
1019       "5 TTOERR\n"
1020       " Console output I/O error\n"
1021       "5 EOF\n"
1022       " End of file\n"
1023       "5 REL\n"
1024       " Relocation error\n"
1025       "5 NOPARAM\n"
1026       " No settable parameters\n"
1027       "5 ALATT\n"
1028       " Unit already attached\n"
1029       "5 TIMER\n"
1030       " Hardware timer error\n"
1031       "5 SIGERR\n"
1032       " Signal handler setup error\n"
1033       "5 TTYERR\n"
1034       " Console terminal setup error\n"
1035       "5 NOFNC\n"
1036       " Command not allowed\n"
1037       "5 UDIS\n"
1038       " Unit disabled\n"
1039       "5 NORO\n"
1040       " Read only operation not allowed\n"
1041       "5 INVSW\n"
1042       " Invalid switch\n"
1043       "5 MISVAL\n"
1044       " Missing value\n"
1045       "5 2FARG\n"
1046       " Too few arguments\n"
1047       "5 2MARG\n"
1048       " Too many arguments\n"
1049       "5 NXDEV\n"
1050       " Non-existent device\n"
1051       "5 NXUN\n"
1052       " Non-existent unit\n"
1053       "5 NXREG\n"
1054       " Non-existent register\n"
1055       "5 NXPAR\n"
1056       " Non-existent parameter\n"
1057       "5 NEST\n"
1058       " Nested DO command limit exceeded\n"
1059       "5 IERR\n"
1060       " Internal error\n"
1061       "5 MTRLNT\n"
1062       " Invalid magtape record length\n"
1063       "5 LOST\n"
1064       " Console Telnet connection lost\n"
1065       "5 TTMO\n"
1066       " Console Telnet connection timed out\n"
1067       "5 STALL\n"
1068       " Console Telnet output stall\n"
1069       "5 AFAIL\n"
1070       " Assertion failed\n"
1071       "5 INVREM\n"
1072       " Invalid remote console command\n"
1073 #define HLP_SHIFT       "*Commands Executing_Command_Files SHIFT"
1074       "3SHIFT\n"
1075       "++shift                    shift the command file's positional parameters\n"
1076 #define HLP_CALL        "*Commands Executing_Command_Files CALL"
1077       "3CALL\n"
1078       "++call                     transfer control to a labeled subroutine\n"
1079 #define HLP_ON          "*Commands Executing_Command_Files ON"
1080       "3ON\n"
1081       "++on <condition> <action>  perform action(s) after condition\n"
1082       "++on <condition>           clear action for specific condition\n"
1083 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED"
1084 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED"
1085        /***************** 80 character line width template *************************/
1086       "3PROCEED/IGNORE\n"
1087       " The PROCEED or IGNORE commands do nothing.  They are potentially useful\n"
1088       " placeholders for an ON action condition which should be explicitly ignored\n"
1089       "++proceed                  continue command file execution without doing anything\n"
1090       "++ignore                   continue command file execution without doing anything\n"
1091 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1092        /***************** 80 character line width template *************************/
1093       "3Displaying Arbitrary Text\n"
1094       " The ECHO command is a useful way of annotating command files.  ECHO prints\n"
1095       " out its arguments on the console (and log):\n\n"
1096       "++ECHO <string>      output string to console\n\n"
1097       " If there is no argument, ECHO prints a blank line on the console.  This\n"
1098       " may be used to provide spacing in the console display or log.\n"
1099        /***************** 80 character line width template *************************/
1100 #define HLP_SEND        "*Commands Executing_Command_Files Injecting_Console_Input"
1101        /***************** 80 character line width template *************************/
1102       "3Injecting Console Input\n"
1103       " The SEND command provides a way to insert input into the console device of\n"
1104       " a simulated system as if it was entered by a user.\n\n"
1105       "++SEND {-t} {after=nn,}{delay=nn,}\"<string>\"\n\n"
1106       " The string argument must be delimited by quote characters.  Quotes may\n"
1107       " be either single or double but the opening and closing quote characters\n"
1108       " must match.  Data in the string may contain escaped character strings.\n\n"
1109       " The SEND command can also insert input into any serial device on a\n"
1110       " simulated system as if it was entered by a user.\n\n"
1111       "++SEND {-t} <dev>:line {after=nn,}{delay=nn,}\"<string>\"\n\n"
1112       "4Delay\n"
1113       " Specifies a positive integer representing a minimal instruction delay\n"
1114       " between characters being sent.  The value specified in a delay\n"
1115       " argument persists across SEND commands to the same device (console or\n"
1116       " serial device).  The delay parameter can be set by itself with:\n\n"
1117       "++SEND DELAY=n\n\n"
1118       " The default value of the delay parameter is 1000.\n"
1119        /***************** 80 character line width template *************************/
1120       "4After\n"
1121       " Specifies a positive integer representing a minimal number of instructions\n"
1122       " which must execute before the first character in the string is sent.\n"
1123       " The value specified as the after parameter persists across SEND commands\n"
1124       " to the same device (console or serial device).   The after parameter value\n"
1125       " can be set by itself with:\n\n"
1126       "++SEND AFTER=n\n\n"
1127       " If the after parameter isn't explicitly set, it defaults to the value of\n"
1128       " the delay parameter.\n"
1129       "4Escaping String Data\n"
1130       " The following character escapes are explicitly supported:\n"
1131       "++\\r  Sends the ASCII Carriage Return character (Decimal value 13)\n"
1132       "++\\n  Sends the ASCII Linefeed character (Decimal value 10)\n"
1133       "++\\f  Sends the ASCII Formfeed character (Decimal value 12)\n"
1134       "++\\t  Sends the ASCII Horizontal Tab character (Decimal value 9)\n"
1135       "++\\v  Sends the ASCII Vertical Tab character (Decimal value 11)\n"
1136       "++\\b  Sends the ASCII Backspace character (Decimal value 8)\n"
1137       "++\\\\  Sends the ASCII Backslash character (Decimal value 92)\n"
1138       "++\\'  Sends the ASCII Single Quote character (Decimal value 39)\n"
1139       "++\\\"  Sends the ASCII Double Quote character (Decimal value 34)\n"
1140       "++\\?  Sends the ASCII Question Mark character (Decimal value 63)\n"
1141       "++\\e  Sends the ASCII Escape character (Decimal value 27)\n"
1142       " as well as octal character values of the form:\n"
1143       "++\\n{n{n}} where each n is an octal digit (0-7)\n"
1144       " and hext character values of the form:\n"
1145       "++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n"
1146       "4Switches\n"
1147       " Switches can be used to influence the behavior of SEND commands\n\n"
1148       "5-t\n"
1149       " The -t switch indicates that the Delay and After values are in\n"
1150       " units of microseconds rather than instructions.\n"
1151        /***************** 80 character line width template *************************/
1152 #define HLP_EXPECT      "*Commands Executing_Command_Files Reacting_To_Console_Output"
1153        /***************** 80 character line width template *************************/
1154       "3Reacting To Console Output\n"
1155       " The EXPECT command provides a way to stop execution and take actions\n"
1156       " when specific output has been generated by the simulated system.\n"
1157       "++EXPECT {dev:line} {[count]} {HALTAFTER=n,}\"<string>\" {actioncommand {; actioncommand}...}\n\n"
1158       " The string argument must be delimited by quote characters.  Quotes may\n"
1159       " be either single or double but the opening and closing quote characters\n"
1160       " must match.  Data in the string may contain escaped character strings.\n"
1161       " If a [count] is specified, the rule will match after the match string\n"
1162       " has matched count times.\n\n"
1163       " When multiple expect rules are defined with the same match string, they\n"
1164       " will match in the same order they were defined in.\n\n"
1165       " When expect rules are defined, they are evaluated agains recently\n"
1166       " produced output as each character is output to the device.  Since this\n"
1167       " evaluation processing is done on each output character, rule matching\n"
1168       " is not specifically line oriented.  If line oriented matching is desired\n"
1169       " then rules should be defined which contain the simulated system's line\n"
1170       " ending character sequence (i.e. \"\\r\\n\").\n"
1171       " Once data has matched any expect rule, that data is no longer eligible\n"
1172       " to match other expect rules which may already be defined.\n"
1173       " Data which is output prior to the definition of an expect rule is not\n"
1174       " eligible to be matched against.\n"
1175        /***************** 80 character line width template *************************/
1176       "4Switches\n"
1177       " Switches can be used to influence the behavior of EXPECT rules\n\n"
1178       "5-p\n"
1179       " EXPECT rules default to be one shot activities.  That is a rule is\n"
1180       " automatically removed when it matches unless it is designated as a\n"
1181       " persistent rule by using a -p switch when the rule is defined.\n"
1182       "5-c\n"
1183       " If an expect rule is defined with the -c switch, it will cause all\n"
1184       " pending expect rules on the current device to be cleared when the rule\n"
1185       " matches data in the device output stream.\n"
1186       "5-r\n"
1187       " If an expect rule is defined with the -r switch, the string is interpreted\n"
1188       " as a regular expression applied to the output data stream.  This regular\n"
1189       " expression may contain parentheses delimited sub-groups.\n\n"
1190        /***************** 80 character line width template *************************/
1191       " Regular expression support is not currently available on your environment.\n"
1192       " This simulator could use regular expression support provided by the\n"
1193       " Perl Compatible Regular Expression (PCRE) package if it was available\n"
1194       " when you simulator was compiled.\n"
1195       "5-i\n"
1196       " If a regular expression expect rule is defined with the -i switch,\n"
1197       " character matching for that expression will be case independent.\n"
1198       " The -i switch is only valid for regular expression expect rules.\n"
1199       "5-t\n"
1200       " The -t switch indicates that the value specified by the HaltAfter\n"
1201       " parameter are in units of microseconds rather than instructions.\n"
1202       "4Determining Which Output Matched\n"
1203       " When an expect rule matches data in the output stream, the rule which\n"
1204       " matched is recorded in the environment variable _EXPECT_MATCH_PATTERN.\n"
1205       " If the expect rule was a regular expression rule, then the environment\n"
1206       " variable _EXPECT_MATCH_GROUP_0 is set to the whole string which matched\n"
1207       " and if the match pattern had any parentheses delimited sub-groups, the\n"
1208       " environment variables _EXPECT_MATCH_PATTERN_1 thru _EXPECT_MATCH_PATTERN_n\n"
1209       " are set to the values within the string which matched the respective\n"
1210       " sub-groups.\n"
1211        /***************** 80 character line width template *************************/
1212       "4Escaping String Data\n"
1213       " The following character escapes are explicitly supported when NOT using\n"
1214       " regular expression match patterns:\n"
1215       "++\\r  Expect the ASCII Carriage Return character (Decimal value 13)\n"
1216       "++\\n  Expect the ASCII Linefeed character (Decimal value 10)\n"
1217       "++\\f  Expect the ASCII Formfeed character (Decimal value 12)\n"
1218       "++\\t  Expect the ASCII Horizontal Tab character (Decimal value 9)\n"
1219       "++\\v  Expect the ASCII Vertical Tab character (Decimal value 11)\n"
1220       "++\\b  Expect the ASCII Backspace character (Decimal value 8)\n"
1221       "++\\\\  Expect the ASCII Backslash character (Decimal value 92)\n"
1222       "++\\'  Expect the ASCII Single Quote character (Decimal value 39)\n"
1223       "++\\\"  Expect the ASCII Double Quote character (Decimal value 34)\n"
1224       "++\\?  Expect the ASCII Question Mark character (Decimal value 63)\n"
1225       "++\\e  Expect the ASCII Escape character (Decimal value 27)\n"
1226       " as well as octal character values of the form:\n"
1227       "++\\n{n{n}} where each n is an octal digit (0-7)\n"
1228       " and hext character values of the form:\n"
1229       "++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n"
1230       "4HaltAfter\n"
1231       " Specifies the number of instructions which should be executed before\n"
1232       " simulator instruction execution should stop.  The default is to stop\n"
1233       " executing instructions immediately (i.e. HALTAFTER=0).\n"
1234       " The HaltAfter delay, once set, persists for all expect behaviors for\n"
1235       " that device.\n"
1236       " The HaltAfter parameter value can be set by itself with:\n\n"
1237       "++EXPECT HALTAFTER=n\n\n"
1238       " To avoid potentially unpredictable system hehavior that will happen\n"
1239       " if multiple expect rules are in effect and a haltafter value is large\n"
1240       " enough for more than one expect rule to match before an earlier haltafter\n"
1241       " delay has expired, only a single EXPECT rule can be defined if a non-zero\n"
1242       " HaltAfter parameter has been set.\n"
1243       /***************** 80 character line width template *************************/
1244 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Simulator_State"
1245 #define HLP_IF          "*Commands Executing_Command_Files Testing_Simulator_State"
1246       "3Testing Simulator State\n"
1247       " There are two ways for a command file to examine simulator state and\n"
1248       " then take action based on that state:\n"
1249       "4ASSERT\n"
1250       " The ASSERT command tests a simulator state condition and halts command\n"
1251       " file execution if the condition is false:\n\n"
1252       "++ASSERT <Simulator State Expressions>\n\n"
1253       " If the indicated expression evaluates to false, the command completes\n"
1254       " with an AFAIL condition.  By default, when a command file encounters a\n"
1255       " command which returns the AFAIL condition, it will exit the running\n"
1256       " command file with the AFAIL status to the calling command file.  This\n"
1257       " behavior can be changed with the ON command as well as switches to the\n"
1258       " invoking DO command.\n\n"
1259       "5Examples:\n"
1260       " A command file might be used to bootstrap an operating system that\n"
1261       " halts after the initial load from disk.  The ASSERT command is then\n"
1262       " used to confirm that the load completed successfully by examining the\n"
1263       " CPU's \"A\" register for the expected value:\n\n"
1264       "++; OS bootstrap command file\n"
1265       "++;\n"
1266       "++ATTACH DS0 os.disk\n"
1267       "++BOOT DS\n"
1268       "++; A register contains error code; 0 = good boot\n"
1269       "++ASSERT A=0\n"
1270       "++ATTACH MT0 sys.tape\n"
1271       "++ATTACH MT1 user.tape\n"
1272       "++RUN\n\n"
1273        /***************** 80 character line width template *************************/
1274       " In the example, if the A register is not 0, the \"ASSERT A=0\" command will\n"
1275       " be echoed, the command file will be aborted with an \"Assertion failed\"\n"
1276       " message.  Otherwise, the command file will continue to bring up the\n"
1277       " operating system.\n"
1278       "4IF\n"
1279       " The IF command tests a simulator state condition and executes additional\n"
1280       " commands if the condition is true:\n\n"
1281       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommandtoprocess}...\n\n"
1282       "5Examples:\n"
1283       " A command file might be used to bootstrap an operating system that\n"
1284       " halts after the initial load from disk.  The ASSERT command is then\n"
1285       " used to confirm that the load completed successfully by examining the\n"
1286       " CPU's \"A\" register for the expected value:\n\n"
1287       "++; OS bootstrap command file\n"
1288       "++;\n"
1289       "++ATTACH DS0 os.disk\n"
1290       "++BOOT DS\n"
1291       "++; A register contains error code; 0 = good boot\n"
1292       "++IF NOT A=0 echo Boot failed - Failure Code; EX A; exit AFAIL\n"
1293       "++ATTACH MT0 sys.tape\n"
1294       "++ATTACH MT1 user.tape\n"
1295       "++RUN\n\n"
1296        /***************** 80 character line width template *************************/
1297       " In the example, if the A register is not 0, the message \"Boot failed -\n"
1298       " Failure Code:\" command will be displayed, the contents of the A register\n"
1299       " will be displayed and the command file will be aborted with an \"Assertion\n"
1300       " failed\" message.  Otherwise, the command file will continue to bring up\n"
1301       " the operating system.\n"
1302       "4Conditional Expressions\n"
1303       " The IF and ASSERT commands evaluate two different forms of conditional\n"
1304       " expressions.:\n\n"
1305       "5Simulator State Expressions\n"
1306       " The values of simulator registers can be evaluated with:\n\n"
1307       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1308       " If <dev> is not specified, CPU is assumed.  <reg> is a register (scalar\n"
1309       " or subscripted) belonging to the indicated device.  <addr> is an address\n"
1310       " in the address space of the indicated device.  The <conditional-op>\n"
1311       " and optional <logical-op> are the same as those used for \"search\n"
1312       " specifiers\" by the EXAMINE and DEPOSIT commands.  The <value>s are\n"
1313       " expressed in the radix specified for <reg>, not in the radix for the\n"
1314       " device when referencing a register and when an address is referenced\n"
1315       " the device radix is used as the default.\n\n"
1316       " If the <logical-op> and <value> are specified, the target register value\n"
1317       " is first altered as indicated.  The result is then compared to the\n"
1318       " <value> via the <conditional-op>.  If the result is true, the additional\n"
1319       " command(s) are executed before proceeding to the next line in the command\n"
1320       " file.  Otherwise, the next command in the command file is processed.\n\n"
1321       "5String Comparison Expressions\n"
1322       " String Values can be compared with:\n"
1323       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1324       " The -i switch, if present, causes comparisons to be case insensitive.\n"
1325       " <string1> and <string2> are quoted string values which may have\n"
1326       " environment variables substituted as desired.\n"
1327       " <compare-op> may be one of:\n\n"
1328       "++==  - equal\n"
1329       "++EQU - equal\n"
1330       "++!=  - not equal\n"
1331       "++NEQ - not equal\n"
1332       "++<   - less than\n"
1333       "++LSS - less than\n"
1334       "++<=  - less than or equal\n"
1335       "++LEQ - less than or equal\n"
1336       "++>   - greater than\n"
1337       "++GTR - greater than\n"
1338       "++>=  - greater than or equal\n"
1339       "++GEQ - greater than or equal\n\n"
1340       " Comparisons are generic.  This means that if both string1 and string2 are\n"
1341       " comprised of all numeric digits, then the strings are converted to numbers\n"
1342       " and a numeric comparison is performed. For example: \"+1\" EQU \"1\" will be\n"
1343       " true.\n"
1344        /***************** 80 character line width template *************************/
1345 #define HLP_EXIT        "*Commands Exiting_The_Simulator"
1346       "2Exiting The Simulator\n"
1347       " EXIT (synonyms QUIT and BYE) returns control to the operating system.\n"
1348        /***************** 80 character line width template *************************/
1349 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1350       "2Executing System Commands\n"
1351       " The simulator can execute operating system commands with the ! (spawn)\n"
1352       " command:\n\n"
1353       "++!                    execute local command interpreter\n"
1354       "++! <command>          execute local host command\n"
1355       " If no operating system command is provided, the simulator attempts to\n"
1356       " launch the host operating system's command shell.\n"
1357       " The exit status from the command which was executed is set as the command\n"
1358       " completion status for the ! command.  This may influence any enabled ON\n"
1359       " condition traps\n";
1360 
1361 static CTAB cmd_table[] = {
1362     { "RESET",      &reset_cmd,     0,          HLP_RESET },
1363     { "EXAMINE",    &exdep_cmd,     EX_E,       HLP_EXAMINE },
1364     { "IEXAMINE",   &exdep_cmd,     EX_E+EX_I,  HLP_IEXAMINE },
1365     { "DEPOSIT",    &exdep_cmd,     EX_D,       HLP_DEPOSIT },
1366     { "IDEPOSIT",   &exdep_cmd,     EX_D+EX_I,  HLP_IDEPOSIT },
1367     { "EVALUATE",   &eval_cmd,      0,          HLP_EVALUATE },
1368     { "RUN",        &run_cmd,       RU_RUN,     HLP_RUN,        NULL, &run_cmd_message },
1369     { "GO",         &run_cmd,       RU_GO,      HLP_GO,         NULL, &run_cmd_message },
1370     { "STEP",       &run_cmd,       RU_STEP,    HLP_STEP,       NULL, &run_cmd_message },
1371     { "NEXT",       &run_cmd,       RU_NEXT,    HLP_NEXT,       NULL, &run_cmd_message },
1372     { "CONTINUE",   &run_cmd,       RU_CONT,    HLP_CONTINUE,   NULL, &run_cmd_message },
1373     { "BOOT",       &run_cmd,       RU_BOOT,    HLP_BOOT,       NULL, &run_cmd_message },
1374     { "BREAK",      &brk_cmd,       SSH_ST,     HLP_BREAK },
1375     { "NOBREAK",    &brk_cmd,       SSH_CL,     HLP_NOBREAK },
1376     { "ATTACH",     &attach_cmd,    0,          HLP_ATTACH },
1377     { "DETACH",     &detach_cmd,    0,          HLP_DETACH },
1378     { "ASSIGN",     &assign_cmd,    0,          HLP_ASSIGN },
1379     { "DEASSIGN",   &deassign_cmd,  0,          HLP_DEASSIGN },
1380     { "SAVE",       &save_cmd,      0,          HLP_SAVE  },
1381     { "RESTORE",    &restore_cmd,   0,          HLP_RESTORE },
1382     { "GET",        &restore_cmd,   0,          NULL },
1383     { "EXIT",       &exit_cmd,      0,          HLP_EXIT },
1384     { "QUIT",       &exit_cmd,      0,          NULL },
1385     { "BYE",        &exit_cmd,      0,          NULL },
1386     { "SET",        &set_cmd,       0,          HLP_SET },
1387     { "SHOW",       &show_cmd,      0,          HLP_SHOW },
1388     { "DO",         &do_cmd,        1,          HLP_DO },
1389     { "GOTO",       &goto_cmd,      1,          HLP_GOTO },
1390     { "RETURN",     &return_cmd,    0,          HLP_RETURN },
1391     { "SHIFT",      &shift_cmd,     0,          HLP_SHIFT },
1392     { "CALL",       &call_cmd,      0,          HLP_CALL },
1393     { "ON",         &on_cmd,        0,          HLP_ON },
1394     { "IF",         &assert_cmd,    0,          HLP_IF },
1395     { "PROCEED",    &noop_cmd,      0,          HLP_PROCEED },
1396     { "IGNORE",     &noop_cmd,      0,          HLP_IGNORE },
1397     { "ECHO",       &echo_cmd,      0,          HLP_ECHO },
1398     { "ASSERT",     &assert_cmd,    1,          HLP_ASSERT },
1399     { "SEND",       &send_cmd,      0,          HLP_SEND },
1400     { "EXPECT",     &expect_cmd,    1,          HLP_EXPECT },
1401     { "NOEXPECT",   &expect_cmd,    0,          HLP_EXPECT },
1402     { "!",          &spawn_cmd,     0,          HLP_SPAWN },
1403     { "HELP",       &help_cmd,      0,          HLP_HELP },
1404     { NULL, NULL, 0 }
1405     };
1406 
1407 static CTAB set_glob_tab[] = {
1408     { "CONSOLE",     &sim_set_console,          0, HLP_SET_CONSOLE },
1409     { "REMOTE",      &sim_set_remote_console,   0, HLP_SET_REMOTE },
1410     { "BREAK",       &brk_cmd,             SSH_ST, HLP_SET_BREAK },
1411     { "NOBREAK",     &brk_cmd,             SSH_CL, HLP_SET_BREAK },
1412     { "TELNET",      &sim_set_telnet,           0 }, /* deprecated */
1413     { "NOTELNET",    &sim_set_notelnet,         0 }, /* deprecated */
1414     { "LOG",         &sim_set_logon,            0, HLP_SET_LOG  },
1415     { "NOLOG",       &sim_set_logoff,           0, HLP_SET_LOG  },
1416     { "DEBUG",       &sim_set_debon,            0, HLP_SET_DEBUG  },
1417     { "NODEBUG",     &sim_set_deboff,           0, HLP_SET_DEBUG  },
1418     { "ENVIRONMENT", &sim_set_environment,      1, HLP_SET_ENVIRON },
1419     { "ON",          &set_on,                   1, HLP_SET_ON },
1420     { "NOON",        &set_on,                   0, HLP_SET_ON },
1421     { "VERIFY",      &set_verify,               1, HLP_SET_VERIFY },
1422     { "VERBOSE",     &set_verify,               1, HLP_SET_VERIFY },
1423     { "NOVERIFY",    &set_verify,               0, HLP_SET_VERIFY },
1424     { "NOVERBOSE",   &set_verify,               0, HLP_SET_VERIFY },
1425     { "MESSAGE",     &set_message,              1, HLP_SET_MESSAGE },
1426     { "NOMESSAGE",   &set_message,              0, HLP_SET_MESSAGE },
1427     { "QUIET",       &set_quiet,                1, HLP_SET_QUIET },
1428     { "NOQUIET",     &set_quiet,                0, HLP_SET_QUIET },
1429     { "PROMPT",      &set_prompt,               0, HLP_SET_PROMPT },
1430     { NULL,          NULL,                      0 }
1431     };
1432 
1433 static C1TAB set_dev_tab[] = {
1434     { "OCTAL",      &set_dev_radix,     8 },
1435     { "DECIMAL",    &set_dev_radix,     10 },
1436     { "HEX",        &set_dev_radix,     16 },
1437     { "ENABLED",    &set_dev_enbdis,    1 },
1438     { "DISABLED",   &set_dev_enbdis,    0 },
1439     { "DEBUG",      &set_dev_debug,     1 },
1440     { "NODEBUG",    &set_dev_debug,     0 },
1441     { NULL,         NULL,               0 }
1442     };
1443 
1444 static C1TAB set_unit_tab[] = {
1445     { "ENABLED",    &set_unit_enbdis,   1 },
1446     { "DISABLED",   &set_unit_enbdis,   0 },
1447     { NULL,         NULL,               0 }
1448     };
1449 
1450 static SHTAB show_glob_tab[] = {
1451     { "CONFIGURATION",  &show_config,               0, HLP_SHOW_CONFIG },
1452     { "DEVICES",        &show_config,               1, HLP_SHOW_DEVICES },
1453     { "FEATURES",       &show_config,               2, HLP_SHOW_FEATURES },
1454     { "QUEUE",          &show_queue,                0, HLP_SHOW_QUEUE },
1455     { "TIME",           &show_time,                 0, HLP_SHOW_TIME },
1456     { "MODIFIERS",      &show_mod_names,            0, HLP_SHOW_MODIFIERS },
1457     { "NAMES",          &show_log_names,            0, HLP_SHOW_NAMES },
1458     { "SHOW",           &show_show_commands,        0, HLP_SHOW_SHOW },
1459     { "VERSION",        &show_version,              1, HLP_SHOW_VERSION },
1460     { "BUILDINFO",      &show_buildinfo,            1, HLP_SHOW_BUILDINFO },
1461     { "PROM",           &show_prom,                 0, HLP_SHOW_PROM },
1462     { "CONSOLE",        &sim_show_console,          0, HLP_SHOW_CONSOLE },
1463     { "REMOTE",         &sim_show_remote_console,   0, HLP_SHOW_REMOTE },
1464     { "BREAK",          &show_break,                0, HLP_SHOW_BREAK },
1465     { "LOG",            &sim_show_log,              0, HLP_SHOW_LOG },
1466     { "TELNET",         &sim_show_telnet,           0 },    /* deprecated */
1467     { "DEBUG",          &sim_show_debug,            0, HLP_SHOW_DEBUG },
1468     { "CLOCKS",         &sim_show_timers,           0, HLP_SHOW_CLOCKS },
1469     { "SEND",           &sim_show_send,             0, HLP_SHOW_SEND },
1470     { "EXPECT",         &sim_show_expect,           0, HLP_SHOW_EXPECT },
1471     { "ON",             &show_on,                   0, HLP_SHOW_ON },
1472     { NULL,             NULL,                       0 }
1473     };
1474 
1475 static SHTAB show_dev_tab[] = {
1476     { "RADIX",      &show_dev_radix,            0 },
1477     { "DEBUG",      &show_dev_debug,            0 },
1478     { "MODIFIERS",  &show_dev_modifiers,        0 },
1479     { "NAMES",      &show_dev_logicals,         0 },
1480     { "SHOW",       &show_dev_show_commands,    0 },
1481     { NULL,         NULL,                       0 }
1482     };
1483 
1484 static SHTAB show_unit_tab[] = {
1485     { NULL, NULL, 0 }
1486     };
1487 
1488 #if defined(_WIN32)
1489 static
setenv(const char * envname,const char * envval,int overwrite)1490 int setenv(const char *envname, const char *envval, int overwrite)
1491 {
1492 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1493 int r;
1494 
1495 sprintf(envstr, "%s=%s", envname, envval);
1496 r = _putenv(envstr);
1497 free(envstr);
1498 return r;
1499 }
1500 
1501 static
unsetenv(const char * envname)1502 int unsetenv(const char *envname)
1503 {
1504 setenv(envname, "", 1);
1505 return 0;
1506 }
1507 #endif
1508 
1509 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1510 
1511 /* Check if running on Rosetta 2 */
1512 
1513 #if defined(__APPLE__)
processIsTranslated(void)1514 int processIsTranslated(void)
1515 {
1516     int ret = 0;
1517     size_t size = sizeof(ret);
1518     if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1519         if (errno == ENOENT)
1520             return 0;
1521         return -1; }
1522     return ret;
1523 }
1524 #endif
1525 
1526 /* Substring removal hack */
1527 
strremove(char * str,const char * sub)1528 char *strremove(char *str, const char *sub)
1529 {
1530     char *p, *q, *r;
1531     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1532         size_t len = strlen(sub);
1533         while ((r = strstr(p = r + len, sub)) != NULL) {
1534             while (p < r)
1535                 *q++ = *p++;
1536         }
1537         while ((*q++ = *p++) != '\0')
1538             continue;
1539     }
1540     return str;
1541 }
1542 
1543 /* Main command loop */
1544 
1545 #ifndef PERF_STRIP
main(int argc,char * argv[])1546 int main (int argc, char *argv[])
1547 {
1548 char *cptr, *cptr2;
1549 char nbuf[PATH_MAX + 7];
1550 char cbuf[4*CBUFSIZE];
1551 char **targv = NULL;
1552 int32 i, sw;
1553 t_bool lookswitch;
1554 t_stat stat;
1555 
1556 # ifdef __MINGW32__
1557 #  ifndef NEED_CONSOLE_SETUP
1558 #   define NEED_CONSOLE_SETUP
1559 #  endif
1560 # endif /* ifdef __MINGW32__ */
1561 
1562 # ifdef CROSS_MINGW32
1563 #  ifndef NEED_CONSOLE_SETUP
1564 #   define NEED_CONSOLE_SETUP
1565 #  endif
1566 # endif /* ifdef CROSS_MINGW32 */
1567 
1568 # ifdef __MINGW64__
1569 #  ifndef NEED_CONSOLE_SETUP
1570 #   define NEED_CONSOLE_SETUP
1571 #  endif
1572 # endif /* ifdef __MINGW64__ */
1573 
1574 # ifdef CROSS_MINGW64
1575 #  ifndef NEED_CONSOLE_SETUP
1576 #   define NEED_CONSOLE_SETUP
1577 #  endif
1578 # endif /* ifdef CROSS_MINGW64 */
1579 
1580 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1581 #  include <windows.h>
1582 #  ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
1583 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1584 #  endif
1585 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1586 
1587 # ifdef NEED_CONSOLE_SETUP
1588 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1589 if (handle != INVALID_HANDLE_VALUE)
1590   {
1591     DWORD mode = 0;
1592     if (GetConsoleMode(handle, &mode))
1593       {
1594         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1595         SetConsoleMode(handle, mode);
1596       }
1597   }
1598 puts ("\e[0m");
1599 # endif /* NEED_CONSOLE_SETUP */
1600 
1601 /* endian-ness sanity test */
1602 int testEndian = decContextTestEndian(1);
1603 if (testEndian != 0) {
1604   if (testEndian == 1) {
1605     fprintf (stderr,
1606       "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1607     return 0;
1608   }
1609   if (testEndian == -1) {
1610     fprintf (stderr,
1611       "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1612     return 0;
1613   }
1614   fprintf (stderr,
1615     "Error: Unable to determine system byte order; aborting.\n");
1616   return 0;
1617 }
1618 
1619 /* invocation sanity check */
1620 if (argc == 0) {
1621     fprintf (stderr, "Error: main() called directly!\n");
1622     return 0;
1623 }
1624 
1625 /* patch intel dispatcher */
1626 # ifdef __DISPATCH_H_
1627 #  if defined(__INTEL_COMPILER)       || \
1628      defined(__INTEL_CLANG_COMPILER) || \
1629      defined(__INTEL_LLVM_COMPILER)  || \
1630      defined(INTEL_MKL_VERSION)      || \
1631      defined(__INTEL_MKL__)
1632 (void)agner_compiler_patch();
1633 #  endif
1634 # endif
1635 
1636 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1637 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1638 for (i=0; i<argc; i++)
1639     targv[i] = argv[i];
1640 argv = targv;
1641 
1642 /* setup defaults */
1643 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1644 *cbuf = 0;                                              /* init arg buffer */
1645 sim_switches = 0;                                       /* init switches */
1646 lookswitch = TRUE;
1647 stdnul = fopen(NULL_DEVICE,"wb");
1648 
1649 /* process arguments */
1650 for (i = 1; i < argc; i++) {                            /* loop thru args */
1651     if (argv[i] == NULL)                                /* paranoia */
1652         continue;
1653 
1654 /* requested only version? */
1655     int onlyvers  = strcmp(argv[i], "--version");
1656     if (onlyvers == 0) {
1657 # ifdef VER_H_GIT_VERSION
1658 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1659 #   if VER_H_GIT_PATCH_INT < 1
1660         fprintf (stdout, "%s simulator %s\n", sim_name, VER_H_GIT_VERSION);
1661 #   else
1662         fprintf (stdout, "%s simulator %s+%s\n", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1663 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1664 #  else
1665         fprintf (stdout, "%s simulator %s\n", sim_name, VER_H_GIT_VERSION);
1666 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1667 # else
1668         fprintf (stdout, "%s simulator\n", sim_name);
1669 # endif /* ifdef VER_H_GIT_VERSION */
1670         return 0;
1671     }
1672 
1673 /* requested short or long help? */
1674     int longhelp  = strcmp(argv[i], "--help");
1675     int shorthelp = strcmp(argv[i], "-h");
1676     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1677     if (longhelp == 0 || shorthelp == 0) {
1678 # ifdef VER_H_GIT_VERSION
1679 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1680 #   if VER_H_GIT_PATCH_INT < 1
1681         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1682 #   else
1683         fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1684 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1685 #  else
1686         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1687 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1688 # else
1689         fprintf (stdout, "%s simulator", sim_name);
1690 # endif /* ifdef VER_H_GIT_VERSION */
1691         fprintf (stdout, "\n\nUsage: %s { [SWITCHES] ... } { <SCRIPT> }\n", argv[0]);
1692         fprintf (stdout, "\nInvoke the %s simulator, with optional switches and/or script.\n", sim_name);
1693         fprintf (stdout, "\n Switches:");
1694         fprintf (stdout, "\n  -e, -E              Abort script processing immediately on errors");
1695         fprintf (stdout, "\n  -h, -H, --help      Display this informational help text and exit");
1696         fprintf (stdout, "\n  -k, -K              Do not utilize exclusive file locking support");
1697         fprintf (stdout, "\n  -l, -L              Disregard fatal exclusive file locking errors");
1698         fprintf (stdout, "\n  -o, -O              Configure script conditions to be inheritable");
1699         fprintf (stdout, "\n  -q, -Q              Disables non-error and informational messages");
1700         fprintf (stdout, "\n  -r, -R              Enables ephemeral (session-only) system state");
1701         fprintf (stdout, "\n  -s, -S              Use a randomized name to persist system state");
1702         fprintf (stdout, "\n  -v, -V              Display every script command before execution");
1703         fprintf (stdout, "\n  --version           Display the simulator version string and exit");
1704         fprintf (stdout, "\n\nThis software is made available under the terms of the ICU License,");
1705         fprintf (stdout, "\nversion 1.8.1 or later.  For complete details, see the \"LICENSE.md\"");
1706         fprintf (stdout, "\nincluded or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1707         return 0;
1708     }
1709     /* invalid arguments? */
1710     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
1711         if ((sw = get_switches (argv[i])) < 0) {
1712             fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1713             return 0;
1714             }
1715         sim_switches = sim_switches | sw;
1716         }
1717     /* parse arguments */
1718     else {
1719         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1720             fprintf (stderr, "Argument string too long\n");
1721             return 0;
1722             }
1723         if (*cbuf)                                      /* concat args */
1724             strcat (cbuf, " ");
1725         sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? \
1726           "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : "");
1727         lookswitch = FALSE;                             /* no more switches */
1728         }
1729     }                                                   /* end for */
1730 sim_nolock = sim_switches & SWMASK ('K');               /* -k means skip locking */
1731 sim_iglock = sim_switches & SWMASK ('L');               /* -l means ignore locking */
1732 sim_randompst = sim_switches & SWMASK ('S');            /* -s means persist random */
1733 sim_quiet = sim_switches & SWMASK ('Q');                /* -q means quiet */
1734 sim_randstate = sim_switches & SWMASK ('R');            /* -r means random sys_state */
1735 if (sim_randompst) sim_randstate = 1;                   /*    and is implied with -s */
1736 sim_on_inherit = sim_switches & SWMASK ('O');           /* -o means inherit on state */
1737 
1738 sim_init_sock ();                                       /* init socket capabilities */
1739 if (sim_dflt_dev == NULL)                               /* if no default */
1740     sim_dflt_dev = sim_devices[0];
1741 if (sim_vm_init != NULL)                                /* call once only */
1742     (*sim_vm_init)();
1743 sim_finit ();                                           /* init fio package */
1744 for (i = 0; cmd_table[i].name; i++) {
1745     size_t alias_len = strlen (cmd_table[i].name);
1746     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
1747 
1748     strcpy (cmd_name, cmd_table[i].name);
1749     while (alias_len > 1) {
1750         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
1751         --alias_len;
1752         if (getenv (cmd_name))                      /* Externally defined command alias? */
1753             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
1754         }
1755     free (cmd_name);
1756     }
1757 stop_cpu = 0;
1758 sim_interval = 0;
1759 sim_time = sim_rtime = 0;
1760 noqueue_time = 0;
1761 sim_clock_queue = QUEUE_LIST_END;
1762 sim_is_running = 0;
1763 sim_log = NULL;
1764 if (sim_emax <= 0)
1765     sim_emax = 1;
1766 sim_timer_init ();
1767 
1768 if ((stat = sim_ttinit ()) != SCPE_OK) {
1769     fprintf (stderr, "Fatal terminal initialization error\n%s\n",
1770         sim_error_text (stat));
1771     return 0;
1772     }
1773 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
1774     fprintf (stderr, "Unable to allocate examine buffer\n");
1775     return 0;
1776     };
1777 if ((stat = reset_all_p (0)) != SCPE_OK) {
1778     fprintf (stderr, "Fatal simulator initialization error\n%s\n",
1779         sim_error_text (stat));
1780     return 0;
1781     }
1782 if ((stat = sim_brk_init ()) != SCPE_OK) {
1783     fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
1784         sim_error_text (stat));
1785     return 0;
1786     }
1787 if (!sim_quiet) {
1788     printf ("\n");
1789     show_version (stdout, NULL, NULL, 0, NULL);
1790     }
1791 
1792 sim_argv = argv;
1793 cptr = getenv("HOME");
1794 if (cptr == NULL) {
1795     cptr = getenv("HOMEPATH");
1796     cptr2 = getenv("HOMEDRIVE");
1797     }
1798 else
1799     cptr2 = NULL;
1800 (void)cptr2;
1801 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
1802     stat = do_cmd (0, cbuf);                            /* proc cmd file */
1803 else if (*argv[0]) {                                    /* sim name arg? */
1804     char *np;                                           /* "path.ini" */
1805     nbuf[0] = '"';                                      /* starting " */
1806     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
1807     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
1808         np = strrchr (nbuf, '/');                       /* stript path and try again in cwd */
1809         if (np == NULL)
1810             np = strrchr (nbuf, '\\');                  /* windows path separator */
1811         if (np != NULL) {
1812             *np = '"';
1813             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
1814             }
1815         }
1816     }
1817 
1818 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
1819 
1820 detach_all (0, TRUE);                                   /* close files */
1821 sim_set_deboff (0, NULL);                               /* close debug */
1822 sim_set_logoff (0, NULL);                               /* close log */
1823 sim_set_notelnet (0, NULL);                             /* close Telnet */
1824 sim_ttclose ();                                         /* close console */
1825 sim_cleanup_sock ();                                    /* cleanup sockets */
1826 fclose (stdnul);                                        /* close bit bucket file handle */
1827 free (targv);                                           /* release any argv copy that was made */
1828 return 0;
1829 }
1830 #endif
1831 
process_stdin_commands(t_stat stat,char * argv[])1832 t_stat process_stdin_commands (t_stat stat, char *argv[])
1833 {
1834 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
1835 CONST char *cptr;
1836 t_stat stat_nomessage;
1837 CTAB *cmdp = NULL;
1838 
1839 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
1840 while (stat != SCPE_EXIT) {                             /* in case exit */
1841     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
1842         printf ("%s%s\n", sim_prompt, cptr);            /* echo */
1843     else if (sim_vm_read != NULL) {                     /* sim routine? */
1844         printf ("%s", sim_prompt);                      /* prompt */
1845         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
1846         }
1847     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prmopt*/
1848     if (cptr == NULL) {                                 /* EOF? */
1849         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
1850         else break;                                     /* otherwise exit */
1851         }
1852     if (*cptr == 0)                                     /* ignore blank */
1853         continue;
1854     sim_sub_args (cbuf, sizeof(cbuf), argv);
1855     if (sim_log)                                        /* log cmd */
1856         fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
1857     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
1858         fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
1859     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
1860     sim_switches = 0;                                   /* init switches */
1861     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
1862         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
1863     else
1864         stat = SCPE_UNK;
1865     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message supression flag */
1866     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
1867     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
1868     sim_last_cmd_stat = stat;                           /* save command error status */
1869     if (!stat_nomessage) {                              /* displaying message status? */
1870         if (cmdp && (cmdp->message))                    /* special message handler? */
1871             cmdp->message (NULL, stat);                 /* let it deal with display */
1872         else
1873             if (stat >= SCPE_BASE)                      /* error? */
1874                 sim_printf ("%s\n", sim_error_text (stat));
1875         }
1876     if (sim_vm_post != NULL)
1877         (*sim_vm_post) (TRUE);
1878     }                                                   /* end while */
1879 return stat;
1880 }
1881 
1882 /* Set prompt routine */
1883 
set_prompt(int32 flag,CONST char * cptr)1884 t_stat set_prompt (int32 flag, CONST char *cptr)
1885 {
1886 char gbuf[CBUFSIZE], *gptr;
1887 
1888 if ((!cptr) || (*cptr == '\0'))
1889     return SCPE_ARG;
1890 
1891 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
1892 if (gbuf[0] == '\0') {                                  /* Token started with quote */
1893     gbuf[sizeof (gbuf)-1] = '\0';
1894     strncpy (gbuf, cptr, sizeof (gbuf)-1);
1895     gptr = strchr (gbuf, '"');
1896     if (gptr)
1897         *gptr = '\0';
1898     }
1899 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
1900 sprintf (sim_prompt, "%s ", gbuf);
1901 return SCPE_OK;
1902 }
1903 
1904 /* Find command routine */
1905 
find_cmd(const char * gbuf)1906 CTAB *find_cmd (const char *gbuf)
1907 {
1908 CTAB *cmdp = NULL;
1909 
1910 if (sim_vm_cmd)                                         /* try ext commands */
1911     cmdp = find_ctab (sim_vm_cmd, gbuf);
1912 if (cmdp == NULL)                                       /* try regular cmds */
1913     cmdp = find_ctab (cmd_table, gbuf);
1914 return cmdp;
1915 }
1916 
1917 /* Exit command */
1918 
exit_cmd(int32 flag,CONST char * cptr)1919 t_stat exit_cmd (int32 flag, CONST char *cptr)
1920 {
1921 return SCPE_EXIT;
1922 }
1923 
1924 /* Help command */
1925 
1926 /* Used when sorting a list of command names */
_cmd_name_compare(const void * pa,const void * pb)1927 static int _cmd_name_compare (const void *pa, const void *pb)
1928 {
1929 CTAB * const *a = (CTAB * const *)pa;
1930 CTAB * const *b = (CTAB * const *)pb;
1931 
1932 return strcmp((*a)->name, (*b)->name);
1933 }
1934 
fprint_help(FILE * st)1935 void fprint_help (FILE *st)
1936 {
1937 CTAB *cmdp;
1938 CTAB **hlp_cmdp = NULL;
1939 int cmd_cnt = 0;
1940 int cmd_size = 0;
1941 size_t max_cmdname_size = 0;
1942 int i, line_offset;
1943 
1944 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
1945     if (cmdp->help) {
1946         if (cmd_cnt >= cmd_size) {
1947             cmd_size += 20;
1948             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
1949             }
1950         hlp_cmdp[cmd_cnt] = cmdp;
1951         ++cmd_cnt;
1952         if (strlen(cmdp->name) > max_cmdname_size)
1953             max_cmdname_size = strlen(cmdp->name);
1954         }
1955     }
1956 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
1957     if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) {
1958         if (cmd_cnt >= cmd_size) {
1959             cmd_size += 20;
1960             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
1961             }
1962         hlp_cmdp[cmd_cnt] = cmdp;
1963         ++cmd_cnt;
1964         if (strlen (cmdp->name) > max_cmdname_size)
1965             max_cmdname_size = strlen(cmdp->name);
1966         }
1967     }
1968 fprintf (st, "Help is available for the following commands:\n\n    ");
1969 if (hlp_cmdp)
1970   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
1971 line_offset = 4;
1972 for ( i = 0 ; i < cmd_cnt ; ++i ) {
1973     fputs (hlp_cmdp[i]->name, st);
1974     line_offset += 5 + max_cmdname_size;
1975     if (line_offset + max_cmdname_size > 79) {
1976         line_offset = 4;
1977         fprintf (st, "\n    ");
1978         }
1979     else
1980         fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
1981     }
1982 free (hlp_cmdp);
1983 fprintf (st, "\n");
1984 return;
1985 }
1986 
fprint_header(FILE * st,t_bool * pdone,char * context)1987 static void fprint_header (FILE *st, t_bool *pdone, char *context)
1988 {
1989 if (!*pdone)
1990     fprintf (st, "%s", context);
1991 *pdone = TRUE;
1992 }
1993 
fprint_reg_help_ex(FILE * st,DEVICE * dptr,t_bool silent)1994 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
1995 {
1996 REG *rptr, *trptr;
1997 t_bool found = FALSE;
1998 t_bool all_unique = TRUE;
1999 size_t max_namelen = 0;
2000 DEVICE *tdptr;
2001 CONST char *tptr;
2002 char *namebuf;
2003 char rangebuf[32];
2004 
2005 if (dptr->registers)
2006     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2007         if (rptr->flags & REG_HIDDEN)
2008             continue;
2009         if (rptr->depth > 1)
2010             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2011         else
2012             strcpy (rangebuf, "");
2013         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2014             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2015         found = TRUE;
2016         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2017         if ((trptr == NULL) || (tdptr != dptr))
2018             all_unique = FALSE;
2019         }
2020 if (!found) {
2021     if (!silent)
2022         fprintf (st, "No register help is available for the %s device\n", dptr->name);
2023     }
2024 else {
2025     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2026     fprintf (st, "\nThe %s device implements these registers:\n\n", dptr->name);
2027     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2028         if (rptr->flags & REG_HIDDEN)
2029             continue;
2030         if (rptr->depth <= 1)
2031             sprintf (namebuf, "%*s", -((int)max_namelen), rptr->name);
2032         else {
2033             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2034             sprintf (namebuf, "%s%*s", rptr->name, (int)(strlen(rptr->name))-((int)max_namelen), rangebuf);
2035             }
2036         if (all_unique) {
2037             fprintf (st, "  %s %4d  %s\n", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2038             continue;
2039             }
2040         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2041         if ((trptr == NULL) || (tdptr != dptr))
2042             fprintf (st, "  %s %s %4d  %s\n", dptr->name, namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2043         else
2044             fprintf (st, "  %*s %s %4d  %s\n", (int)strlen(dptr->name), "", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2045         }
2046     free (namebuf);
2047     }
2048 }
2049 
fprint_reg_help(FILE * st,DEVICE * dptr)2050 void fprint_reg_help (FILE *st, DEVICE *dptr)
2051 {
2052 fprint_reg_help_ex (st, dptr, TRUE);
2053 }
2054 
fprint_attach_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2055 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2056 {
2057 if (dptr->attach_help) {
2058     fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2059     dptr->attach_help (st, dptr, NULL, 0, NULL);
2060     return;
2061     }
2062 if (DEV_TYPE(dptr) == DEV_DISK) {
2063     fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2064     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2065     return;
2066     }
2067 if (DEV_TYPE(dptr) == DEV_TAPE) {
2068     fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2069     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2070     return;
2071     }
2072 if (!silent) {
2073     fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2074     if (dptr->help)
2075         dptr->help (st, dptr, NULL, 0, NULL);
2076     }
2077 }
2078 
fprint_set_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2079 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2080 {
2081 MTAB *mptr;
2082 DEBTAB *dep;
2083 t_bool found = FALSE;
2084 char buf[CBUFSIZE], header[CBUFSIZE];
2085 uint32 enabled_units = dptr->numunits;
2086 uint32 unit;
2087 
2088 sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2089 for (unit=0; unit < dptr->numunits; unit++)
2090     if (dptr->units[unit].flags & UNIT_DIS)
2091         --enabled_units;
2092 if (dptr->modifiers) {
2093     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2094         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2095             continue;                                       /* skip unit only extended modifiers */
2096         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2097             continue;                                       /* skip unit only simple modifiers */
2098         if (mptr->mstring) {
2099             fprint_header (st, &found, header);
2100             sprintf (buf, "set %s %s%s", sim_dname (dptr), mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}" : "")));
2101             if ((strlen (buf) < 30) || (!mptr->help))
2102                 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2103             else
2104                 fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2105             }
2106         }
2107     }
2108 if (dptr->flags & DEV_DISABLE) {
2109     fprint_header (st, &found, header);
2110     sprintf (buf, "set %s ENABLE", sim_dname (dptr));
2111     fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2112     sprintf (buf, "set %s DISABLE", sim_dname (dptr));
2113     fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2114     }
2115 if (dptr->flags & DEV_DEBUG) {
2116     fprint_header (st, &found, header);
2117     sprintf (buf, "set %s DEBUG", sim_dname (dptr));
2118     fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2119     sprintf (buf, "set %s NODEBUG", sim_dname (dptr));
2120     fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2121     if (dptr->debflags) {
2122         t_bool desc_available = FALSE;
2123         strcpy (buf, "");
2124         fprintf (st, "set %s DEBUG=", sim_dname (dptr));
2125         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2126             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2127             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2128             }
2129         fprintf (st, "\n");
2130         fprintf (st,  "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2131         fprintf (st, "set %s NODEBUG=", sim_dname (dptr));
2132         for (dep = dptr->debflags; dep->name != NULL; dep++)
2133             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2134         fprintf (st, "\n");
2135         fprintf (st,  "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2136         if (desc_available) {
2137             fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2138             for (dep = dptr->debflags; dep->name != NULL; dep++)
2139                 fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2140             }
2141         }
2142     }
2143 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2144     if (dptr->units->flags & UNIT_DISABLE) {
2145         fprint_header (st, &found, header);
2146         sprintf (buf, "set %sn ENABLE", sim_dname (dptr));
2147         fprintf (st,  "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2148         sprintf (buf, "set %sn DISABLE", sim_dname (dptr));
2149         fprintf (st,  "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2150         }
2151     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2152         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2153             continue;                                           /* skip device only modifiers */
2154         if ((!mptr->valid) && MODMASK(mptr,MTAB_XTD))
2155             continue;                                           /* skip show only modifiers */
2156         if (mptr->mstring) {
2157             fprint_header (st, &found, header);
2158             sprintf (buf, "set %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}": "")));
2159             fprintf (st, "%-30s\t%s\n", buf, (strchr(mptr->mstring, '=')) ? "" : (mptr->help ? mptr->help : ""));
2160             }
2161         }
2162     }
2163 if (!found && !silent)
2164     fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2165 }
2166 
fprint_set_help(FILE * st,DEVICE * dptr)2167 void fprint_set_help (FILE *st, DEVICE *dptr)
2168 {
2169   fprint_set_help_ex (st, dptr, TRUE);
2170 }
2171 
fprint_show_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2172 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2173 {
2174 MTAB *mptr;
2175 t_bool found = FALSE;
2176 char buf[CBUFSIZE], header[CBUFSIZE];
2177 uint32 enabled_units = dptr->numunits;
2178 uint32 unit;
2179 
2180 sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2181 for (unit=0; unit < dptr->numunits; unit++)
2182     if (dptr->units[unit].flags & UNIT_DIS)
2183         --enabled_units;
2184 if (dptr->modifiers) {
2185     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2186         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2187             continue;                                       /* skip unit only extended modifiers */
2188         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2189             continue;                                       /* skip unit only simple modifiers */
2190         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2191             continue;
2192         fprint_header (st, &found, header);
2193         sprintf (buf, "show %s %s%s", sim_dname (dptr), mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2194         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2195         }
2196     }
2197 if (dptr->flags & DEV_DEBUG) {
2198     fprint_header (st, &found, header);
2199     sprintf (buf, "show %s DEBUG", sim_dname (dptr));
2200     fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2201     }
2202 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2203     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2204         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2205             continue;                                           /* skip device only modifiers */
2206         if ((!mptr->disp) || (!mptr->pstring))
2207             continue;
2208         fprint_header (st, &found, header);
2209         sprintf (buf, "show %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2210         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2211         }
2212     }
2213 if (!found && !silent)
2214     fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2215 }
2216 
fprint_show_help(FILE * st,DEVICE * dptr)2217 void fprint_show_help (FILE *st, DEVICE *dptr)
2218     {
2219     fprint_show_help_ex (st, dptr, TRUE);
2220     }
2221 
fprint_brk_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2222 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2223 {
2224 BRKTYPTAB *brkt = dptr->brk_types;
2225 char gbuf[CBUFSIZE];
2226 
2227 if (sim_brk_types == 0) {
2228     if ((dptr != sim_dflt_dev) && (!silent)) {
2229         fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2230         if (dptr->help)
2231             dptr->help (st, dptr, NULL, 0, NULL);
2232         }
2233     return;
2234     }
2235 if (brkt == NULL) {
2236     int i;
2237 
2238     if (dptr == sim_dflt_dev) {
2239         if (sim_brk_types & ~sim_brk_dflt) {
2240             fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2241             for (i=0; i<26; i++) {
2242                 if (sim_brk_types & (1<<i))
2243                     fprintf (st, "  -%c\n", 'A'+i);
2244                 }
2245             }
2246         fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2247         }
2248     return;
2249     }
2250 fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2251 while (brkt->btyp) {
2252     fprintf (st, "  %s     %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2253     ++brkt;
2254     }
2255 fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2256 }
2257 
help_dev_help(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,const char * cptr)2258 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
2259 {
2260 char gbuf[CBUFSIZE];
2261 CTAB *cmdp;
2262 
2263 if (*cptr) {
2264     get_glyph (cptr, gbuf, 0);
2265     if ((cmdp = find_cmd (gbuf))) {
2266         if (cmdp->action == &exdep_cmd) {
2267             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2268                 return dptr->help (st, dptr, uptr, flag, cptr);
2269             else
2270                 fprintf (st, "No help available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2271             return SCPE_OK;
2272             }
2273         if (cmdp->action == &set_cmd) {
2274             fprint_set_help_ex (st, dptr, FALSE);
2275             return SCPE_OK;
2276             }
2277         if (cmdp->action == &show_cmd) {
2278             fprint_show_help_ex (st, dptr, FALSE);
2279             return SCPE_OK;
2280             }
2281         if (cmdp->action == &attach_cmd) {
2282             fprint_attach_help_ex (st, dptr, FALSE);
2283             return SCPE_OK;
2284             }
2285         if (cmdp->action == &brk_cmd) {
2286             fprint_brk_help_ex (st, dptr, FALSE);
2287             return SCPE_OK;
2288             }
2289         if (dptr->help)
2290             return dptr->help (st, dptr, uptr, flag, cptr);
2291         fprintf (st, "No %s help is available for the %s device\n", cmdp->name, dptr->name);
2292         return SCPE_OK;
2293         }
2294     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2295         fprint_reg_help_ex (st, dptr, FALSE);
2296         return SCPE_OK;
2297         }
2298     if (dptr->help)
2299         return dptr->help (st, dptr, uptr, flag, cptr);
2300     fprintf (st, "No %s help is available for the %s device\n", gbuf, dptr->name);
2301     return SCPE_OK;
2302     }
2303 if (dptr->help) {
2304     return dptr->help (st, dptr, uptr, flag, cptr);
2305     }
2306 if (dptr->description)
2307     fprintf (st, "%s %s help\n", dptr->description (dptr), dptr->name);
2308 else
2309     fprintf (st, "%s help\n", dptr->name);
2310 fprint_set_help_ex (st, dptr, TRUE);
2311 fprint_show_help_ex (st, dptr, TRUE);
2312 fprint_attach_help_ex (st, dptr, TRUE);
2313 fprint_reg_help_ex (st, dptr, TRUE);
2314 fprint_brk_help_ex (st, dptr, TRUE);
2315 return SCPE_OK;
2316 }
2317 
help_cmd_output(int32 flag,const char * help,const char * help_base)2318 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
2319 {
2320 switch (help[0]) {
2321     case '*':
2322         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2323         if (sim_log)
2324             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2325         break;
2326     default:
2327         fputs (help, stdout);
2328         if (sim_log)
2329             fputs (help, sim_log);
2330         break;
2331     }
2332 return SCPE_OK;
2333 }
2334 
help_cmd(int32 flag,CONST char * cptr)2335 t_stat help_cmd (int32 flag, CONST char *cptr)
2336 {
2337 char gbuf[CBUFSIZE];
2338 CTAB *cmdp;
2339 
2340 GET_SWITCHES (cptr);
2341 if (sim_switches & SWMASK ('F'))
2342     flag = flag | SCP_HELP_FLAT;
2343 if (*cptr) {
2344     cptr = get_glyph (cptr, gbuf, 0);
2345     if ((cmdp = find_cmd (gbuf))) {
2346         if (*cptr) {
2347             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2348                 DEVICE *dptr;
2349                 UNIT *uptr;
2350                 t_stat r;
2351                 cptr = get_glyph (cptr, gbuf, 0);
2352                 dptr = find_unit (gbuf, &uptr);
2353                 if (dptr == NULL)
2354                     dptr = find_dev (gbuf);
2355                 if (dptr != NULL) {
2356                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2357                     if (sim_log)
2358                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2359                     return r;
2360                     }
2361                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2362                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2363                          (cmdp->help))
2364                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2365                     }
2366                 else { /* HELP SHOW xxx (not device or unit) */
2367                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2368                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2369                         return SCPE_ARG;
2370                     return help_cmd_output (flag, shptr->help, NULL);
2371                     }
2372                 return SCPE_ARG;
2373                 }
2374             else
2375                 return SCPE_2MARG;
2376             }
2377         if (cmdp->help) {
2378             if (strcmp (cmdp->name, "HELP") == 0) {
2379                 DEVICE *dptr;
2380                 int i;
2381                 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
2382                     if (dptr->help)
2383                         sim_printf ("h{elp} %-17s display help for device %s\n", dptr->name, dptr->name);
2384                     if (dptr->attach_help ||
2385                         (DEV_TYPE(dptr) == DEV_MUX) ||
2386                         (DEV_TYPE(dptr) == DEV_DISK) ||
2387                         (DEV_TYPE(dptr) == DEV_TAPE)) {
2388                         sim_printf ("h{elp} %s ATTACH\t display help for device %s ATTACH command\n", dptr->name, dptr->name);
2389                         }
2390                     if (dptr->registers) {
2391                         if (dptr->registers->name != NULL)
2392                             sim_printf ("h{elp} %s REGISTERS\t display help for device %s register variables\n", dptr->name, dptr->name);
2393                         }
2394                     if (dptr->modifiers) {
2395                         MTAB *mptr;
2396                         for (mptr = dptr->modifiers; mptr->pstring != NULL; mptr++) {
2397                             if (mptr->help) {
2398                                 sim_printf ("h{elp} %s SET\t\t display help for device %s SET commands (modifiers)\n", dptr->name, dptr->name);
2399                                 break;
2400                                 }
2401                             }
2402                         }
2403                     }
2404                 }
2405             else {
2406                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2407                     sim_dflt_dev && sim_dflt_dev->help) {
2408                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2409                         if (sim_log)
2410                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2411                     }
2412                 }
2413             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2414             }
2415         else { /* no help so it is likely a command alias */
2416             CTAB *cmdpa;
2417             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2418                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2419                     sim_printf ("%s is an alias for the %s command:\n%s",
2420                                 cmdp->name, cmdpa->name, cmdpa->help);
2421                     break;
2422                     }
2423             if (cmdpa->name == NULL)                /* not found? */
2424                 sim_printf ("No help available for the %s command\n", cmdp->name);
2425             }
2426         }
2427     else {
2428         DEVICE *dptr;
2429         UNIT *uptr;
2430         t_stat r;
2431         dptr = find_unit (gbuf, &uptr);
2432         if (dptr == NULL) {
2433             dptr = find_dev (gbuf);
2434             if (dptr == NULL)
2435                 return SCPE_ARG;
2436             if (dptr->flags & DEV_DIS)
2437                 sim_printf ("Device %s is currently disabled\n", dptr->name);
2438             }
2439         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2440         if (sim_log)
2441             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2442         return r;
2443         }
2444     }
2445 else {
2446     fprint_help (stdout);
2447     if (sim_log)
2448         fprint_help (sim_log);
2449     }
2450 return SCPE_OK;
2451 }
2452 
2453 /* Spawn command */
2454 
spawn_cmd(int32 flag,CONST char * cptr)2455 t_stat spawn_cmd (int32 flag, CONST char *cptr)
2456 {
2457 t_stat status;
2458 if ((cptr == NULL) || (strlen (cptr) == 0))
2459     cptr = getenv("SHELL");
2460 if ((cptr == NULL) || (strlen (cptr) == 0))
2461     cptr = getenv("ComSpec");
2462 fflush(stdout);                                         /* flush stdout */
2463 if (sim_log)                                            /* flush log if enabled */
2464     fflush (sim_log);
2465 if (sim_deb)                                            /* flush debug if enabled */
2466     fflush (sim_deb);
2467 status = system (cptr);
2468 
2469 return status;
2470 }
2471 
2472 /* Echo command */
2473 
echo_cmd(int32 flag,CONST char * cptr)2474 t_stat echo_cmd (int32 flag, CONST char *cptr)
2475 {
2476 sim_printf ("%s\n", cptr);
2477 return SCPE_OK;
2478 }
2479 
2480 /* Do command
2481 
2482    Syntax: DO {-E} {-V} <filename> {<arguments>...}
2483 
2484    -E causes all command errors to be fatal; without it, only EXIT and ASSERT
2485    failure will stop a command file.
2486 
2487    -V causes commands to be echoed before execution.
2488 
2489    Note that SCPE_STEP ("Step expired") is considered a note and not an error
2490    and so does not abort command execution when using -E.
2491 
2492    Inputs:
2493         flag    =   caller and nesting level indicator
2494         fcptr   =   filename and optional arguments, space-separated
2495    Outputs:
2496         status  =   error status
2497 
2498    The "flag" input value indicates the source of the call, as follows:
2499 
2500         -1      =   initialization file (no error if not found)
2501          0      =   command line file
2502          1      =   "DO" command
2503         >1      =   nested "DO" command
2504 */
2505 
do_cmd(int32 flag,CONST char * fcptr)2506 t_stat do_cmd (int32 flag, CONST char *fcptr)
2507 {
2508 return do_cmd_label (flag, fcptr, NULL);
2509 }
2510 
do_position(void)2511 static char *do_position(void)
2512 {
2513 static char cbuf[4*CBUFSIZE];
2514 
2515 snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth], sim_do_label[sim_do_depth] ? "::" : "", sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "", sim_goto_line[sim_do_depth]);
2516 return cbuf;
2517 }
2518 
do_cmd_label(int32 flag,CONST char * fcptr,CONST char * label)2519 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
2520 {
2521 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2522 CONST char *cptr;
2523 FILE *fpin;
2524 CTAB *cmdp = NULL;
2525 int32 echo, nargs, errabort, i;
2526 int32 saved_sim_do_echo = sim_do_echo,
2527       saved_sim_show_message = sim_show_message,
2528       saved_sim_on_inherit = sim_on_inherit,
2529       saved_sim_quiet = sim_quiet;
2530 t_bool staying;
2531 t_stat stat, stat_nomessage;
2532 
2533 stat = SCPE_OK;
2534 staying = TRUE;
2535 if (flag > 0)                                           /* need switches? */
2536     GET_SWITCHES (fcptr);                               /* get switches */
2537 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
2538 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2539 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2540 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
2541 
2542 abuf[sizeof(abuf)-1] = '\0';
2543 strncpy (abuf, fcptr, sizeof(abuf)-1);
2544 c = abuf;
2545 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
2546 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
2547     while (sim_isspace (*c))                            /* skip blanks */
2548         c++;
2549     if (*c == 0)                                        /* all done? */
2550         do_arg [nargs++] = NULL;                        /* null argument */
2551     else {
2552         if (*c == '\'' || *c == '"')                    /* quoted string? */
2553             quote = *c++;
2554         else quote = 0;
2555         do_arg[nargs++] = c;                            /* save start */
2556         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2557             c++;
2558         if (*c)                                         /* term at quote/spc */
2559             *c++ = 0;
2560         }
2561     }                                                   /* end for */
2562 
2563 if (do_arg [0] == NULL)                                 /* need at least 1 */
2564     return SCPE_2FARG;
2565 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
2566     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
2567     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
2568         if (flag == 0)                                  /* cmd line file? */
2569              fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2570         return SCPE_OPENERR;                            /* return failure */
2571         }
2572     }
2573 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2574     ++sim_do_depth;
2575     if (sim_on_inherit) {                               /* inherit ON condition actions? */
2576         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2577         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
2578             if (sim_on_actions[sim_do_depth-1][i]) {
2579                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2580                 if (NULL == sim_on_actions[sim_do_depth][i]) {
2581                     while (--i >= 0) {
2582                         free(sim_on_actions[sim_do_depth][i]);
2583                         sim_on_actions[sim_do_depth][i] = NULL;
2584                         }
2585                     sim_on_check[sim_do_depth] = 0;
2586                     sim_brk_clract ();                  /* defang breakpoint actions */
2587                     --sim_do_depth;                     /* unwind nesting */
2588                     return SCPE_MEM;
2589                     }
2590                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2591                 }
2592             }
2593         }
2594     }
2595 
2596 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
2597 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
2598 sim_goto_line[sim_do_depth] = 0;
2599 if (label) {
2600     sim_gotofile = fpin;
2601     sim_do_echo = echo;
2602     stat = goto_cmd (0, label);
2603     if (stat != SCPE_OK) {
2604         strcpy(cbuf, "RETURN SCPE_ARG");
2605         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
2606         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
2607         goto Cleanup_Return;
2608         }
2609     }
2610 if (errabort)                                           /* -e flag? */
2611     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
2612 
2613 do {
2614     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
2615     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
2616         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
2617         sim_goto_line[sim_do_depth] += 1;
2618         }
2619     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
2620     if (cptr == NULL) {                                 /* EOF? */
2621         stat = SCPE_OK;                                 /* set good return */
2622         break;
2623         }
2624     if (*cptr == 0)                                     /* ignore blank */
2625         continue;
2626     if (echo)                                           /* echo if -v */
2627         sim_printf("%s> %s\n", do_position(), cptr);
2628     if (*cptr == ':')                                   /* ignore label */
2629         continue;
2630     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2631     sim_switches = 0;                                   /* init switches */
2632     sim_gotofile = fpin;
2633     sim_do_echo = echo;
2634     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
2635         if (cmdp->action == &return_cmd)                /* RETURN command? */
2636             break;                                      /*    done! */
2637         if (cmdp->action == &do_cmd) {                  /* DO command? */
2638             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
2639                 stat = SCPE_NEST;
2640             else
2641                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
2642             }
2643         else
2644             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
2645         }
2646     else stat = SCPE_UNK;                               /* bad cmd given */
2647     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
2648     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message supression flag */
2649     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2650     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2651     if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
2652         ((cmdp->action != &return_cmd) &&
2653          (cmdp->action != &goto_cmd) &&
2654          (cmdp->action != &on_cmd) &&
2655          (cmdp->action != &echo_cmd)))
2656         sim_last_cmd_stat = stat;                       /* save command error status */
2657     switch (stat) {
2658         case SCPE_AFAIL:
2659             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
2660                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
2661             break;
2662         case SCPE_EXIT:
2663             staying = FALSE;
2664             break;
2665         case SCPE_OK:
2666         case SCPE_STEP:
2667             break;
2668         default:
2669             break;
2670         }
2671     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
2672         (stat != SCPE_STEP)) {
2673         if (!echo && !sim_quiet &&                      /* report if not echoing */
2674             !stat_nomessage &&                          /* and not suppressing messages */
2675             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
2676             sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
2677             }
2678         }
2679     if (!stat_nomessage) {                              /* report error if not suppressed */
2680         if (cmdp && cmdp->message)                      /* special message handler */
2681             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
2682         else
2683             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
2684                 sim_printf ("%s\n", sim_error_text (stat));
2685         }
2686     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
2687         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
2688     if (staying &&
2689         (sim_on_check[sim_do_depth]) &&
2690         (stat != SCPE_OK) &&
2691         (stat != SCPE_STEP)) {
2692         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
2693             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
2694         else
2695             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
2696         }
2697     if (sim_vm_post != NULL)
2698         (*sim_vm_post) (TRUE);
2699     } while (staying);
2700 Cleanup_Return:
2701 fclose (fpin);                                          /* close file */
2702 sim_gotofile = NULL;
2703 if (flag >= 0) {
2704     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
2705     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
2706     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
2707     }
2708 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
2709 if ((flag >= 0) || (!sim_on_inherit)) {
2710     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
2711         free (sim_on_actions[sim_do_depth][i]);
2712         sim_on_actions[sim_do_depth][i] = NULL;
2713         }
2714     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
2715     }
2716 if (flag >= 0)
2717     --sim_do_depth;                                     /* unwind nesting */
2718 sim_brk_clract ();                                      /* defang breakpoint actions */
2719 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
2720     sim_string_to_stat (cptr, &stat);
2721     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
2722     if (sim_switches & SWMASK ('Q'))
2723         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
2724     return stat;                                        /* return with explicit return status */
2725     }
2726 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
2727 }
2728 
2729 /* Substitute_args - replace %n tokens in 'instr' with the do command's arguments
2730                      and other enviroment variables
2731 
2732    Calling sequence
2733    instr        =       input string
2734    instr_size   =       sizeof input string buffer
2735    do_arg[10]   =       arguments
2736 
2737    Token "%0" expands to the command file name.
2738    Token %n (n being a single digit) expands to the n'th argument
2739    Tonen %* expands to the whole set of arguments (%1 ... %9)
2740 
2741    The input sequence "\%" represents a literal "%", and "\\" represents a
2742    literal "\".  All other character combinations are rendered literally.
2743 
2744    Omitted parameters result in null-string substitutions.
2745 
2746    A Tokens preceeded and followed by % characters are expanded as environment
2747    variables, and if one isn't found then can be one of several special
2748    variables:
2749           %DATE%              yyyy-mm-dd
2750           %TIME%              hh:mm:ss
2751           %STIME%             hh_mm_ss
2752           %CTIME%             Www Mmm dd hh:mm:ss yyyy
2753           %STATUS%            Status value from the last command executed
2754           %TSTATUS%           The text form of the last status value
2755           %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
2756           %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
2757           %SIM_QUIET%         The Quiet mode of the current Do command file
2758           %SIM_MESSAGE%       The message display status of the current Do command file
2759    Environment variable lookups are done first with the precise name between
2760    the % characters and if that fails, then the name between the % characters
2761    is upcased and a lookup of that valus is attempted.
2762 
2763    The first Space delimited token on the line is extracted in uppercase and
2764    then looked up as an environment variable.  If found it the value is
2765    supstituted for the original string before expanding everything else.  If
2766    it is not found, then the original beginning token on the line is left
2767    untouched.
2768 */
2769 
sim_sub_args(char * instr,size_t instr_size,char * do_arg[])2770 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
2771 {
2772 char gbuf[CBUFSIZE];
2773 char *ip = instr, *op, *oend, *tmpbuf;
2774 const char *ap;
2775 char rbuf[CBUFSIZE];
2776 int i;
2777 time_t now;
2778 struct tm *tmnow;
2779 
2780 time(&now);
2781 tmnow = localtime(&now);
2782 tmpbuf = (char *)malloc(instr_size);
2783 op = tmpbuf;
2784 oend = tmpbuf + instr_size - 2;
2785 while (sim_isspace (*ip))                               /* skip leading spaces */
2786     *op++ = *ip++;
2787 for (; *ip && (op < oend); ) {
2788     if ((ip [0] == '\\') &&                             /* literal escape? */
2789         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
2790         ip++;                                           /* skip '\' */
2791         *op++ = *ip++;                                  /* copy escaped char */
2792         }
2793     else
2794         if ((*ip == '%') &&
2795             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
2796             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
2797                 ap = do_arg[ip[1] - '0'];
2798                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
2799                     if (do_arg[i] == NULL) {
2800                         ap = NULL;
2801                         break;
2802                         }
2803                 ip = ip + 2;
2804                 }
2805             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
2806                 memset (rbuf, '\0', sizeof(rbuf));
2807                 ap = rbuf;
2808                 for (i=1; i<=9; ++i)
2809                     if (do_arg[i] == NULL)
2810                         break;
2811                     else
2812                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
2813                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
2814                                 char quote = '"';
2815                                 if (strchr(do_arg[i], quote))
2816                                     quote = '\'';
2817                                 sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"", (i != 1) ? " " : "", quote, do_arg[i], quote);
2818                                 }
2819                             else
2820                                 sprintf(&rbuf[strlen(rbuf)], "%s%s", (i != 1) ? " " : "", do_arg[i]);
2821                             }
2822                         else
2823                             break;
2824                 ip = ip + 2;
2825                 }
2826             else {                                      /* environment variable */
2827                 ap = NULL;
2828                 get_glyph_nc (ip+1, gbuf, '%');         /* first try using the literal name */
2829                 ap = getenv(gbuf);
2830                 if (!ap) {
2831                     get_glyph (ip+1, gbuf, '%');        /* now try using the upcased name */
2832                     ap = getenv(gbuf);
2833                     }
2834                 ip += 1 + strlen (gbuf);
2835                 if (*ip == '%') ++ip;
2836                 if (!ap) {
2837                     /* ISO 8601 format date/time info */
2838                     if (!strcmp ("DATE", gbuf)) {
2839                         sprintf (rbuf, "%4d-%02d-%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday);
2840                         ap = rbuf;
2841                         }
2842                     else if (!strcmp ("TIME", gbuf)) {
2843                         sprintf (rbuf, "%02d:%02d:%02d", tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2844                         ap = rbuf;
2845                         }
2846                     else if (!strcmp ("DATETIME", gbuf)) {
2847                         sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2848                         ap = rbuf;
2849                         }
2850                     /* Locale oriented formatted date/time info */
2851                     if (!strcmp ("LDATE", gbuf)) {
2852                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
2853                         ap = rbuf;
2854                         }
2855                     else if (!strcmp ("LTIME", gbuf)) {
2856                         strftime (rbuf, sizeof(rbuf), "%r", tmnow);
2857                         ap = rbuf;
2858                         }
2859                     else if (!strcmp ("CTIME", gbuf)) {
2860                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
2861                         ap = rbuf;
2862                         }
2863                     /* Separate Date/Time info */
2864                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
2865                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
2866                         ap = rbuf;
2867                         }
2868                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
2869                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
2870                         ap = rbuf;
2871                         }
2872                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
2873                         sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
2874                         ap = rbuf;
2875                         }
2876                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
2877                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
2878                         int year = tmnow->tm_year + 1900;
2879                         int days = year - 2001;
2880                         int leaps = days/4 - days/100 + days/400;
2881                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
2882                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
2883                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
2884                                               96, 80, 92, 76, 88, 72, 84};
2885                         int cal_year = years[selector];
2886 
2887                         if (!strcmp ("DATE_19XX_YY", gbuf))
2888                             sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
2889                         else
2890                             sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
2891                         ap = rbuf;
2892                         }
2893                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
2894                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
2895                         ap = rbuf;
2896                         }
2897                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
2898                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
2899                         ap = rbuf;
2900                         }
2901                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
2902                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
2903                         ap = rbuf;
2904                         }
2905                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
2906                         sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
2907                         ap = rbuf;
2908                         }
2909                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
2910                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
2911                         int iso_yr = tmnow->tm_year + 1900;
2912                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
2913 
2914                         if (iso_wk == 0) {
2915                             iso_yr = iso_yr - 1;
2916                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
2917                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
2918                             }
2919                         else
2920                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
2921                                 ++iso_yr;
2922                                 iso_wk = 1;
2923                                 }
2924                         if (!strcmp ("DATE_WW", gbuf))
2925                             sprintf (rbuf, "%02d", iso_wk);
2926                         else
2927                             sprintf (rbuf, "%04d", iso_yr);
2928                         ap = rbuf;
2929                         }
2930                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
2931                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
2932                         ap = rbuf;
2933                         }
2934                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
2935                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
2936                         ap = rbuf;
2937                         }
2938                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
2939                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
2940                         ap = rbuf;
2941                         }
2942                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
2943                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
2944                         ap = rbuf;
2945                         }
2946                     else if (!strcmp ("STATUS", gbuf)) {
2947                         sprintf (rbuf, "%08X", sim_last_cmd_stat);
2948                         ap = rbuf;
2949                         }
2950                     else if (!strcmp ("TSTATUS", gbuf)) {
2951                         sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
2952                         ap = rbuf;
2953                         }
2954                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
2955                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
2956                         ap = rbuf;
2957                         }
2958                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
2959                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
2960                         ap = rbuf;
2961                         }
2962                     else if (!strcmp ("SIM_QUIET", gbuf)) {
2963                         sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
2964                         ap = rbuf;
2965                         }
2966                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
2967                         sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
2968                         ap = rbuf;
2969                         }
2970                     }
2971                 }
2972             if (ap) {                                   /* non-null arg? */
2973                 while (*ap && (op < oend))              /* copy the argument */
2974                     *op++ = *ap++;
2975                 }
2976             }
2977         else
2978             *op++ = *ip++;
2979     }
2980 *op = 0;                                                /* term buffer */
2981 strcpy (instr, tmpbuf);
2982 free (tmpbuf);
2983 return;
2984 }
2985 
2986 static
sim_cmp_string(const char * s1,const char * s2)2987 int sim_cmp_string (const char *s1, const char *s2)
2988 {
2989 long int v1, v2;
2990 char *ep1, *ep2;
2991 
2992 v1 = strtol(s1+1, &ep1, 0);
2993 v2 = strtol(s2+1, &ep2, 0);
2994 if ((ep1 != s1 + strlen (s1) - 1) ||
2995     (ep2 != s2 + strlen (s2) - 1))
2996     return strcmp (s1, s2);
2997 if (v1 == v2)
2998     return 0;
2999 if (v1 < v2)
3000     return -1;
3001 return 1;
3002 }
3003 
3004 /* Assert command
3005    If command
3006 
3007    Syntax: ASSERT {NOT} {<dev>} <reg>{<logical-op><value>}<conditional-op><value>
3008    Syntax: IF {NOT} {<dev>} <reg>{<logical-op><value>}<conditional-op><value> commandtoprocess{; additionalcommandtoprocess}...
3009 
3010        If NOT is specified, the resulting expression value is inverted.
3011        If <dev> is not specified, sim_dflt_dev (CPU) is assumed.
3012        <value> is expressed in the radix specified for <reg>.
3013        <logical-op> and <conditional-op> are the same as that
3014        allowed for examine and deposit search specifications.
3015 
3016    Syntax: ASSERT {-i} {NOT} "<string1>" <compare-op> "<string2>"
3017    Syntax: IF {-i} {NOT} "<string1>" <compare-op> "<string2>" commandtoprocess{; additionalcommandtoprocess}...
3018 
3019        If -i is specified, the comparisons are done in a case insensitive manner.
3020        If NOT is specified, the resulting expression value is inverted.
3021        "<string1>" and "<string2>" are quote delimited strings which include
3022        expansion references to environment variables in the simulator.
3023        <compare-op> can be any one of:
3024             ==  - equal
3025             EQU - equal
3026             !=  - not equal
3027             NEQ - not equal
3028             <   - less than
3029             LSS - less than
3030             <=  - less than or equal
3031             LEQ - less than or equal
3032             >   - greater than
3033             GTR - greater than
3034             >=  - greater than or equal
3035             GEQ - greater than or equal
3036 */
assert_cmd(int32 flag,CONST char * cptr)3037 t_stat assert_cmd (int32 flag, CONST char *cptr)
3038 {
3039 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3040 CONST char *tptr, *gptr;
3041 REG *rptr;
3042 uint32 idx = 0;
3043 t_value val;
3044 t_stat r;
3045 t_bool Not = FALSE;
3046 t_bool result;
3047 t_addr addr = 0;
3048 t_stat reason;
3049 
3050 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3051                                                         /* get sw, default */
3052 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3053 if (*cptr == 0)                                         /* must be more */
3054     return SCPE_2FARG;
3055 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3056 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3057     Not = TRUE;                                         /* remember that, and */
3058     cptr = (CONST char *)tptr;
3059     }
3060 if (*cptr == '"') {                                     /* quoted string comparison? */
3061     char op[CBUFSIZE];
3062     static struct {
3063         const char *op;
3064         int aval;
3065         int bval;
3066         t_bool invert;
3067         } *optr, compare_ops[] =
3068         {
3069             {"==",   0,  0, FALSE},
3070             {"EQU",  0,  0, FALSE},
3071             {"!=",   0,  0, TRUE},
3072             {"NEQ",  0,  0, TRUE},
3073             {"<",   -1, -1, FALSE},
3074             {"LSS", -1, -1, FALSE},
3075             {"<=",   0, -1, FALSE},
3076             {"LEQ",  0, -1, FALSE},
3077             {">",    1,  1, FALSE},
3078             {"GTR",  1,  1, FALSE},
3079             {">=",   0,  1, FALSE},
3080             {"GEQ",  0,  1, FALSE},
3081             {NULL}};
3082 
3083     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3084                                                     /* get first string */
3085     if (!*tptr)
3086         return SCPE_2FARG;
3087     cptr += strlen (gbuf);
3088     while (sim_isspace (*cptr))                     /* skip spaces */
3089         ++cptr;
3090     get_glyph (cptr, op, '"');
3091     for (optr = compare_ops; optr->op; optr++)
3092         if (0 == strcmp (op, optr->op))
3093             break;
3094     if (!optr->op)
3095         return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3096     cptr += strlen (op);
3097     while (sim_isspace (*cptr))                         /* skip spaces */
3098         ++cptr;
3099     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3100                                                         /* get second string */
3101     if (*cptr) {                                        /* more? */
3102         if (flag)                                       /* ASSERT has no more args */
3103             return SCPE_2MARG;
3104         }
3105     else {
3106         if (!flag)
3107             return SCPE_2FARG;                          /* IF needs actions! */
3108         }
3109     result = sim_cmp_string (gbuf, gbuf2);
3110     result = ((result == optr->aval) || (result == optr->bval));
3111     if (optr->invert)
3112         result = !result;
3113     }
3114 else {
3115     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3116     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3117     if (rptr) {                                         /* got register? */
3118         if (*gptr == '[') {                             /* subscript? */
3119             if (rptr->depth <= 1)                       /* array register? */
3120                 return SCPE_ARG;
3121             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3122             if ((gptr == tptr) || (*tptr++ != ']'))
3123                 return SCPE_ARG;
3124             gptr = tptr;                                /* update */
3125             }
3126         else idx = 0;                                   /* not array */
3127         if (idx >= rptr->depth)                         /* validate subscript */
3128             return SCPE_SUB;
3129         }
3130     else {                                              /* not reg, check for memory */
3131         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3132             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3133         else addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev->dradix);
3134         if (gbuf == gptr)                               /* error? */
3135             return SCPE_NXREG;
3136         }
3137     if (*gptr != 0)                                     /* more? must be search */
3138         get_glyph (gptr, gbuf, 0);
3139     else {
3140         if (*cptr == 0)                                 /* must be more */
3141             return SCPE_2FARG;
3142         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3143         }
3144     if (*cptr) {                                        /* more? */
3145         if (flag)                                       /* ASSERT has no more args */
3146             return SCPE_2MARG;
3147         }
3148     else {
3149         if (!flag)
3150             return SCPE_2FARG;                          /* IF needs actions! */
3151         }
3152     if (rptr) {                                         /* Handle register case */
3153         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3154             (sim_stabr.boolop == -1))                   /* relational op reqd */
3155             return SCPE_MISVAL;
3156         val = get_rval (rptr, idx);                     /* get register value */
3157         result = test_search (&val, &sim_stabr);        /* test condition */
3158         }
3159     else {                                              /* Handle memory case */
3160         if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3161             (sim_staba.boolop == -1))                    /* relational op reqd */
3162             return SCPE_MISVAL;
3163         reason = get_aval (addr, sim_dfdev, sim_dfunit);/* get data */
3164         if (reason != SCPE_OK)                          /* return if error */
3165             return reason;
3166         result = test_search (sim_eval, &sim_staba);    /* test condition */
3167         }
3168     }
3169 if (Not ^ result) {
3170     if (!flag)
3171         sim_brk_setact (cptr);                          /* set up IF actions */
3172     }
3173 else
3174     if (flag)
3175         return SCPE_AFAIL;                              /* return assert status */
3176 return SCPE_OK;
3177 }
3178 
3179 /* Send command
3180 
3181    Syntax: SEND {After=m},{Delay=n},"string-to-send"
3182 
3183    After  - is a positive integer representing a number of instruction delay
3184             before the initial characters is sent.  The value specified
3185             in a after argument persists across SEND commands.  The after
3186             parameter can be set by itself with SEND AFTER=n
3187    Delay  - is a positive integer representing a minimal instruction delay
3188             before and between characters being sent.  The value specified
3189             in a delay argument persists across SEND commands.  The delay
3190             parameter can be set by itself with SEND DELAY=n
3191    String - must be quoted.  Quotes may be either single or double but the
3192             opening anc closing quote characters must match.  Within quotes
3193             C style character escapes are allowed.
3194             The following character escapes are explicitly supported:
3195         \r  Sends the ASCII Carriage Return character (Decimal value 13)
3196         \n  Sends the ASCII Linefeed character (Decimal value 10)
3197         \f  Sends the ASCII Formfeed character (Decimal value 12)
3198         \t  Sends the ASCII Horizontal Tab character (Decimal value 9)
3199         \v  Sends the ASCII Vertical Tab character (Decimal value 11)
3200         \b  Sends the ASCII Backspace character (Decimal value 8)
3201         \\  Sends the ASCII Backslash character (Decimal value 92)
3202         \'  Sends the ASCII Single Quote character (Decimal value 39)
3203         \"  Sends the ASCII Double Quote character (Decimal value 34)
3204         \?  Sends the ASCII Question Mark character (Decimal value 63)
3205         \e  Sends the ASCII Escape character (Decimal value 27)
3206      as well as octal character values of the form:
3207         \n{n{n}} where each n is an octal digit (0-7)
3208      and hext character values of the form:
3209         \xh{h} where each h is a hex digit (0-9A-Fa-f)
3210    */
3211 
send_cmd(int32 flag,CONST char * cptr)3212 t_stat send_cmd (int32 flag, CONST char *cptr)
3213 {
3214 char gbuf[CBUFSIZE];
3215 CONST char *tptr;
3216 uint8 dbuf[CBUFSIZE];
3217 uint32 dsize = 0;
3218 uint32 delay = 0;
3219 uint32 after = 0;
3220 t_stat r;
3221 SEND *snd = NULL;
3222 
3223 GET_SWITCHES (cptr);                                    /* get switches */
3224 tptr = get_glyph (cptr, gbuf, ',');
3225 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3226     cptr = tptr;
3227     tptr = get_glyph (tptr, gbuf, ',');
3228     }
3229 else
3230     snd = sim_cons_get_send ();
3231 
3232 while (*cptr) {
3233     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3234         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3235         if (r != SCPE_OK)
3236             return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3237         cptr = tptr;
3238         tptr = get_glyph (cptr, gbuf, ',');
3239         continue;
3240         }
3241     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3242         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3243         if (r != SCPE_OK)
3244             return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3245         cptr = tptr;
3246         tptr = get_glyph (cptr, gbuf, ',');
3247         continue;
3248         }
3249     if ((*cptr == '"') || (*cptr == '\''))
3250         break;
3251     return SCPE_ARG;
3252     }
3253 if (*cptr) {
3254     if ((*cptr != '"') && (*cptr != '\''))
3255         return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3256     cptr = get_glyph_quoted (cptr, gbuf, 0);
3257     if (*cptr != '\0')
3258         return SCPE_2MARG;                  /* No more arguments */
3259 
3260     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3261         return sim_messagef (SCPE_ARG, "Invalid String\n");
3262     }
3263 if ((dsize == 0) && (delay == 0) && (after == 0))
3264     return SCPE_2FARG;
3265 return sim_send_input (snd, dbuf, dsize, after, delay);
3266 }
3267 
sim_show_send(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3268 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3269 {
3270 char gbuf[CBUFSIZE];
3271 CONST char *tptr;
3272 SEND *snd = NULL;
3273 
3274 tptr = get_glyph (cptr, gbuf, ',');
3275 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3276     cptr = tptr;
3277     }
3278 else
3279     snd = sim_cons_get_send ();
3280 if (*cptr)
3281     return SCPE_2MARG;
3282 return sim_show_send_input (st, snd);
3283 }
3284 
expect_cmd(int32 flag,CONST char * cptr)3285 t_stat expect_cmd (int32 flag, CONST char *cptr)
3286 {
3287 char gbuf[CBUFSIZE];
3288 CONST char *tptr;
3289 EXPECT *exp = NULL;
3290 
3291 GET_SWITCHES (cptr);                                    /* get switches */
3292 tptr = get_glyph (cptr, gbuf, ',');
3293 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3294     cptr = tptr;
3295     }
3296 else
3297     exp = sim_cons_get_expect ();
3298 if (flag)
3299     return sim_set_expect (exp, cptr);
3300 else
3301     return sim_set_noexpect (exp, cptr);
3302 }
3303 
sim_show_expect(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3304 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3305 {
3306 char gbuf[CBUFSIZE];
3307 CONST char *tptr;
3308 EXPECT *exp = NULL;
3309 
3310 tptr = get_glyph (cptr, gbuf, ',');
3311 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3312     cptr = tptr;
3313     }
3314 else
3315     exp = sim_cons_get_expect ();
3316 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3317     return SCPE_ARG;            /* String must be quote delimited */
3318 tptr = get_glyph_quoted (cptr, gbuf, 0);
3319 if (*tptr != '\0')
3320     return SCPE_2MARG;          /* No more arguments */
3321 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3322     return SCPE_ARG;            /* String must be quote delimited */
3323 return sim_exp_show (st, exp, gbuf);
3324 }
3325 
3326 /* Goto command */
3327 
goto_cmd(int32 flag,CONST char * fcptr)3328 t_stat goto_cmd (int32 flag, CONST char *fcptr)
3329 {
3330 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3331 const char *cptr;
3332 long fpos;
3333 int32 saved_do_echo = sim_do_echo;
3334 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3335 
3336 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3337 get_glyph (fcptr, gbuf1, 0);
3338 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
3339 fpos = ftell(sim_gotofile);                             /* Save start position */
3340 rewind(sim_gotofile);                                   /* start search for label */
3341 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
3342 sim_do_echo = 0;                                        /* Don't echo while searching for label */
3343 while (1) {
3344     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3345     if (cptr == NULL) break;                            /* exit on eof */
3346     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
3347     if (*cptr == 0) continue;                           /* ignore blank */
3348     if (*cptr != ':') continue;                         /* ignore non-labels */
3349     ++cptr;                                             /* skip : */
3350     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
3351     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
3352     if (0 == strcmp(gbuf, gbuf1)) {
3353         sim_brk_clract ();                              /* goto defangs current actions */
3354         sim_do_echo = saved_do_echo;                    /* restore echo mode */
3355         if (sim_do_echo)                                /* echo if -v */
3356             sim_printf("%s> %s\n", do_position(), cbuf);
3357         return SCPE_OK;
3358         }
3359     }
3360 sim_do_echo = saved_do_echo;                            /* restore echo mode */
3361 fseek(sim_gotofile, fpos, SEEK_SET);                    /* resture start position */
3362 sim_goto_line[sim_do_depth] = saved_goto_line;          /* restore start line number */
3363 return SCPE_ARG;
3364 }
3365 
3366 /* Return command */
3367 /* The return command is invalid unless encountered in a do_cmd context, */
3368 /* and in that context, it is handled as a special case inside of do_cmd() */
3369 /* and not dispatched here, so if we get here a return has been issued from */
3370 /* interactive input */
3371 
return_cmd(int32 flag,CONST char * fcptr)3372 t_stat return_cmd (int32 flag, CONST char *fcptr)
3373 {
3374 return SCPE_UNK;                                        /* only valid inside of do_cmd */
3375 }
3376 
3377 /* Shift command */
3378 /* The shift command is invalid unless encountered in a do_cmd context, */
3379 /* and in that context, it is handled as a special case inside of do_cmd() */
3380 /* and not dispatched here, so if we get here a shift has been issued from */
3381 /* interactive input (it is not valid interactively since it would have to */
3382 /* mess with the program's argv which is owned by the C runtime library */
3383 
shift_cmd(int32 flag,CONST char * fcptr)3384 t_stat shift_cmd (int32 flag, CONST char *fcptr)
3385 {
3386 return SCPE_UNK;                                        /* only valid inside of do_cmd */
3387 }
3388 
3389 /* Call command */
3390 /* The call command is invalid unless encountered in a do_cmd context, */
3391 /* and in that context, it is handled as a special case inside of do_cmd() */
3392 /* and not dispatched here, so if we get here a call has been issued from */
3393 /* interactive input */
3394 
call_cmd(int32 flag,CONST char * fcptr)3395 t_stat call_cmd (int32 flag, CONST char *fcptr)
3396 {
3397 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3398 const char *cptr;
3399 
3400 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3401 cptr = get_glyph (fcptr, gbuf, 0);
3402 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
3403 snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3404 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
3405 return do_cmd_label (flag, cbuf, gbuf);
3406 }
3407 
3408 /* On command */
3409 
on_cmd(int32 flag,CONST char * cptr)3410 t_stat on_cmd (int32 flag, CONST char *cptr)
3411 {
3412 char gbuf[CBUFSIZE];
3413 t_stat cond;
3414 
3415 cptr = get_glyph (cptr, gbuf, 0);
3416 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
3417 if (0 == strcmp("ERROR", gbuf))
3418     cond = 0;
3419 else
3420     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3421         return SCPE_ARG;
3422 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
3423     free(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
3424     sim_on_actions[sim_do_depth][cond] = NULL; }
3425 else {
3426     sim_on_actions[sim_do_depth][cond] =
3427         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3428     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3429     }
3430 return SCPE_OK;
3431 }
3432 
3433 /* noop command */
3434 /* The noop command (IGNORE, PROCEED) does nothing */
3435 
noop_cmd(int32 flag,CONST char * cptr)3436 t_stat noop_cmd (int32 flag, CONST char *cptr)
3437 {
3438 if (cptr && (*cptr != 0))                               /* now eol? */
3439     return SCPE_2MARG;
3440 return SCPE_OK;                                         /* we're happy doing nothing */
3441 }
3442 
3443 /* Set on/noon routine */
3444 
set_on(int32 flag,CONST char * cptr)3445 t_stat set_on (int32 flag, CONST char *cptr)
3446 {
3447 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
3448     char gbuf[CBUFSIZE];
3449 
3450     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
3451     if (((MATCH_CMD(gbuf,"INHERIT")) &&
3452          (MATCH_CMD(gbuf,"NOINHERIT"))) ||
3453         (*cptr))
3454         return SCPE_2MARG;
3455     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT")))
3456         sim_on_inherit = 1;
3457     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT")))
3458         sim_on_inherit = 0;
3459     return SCPE_OK;
3460     }
3461 if (cptr && (*cptr != 0))                               /* now eol? */
3462     return SCPE_2MARG;
3463 sim_on_check[sim_do_depth] = flag;
3464 if ((sim_do_depth != 0) &&
3465     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
3466     sim_on_actions[sim_do_depth][0] =                   /* No, so make "RETURN" */
3467         (char *)malloc(1+strlen("RETURN"));             /* be the default action */
3468     strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
3469     }
3470 if ((sim_do_depth != 0) &&
3471     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
3472     sim_on_actions[sim_do_depth][SCPE_AFAIL] =          /* No, so make "RETURN" */
3473         (char *)malloc(1+strlen("RETURN"));             /* be the action */
3474     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
3475     }
3476 return SCPE_OK;
3477 }
3478 
3479 /* Set verify/noverify routine */
3480 
set_verify(int32 flag,CONST char * cptr)3481 t_stat set_verify (int32 flag, CONST char *cptr)
3482 {
3483 if (cptr && (*cptr != 0))                               /* now eol? */
3484     return SCPE_2MARG;
3485 if (flag == sim_do_echo)                                /* already set correctly? */
3486     return SCPE_OK;
3487 sim_do_echo = flag;
3488 return SCPE_OK;
3489 }
3490 
3491 /* Set message/nomessage routine */
3492 
set_message(int32 flag,CONST char * cptr)3493 t_stat set_message (int32 flag, CONST char *cptr)
3494 {
3495 if (cptr && (*cptr != 0))                               /* now eol? */
3496     return SCPE_2MARG;
3497 if (flag == sim_show_message)                           /* already set correctly? */
3498     return SCPE_OK;
3499 sim_show_message = flag;
3500 return SCPE_OK;
3501 }
3502 
3503 /* Set quiet/noquiet routine */
3504 
set_quiet(int32 flag,CONST char * cptr)3505 t_stat set_quiet (int32 flag, CONST char *cptr)
3506 {
3507 if (cptr && (*cptr != 0))                               /* now eol? */
3508     return SCPE_2MARG;
3509 if (flag == sim_quiet)                                  /* already set correctly? */
3510     return SCPE_OK;
3511 sim_quiet = flag;
3512 return SCPE_OK;
3513 }
3514 
3515 /* Set environment routine */
3516 
sim_set_environment(int32 flag,CONST char * cptr)3517 t_stat sim_set_environment (int32 flag, CONST char *cptr)
3518 {
3519 char varname[CBUFSIZE];
3520 
3521 if ((!cptr) || (*cptr == 0))                            /* now eol? */
3522     return SCPE_2FARG;
3523 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
3524 setenv(varname, cptr, 1);
3525 return SCPE_OK;
3526 }
3527 
3528 /* Set command */
3529 
set_cmd(int32 flag,CONST char * cptr)3530 t_stat set_cmd (int32 flag, CONST char *cptr)
3531 {
3532 uint32 lvl = 0;
3533 t_stat r;
3534 char gbuf[CBUFSIZE], *cvptr;
3535 CONST char *svptr;
3536 DEVICE *dptr;
3537 UNIT *uptr;
3538 MTAB *mptr;
3539 CTAB *gcmdp;
3540 C1TAB *ctbr = NULL, *glbr;
3541 
3542 GET_SWITCHES (cptr);                                    /* get switches */
3543 if (*cptr == 0)                                         /* must be more */
3544     return SCPE_2FARG;
3545 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
3546 
3547 if ((dptr = find_dev (gbuf))) {                         /* device match? */
3548     uptr = dptr->units;                                 /* first unit */
3549     ctbr = set_dev_tab;                                 /* global table */
3550     lvl = MTAB_VDV;                                     /* device match */
3551     GET_SWITCHES (cptr);                                /* get more switches */
3552     }
3553 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
3554     if (uptr == NULL)                                   /* invalid unit */
3555         return SCPE_NXUN;
3556     ctbr = set_unit_tab;                                /* global table */
3557     lvl = MTAB_VUN;                                     /* unit match */
3558     GET_SWITCHES (cptr);                                /* get more switches */
3559     }
3560 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
3561     GET_SWITCHES (cptr);                                /* get more switches */
3562     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
3563     }
3564 else {
3565     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3566         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
3567             *cvptr++ = 0;
3568         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
3569             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
3570                 dptr = sim_dflt_dev;
3571                 cptr = svptr;
3572                 while (sim_isspace(*cptr))
3573                     ++cptr;
3574                 break;
3575                 }
3576             }
3577         }
3578     if (!dptr)
3579         return SCPE_NXDEV;                              /* no match */
3580     lvl = MTAB_VDV;                                     /* device match */
3581     uptr = dptr->units;                                 /* first unit */
3582     }
3583 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
3584     return SCPE_2FARG;
3585 GET_SWITCHES (cptr);                                    /* get more switches */
3586 
3587 while (*cptr != 0) {                                    /* do all mods */
3588     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
3589     if (0 == strcmp (gbuf, ";"))
3590         break;
3591     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
3592         *cvptr++ = 0;
3593     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3594         if ((mptr->mstring) &&                          /* match string */
3595             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
3596             if (mptr->mask & MTAB_XTD) {                /* extended? */
3597                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
3598                     return SCPE_ARG;
3599                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
3600                     return SCPE_UDIS;                   /* unit disabled? */
3601                 if (mptr->valid) {                      /* validation rtn? */
3602                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
3603                         svptr = get_glyph_quoted (svptr, gbuf, ',');
3604                         if ((cvptr = strchr (gbuf, '='))) {
3605                             *cvptr++ = 0;
3606                             cptr = svptr;
3607                             }
3608                         }
3609                     else {
3610                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
3611                             get_glyph_nc (svptr, gbuf, ',');
3612                             if ((cvptr = strchr (gbuf, '=')))
3613                                 *cvptr++ = 0;
3614                             }
3615                         }
3616                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
3617                     if (r != SCPE_OK)
3618                         return r;
3619                     }
3620                 else if (!mptr->desc)                   /* value desc? */
3621                     break;
3622                 else if (cvptr)                         /* = value? */
3623                     return SCPE_ARG;
3624                 else *((int32 *) mptr->desc) = mptr->match;
3625                 }                                       /* end if xtd */
3626             else {                                      /* old style */
3627                 if (cvptr)                              /* = value? */
3628                     return SCPE_ARG;
3629                 if (uptr->flags & UNIT_DIS)             /* disabled? */
3630                      return SCPE_UDIS;
3631                 if ((mptr->valid) &&                    /* invalid? */
3632                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
3633                     return r;
3634                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
3635                     (mptr->match & mptr->mask);         /* set new value */
3636                 }                                       /* end else xtd */
3637             break;                                      /* terminate for */
3638             }                                           /* end if match */
3639         }                                               /* end for */
3640     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
3641         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
3642             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
3643             if (r != SCPE_OK)
3644                 return r;
3645             }
3646         else if (!dptr->modifiers)                      /* no modifiers? */
3647             return SCPE_NOPARAM;
3648         else return SCPE_NXPAR;
3649         }                                               /* end if no mat */
3650     }                                                   /* end while */
3651 return SCPE_OK;                                         /* done all */
3652 }
3653 
3654 /* Match CTAB/CTAB1 name */
3655 
find_ctab(CTAB * tab,const char * gbuf)3656 CTAB *find_ctab (CTAB *tab, const char *gbuf)
3657 {
3658 if (!tab)
3659     return NULL;
3660 for (; tab->name != NULL; tab++) {
3661     if (MATCH_CMD (gbuf, tab->name) == 0)
3662         return tab;
3663     }
3664 return NULL;
3665 }
3666 
find_c1tab(C1TAB * tab,const char * gbuf)3667 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
3668 {
3669 if (!tab)
3670     return NULL;
3671 for (; tab->name != NULL; tab++) {
3672     if (MATCH_CMD (gbuf, tab->name) == 0)
3673         return tab;
3674     }
3675 return NULL;
3676 }
3677 
3678 /* Set device data radix routine */
3679 
set_dev_radix(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3680 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3681 {
3682 if (cptr)
3683     return SCPE_ARG;
3684 dptr->dradix = flag & 037;
3685 return SCPE_OK;
3686 }
3687 
3688 /* Set device enabled/disabled routine */
3689 
set_dev_enbdis(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3690 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3691 {
3692 UNIT *up;
3693 uint32 i;
3694 
3695 if (cptr)
3696     return SCPE_ARG;
3697 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
3698     return SCPE_NOFNC;
3699 if (flag) {                                             /* enable? */
3700     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
3701         return SCPE_OK;
3702     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
3703     }
3704 else {
3705     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
3706         return SCPE_OK;
3707     for (i = 0; i < dptr->numunits; i++) {              /* check units */
3708         up = (dptr->units) + i;                         /* att or active? */
3709         if ((up->flags & UNIT_ATT) || sim_is_active (up))
3710             return SCPE_NOFNC;                          /* can't do it */
3711         }
3712     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
3713     }
3714 if (dptr->reset)                                        /* reset device */
3715     return dptr->reset (dptr);
3716 else return SCPE_OK;
3717 }
3718 
3719 /* Set unit enabled/disabled routine */
3720 
set_unit_enbdis(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3721 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3722 {
3723 if (cptr)
3724     return SCPE_ARG;
3725 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
3726     return SCPE_NOFNC;
3727 if (flag)                                               /* enb? enable */
3728     uptr->flags = uptr->flags & ~UNIT_DIS;
3729 else {
3730     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
3731         sim_is_active (uptr))                           /* more tests */
3732         return SCPE_NOFNC;
3733     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
3734     }
3735 return SCPE_OK;
3736 }
3737 
3738 /* Set device debug enabled/disabled routine */
3739 
set_dev_debug(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3740 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3741 {
3742 char gbuf[CBUFSIZE];
3743 DEBTAB *dep;
3744 
3745 if ((dptr->flags & DEV_DEBUG) == 0)
3746     return SCPE_NOFNC;
3747 if (cptr == NULL) {                                     /* no arguments? */
3748     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
3749     if (flag && dptr->debflags) {                       /* enable with table? */
3750         for (dep = dptr->debflags; dep->name != NULL; dep++)
3751             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
3752         }
3753     return SCPE_OK;
3754     }
3755 if (dptr->debflags == NULL)                             /* must have table */
3756     return SCPE_ARG;
3757 while (*cptr) {
3758     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
3759     for (dep = dptr->debflags; dep->name != NULL; dep++) {
3760         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
3761             if (flag)
3762                 dptr->dctrl = dptr->dctrl | dep->mask;
3763             else dptr->dctrl = dptr->dctrl & ~dep->mask;
3764             break;
3765             }
3766         }                                               /* end for */
3767     if (dep->mask == 0)                                 /* no match? */
3768         return SCPE_ARG;
3769     }                                                   /* end while */
3770 return SCPE_OK;
3771 }
3772 
3773 /* Show command */
3774 
show_cmd(int32 flag,CONST char * cptr)3775 t_stat show_cmd (int32 flag, CONST char *cptr)
3776 {
3777 t_stat r;
3778 
3779 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
3780                                                         /* get sw, ofile */
3781 if (!cptr)                                              /* error? */
3782     return r;
3783 if (sim_ofile) {                                        /* output file? */
3784     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
3785     fclose (sim_ofile);
3786     }
3787 else {
3788     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
3789     if (sim_log && (sim_log != stdout))
3790         show_cmd_fi (sim_log, flag, cptr);
3791     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
3792         show_cmd_fi (sim_deb, flag, cptr);
3793     }
3794 return r;
3795 }
3796 
show_cmd_fi(FILE * ofile,int32 flag,CONST char * cptr)3797 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
3798 {
3799 uint32 lvl = 0xFFFFFFFF;
3800 char gbuf[CBUFSIZE], *cvptr;
3801 CONST char *svptr;
3802 DEVICE *dptr;
3803 UNIT *uptr;
3804 MTAB *mptr;
3805 SHTAB *shtb = NULL, *shptr;
3806 
3807 GET_SWITCHES (cptr);                                    /* get switches */
3808 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
3809     return SCPE_2FARG;
3810 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
3811 
3812 if ((dptr = find_dev (gbuf))) {                         /* device match? */
3813     uptr = dptr->units;                                 /* first unit */
3814     shtb = show_dev_tab;                                /* global table */
3815     lvl = MTAB_VDV;                                     /* device match */
3816     GET_SWITCHES (cptr);                                /* get more switches */
3817     }
3818 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
3819     if (uptr == NULL)                                   /* invalid unit */
3820         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
3821     if (uptr->flags & UNIT_DIS)                         /* disabled? */
3822         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
3823     shtb = show_unit_tab;                               /* global table */
3824     lvl = MTAB_VUN;                                     /* unit match */
3825     GET_SWITCHES (cptr);                                /* get more switches */
3826     }
3827 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
3828     GET_SWITCHES (cptr);                                /* get more switches */
3829     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
3830     }
3831 else {
3832     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3833         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
3834             *cvptr++ = 0;
3835         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
3836             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
3837                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) ||
3838                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
3839                 dptr = sim_dflt_dev;
3840                 lvl = MTAB_VDV;                         /* device match */
3841                 cptr = svptr;
3842                 while (sim_isspace(*cptr))
3843                     ++cptr;
3844                 break;
3845                 }
3846             }
3847         }
3848     if (!dptr) {
3849         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
3850             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
3851         else
3852             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
3853         }
3854     }
3855 
3856 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
3857     return (lvl == MTAB_VDV)?
3858         show_device (ofile, dptr, 0):
3859         show_unit (ofile, dptr, uptr, -1);
3860     }
3861 GET_SWITCHES (cptr);                                    /* get more switches */
3862 
3863 while (*cptr != 0) {                                    /* do all mods */
3864     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
3865     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
3866         *cvptr++ = 0;
3867     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3868         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
3869             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
3870             ((mptr->disp && mptr->pstring &&            /* named disp? */
3871             (MATCH_CMD (gbuf, mptr->pstring) == 0))
3872             )) {
3873             if (cvptr && !MODMASK(mptr,MTAB_SHP))
3874                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
3875             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
3876             break;
3877             }                                           /* end if */
3878         }                                               /* end for */
3879     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
3880         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
3881             t_stat r;
3882 
3883             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
3884             if (r != SCPE_OK)
3885                 return r;
3886             }
3887         else {
3888             if (!dptr->modifiers)                       /* no modifiers? */
3889                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
3890             else
3891                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
3892             }
3893         }                                               /* end if */
3894     }                                                   /* end while */
3895 return SCPE_OK;
3896 }
3897 
find_shtab(SHTAB * tab,const char * gbuf)3898 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
3899 {
3900 if (!tab)
3901     return NULL;
3902 for (; tab->name != NULL; tab++) {
3903     if (MATCH_CMD (gbuf, tab->name) == 0)
3904         return tab;
3905     }
3906 return NULL;
3907 }
3908 
3909 /* Show device and unit */
3910 
show_device(FILE * st,DEVICE * dptr,int32 flag)3911 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
3912 {
3913 uint32 j, udbl, ucnt;
3914 UNIT *uptr;
3915 int32 toks = 0;
3916 
3917 fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
3918 if ((flag == 2) && dptr->description) {
3919     fprintf (st, "\t%s\n", dptr->description(dptr));
3920     }
3921 else {
3922     if ((sim_switches & SWMASK ('D')) && dptr->description)
3923         fprintf (st, "\t%s\n", dptr->description(dptr));
3924     }
3925 if (qdisable (dptr)) {                                  /* disabled? */
3926     fprintf (st, "\tdisabled\n");
3927     return SCPE_OK;
3928     }
3929 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
3930     uptr = dptr->units + j;
3931     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
3932         ucnt++;
3933     else if (uptr->flags & UNIT_DISABLE)
3934         udbl++;                                         /* count user-disabled */
3935     }
3936 show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
3937 if (dptr->numunits == 0) {
3938     if (toks)
3939         fprintf (st, "\n");
3940     }
3941 else {
3942     if (ucnt == 0) {
3943         fprint_sep (st, &toks);
3944         fprintf (st, "all units disabled\n");
3945         }
3946     else if ((ucnt > 1) || (udbl > 0)) {
3947         fprint_sep (st, &toks);
3948         fprintf (st, "%d units\n", ucnt + udbl);
3949         }
3950     else
3951         if ((flag != 2) || !dptr->description || toks)
3952             fprintf (st, "\n");
3953     toks = 0;
3954     }
3955 if (flag)                                               /* dev only? */
3956     return SCPE_OK;
3957 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
3958     uptr = dptr->units + j;
3959     if ((uptr->flags & UNIT_DIS) == 0)
3960         show_unit (st, dptr, uptr, ucnt + udbl);
3961     }
3962 return SCPE_OK;
3963 }
3964 
fprint_sep(FILE * st,int32 * tokens)3965 void fprint_sep (FILE *st, int32 *tokens)
3966 {
3967 fprintf (st, "%s", (*tokens > 0) ? ", " : "\t");
3968 *tokens += 1;
3969 }
3970 
show_unit(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag)3971 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
3972 {
3973 int32 u = (int32)(uptr - dptr->units);
3974 int32 toks = 0;
3975 
3976 if (flag > 1)
3977     fprintf (st, "  %s%d", sim_dname (dptr), u);
3978 else if (flag < 0)
3979     fprintf (st, "%s%d", sim_dname (dptr), u);
3980 if (uptr->flags & UNIT_FIX) {
3981     fprint_sep (st, &toks);
3982     fprint_capac (st, dptr, uptr);
3983     }
3984 if (uptr->flags & UNIT_ATT) {
3985     fprint_sep (st, &toks);
3986     fprintf (st, "attached to %s", uptr->filename);
3987     if (uptr->flags & UNIT_RO)
3988         fprintf (st, ", read only");
3989     }
3990 else {
3991     if (uptr->flags & UNIT_ATTABLE) {
3992         fprint_sep (st, &toks);
3993         fprintf (st, "not attached");
3994         }
3995     }
3996 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
3997 if (toks || (flag < 0) || (flag > 1))
3998     fprintf (st, "\n");
3999 return SCPE_OK;
4000 }
4001 
sprint_capac(DEVICE * dptr,UNIT * uptr)4002 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
4003 {
4004 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4005 t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000;
4006 t_addr mval;
4007 t_addr psize = uptr->capac;
4008 char *scale, *width;
4009 
4010 if (sim_switches & SWMASK ('B'))
4011     kval = 1024;
4012 mval = kval * kval;
4013 if (dptr->flags & DEV_SECTORS) {
4014     kval = kval / 512;
4015     mval = mval / 512;
4016     }
4017 if ((dptr->dwidth / dptr->aincr) > 8)
4018     width = "W";
4019 else
4020     width = "B";
4021 if ((psize < (kval * 10)) &&
4022     (0 != (psize % kval))) {
4023     scale = "";
4024     }
4025 else if ((psize < (mval * 10)) &&
4026          (0 != (psize % mval))){
4027     scale = "K";
4028     psize = psize / kval;
4029     }
4030 else {
4031     scale = "M";
4032     psize = psize / mval;
4033     }
4034 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4035 sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4036 return capac_buf;
4037 }
4038 
fprint_capac(FILE * st,DEVICE * dptr,UNIT * uptr)4039 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
4040 {
4041 fprintf (st, "%s", sprint_capac (dptr, uptr));
4042 }
4043 
4044 /* Show <global name> processors  */
4045 
show_prom(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4046 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4047 {
4048 #define DPS8_SCP
4049 #define PROM SCPPROM
4050 #include "../dps8/dps8_prom.h"
4051         int n = 6;
4052         int l = 174; /* end of populated area */
4053         sim_printf(" PROM initialization data: \n\n");
4054         for (int prombyte = 0; prombyte < l; ++prombyte) {
4055                 if (prombyte % n == 0 && prombyte != 0) {
4056                         sim_printf("\n"); }
4057                 sim_printf("%03d: %02x [", prombyte, PROM[prombyte]);
4058                 if (PROM[prombyte] > 31) {
4059                         if (PROM[prombyte] < 128) {
4060                                 sim_printf("%c]  ", (unsigned char)PROM[prombyte]); }
4061                 } else {
4062                         sim_printf(".]  "); }
4063         }
4064         sim_printf("\n");
4065         return 0;
4066 }
4067 
show_buildinfo(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4068 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4069 {
4070     fprintf (st, " Build Information:\n");
4071 #if defined(BUILDINFO_scp) && defined(SYSDEFS_USED)
4072     fprintf (st, "      Compilation info: %s\n", BUILDINFO_scp );
4073 # ifndef __OPEN64__
4074     fprintf (st, "  Relevant definitions: %s\n", SYSDEFS_USED );
4075 # endif
4076 #elif defined(BUILDINFO_scp)
4077     fprintf (st, "      Compilation info: %s\n", BUILDINFO_scp );
4078 #else
4079     fprintf (st, "      Compilation info: Not available\n" );
4080 #endif
4081 #if defined (UV_VERSION_MAJOR) && \
4082     defined (UV_VERSION_MINOR) && \
4083     defined (UV_VERSION_PATCH)
4084 # ifdef UV_VERSION_MAJOR
4085 #  ifndef UV_VERSION_MINOR
4086 #   ifndef UV_VERSION_PATCH
4087 #    ifndef UV_VERSION_SUFFIX
4088     fprintf (st, "    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4089 #    endif /* ifndef UV_VERSION_SUFFIX */
4090 #   endif /* ifndef UV_VERSION_PATCH */
4091 #  endif /* ifndef UV_VERSION_MINOR */
4092 #  ifdef UV_VERSION_MINOR
4093 #   ifndef UV_VERSION_PATCH
4094 #    ifndef UV_VERSION_SUFFIX
4095     fprintf (st, "    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4096             UV_VERSION_MINOR);
4097 #    endif /* ifndef UV_VERSION_SUFFIX */
4098 #   endif /* ifndef UV_VERSION_PATCH */
4099 #   ifdef UV_VERSION_PATCH
4100 #    ifndef UV_VERSION_SUFFIX
4101     fprintf (st, "    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4102             UV_VERSION_MINOR, UV_VERSION_PATCH);
4103 #    endif /* ifndef UV_VERSION_SUFFIX */
4104 #    ifdef UV_VERSION_SUFFIX
4105     fprintf (st, "    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4106             UV_VERSION_MINOR, UV_VERSION_PATCH);
4107 #     ifdef UV_VERSION_IS_RELEASE
4108 #      if UV_VERSION_IS_RELEASE == 1
4109 #       define UV_RELEASE_TYPE " (release)"
4110 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4111 #      if UV_VERSION_IS_RELEASE == 0
4112 #       define UV_RELEASE_TYPE " (snapshot)"
4113 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4114 #      ifndef UV_RELEASE_TYPE
4115 #       define UV_RELEASE_TYPE ""
4116 #      endif /* ifndef UV_RELEASE_TYPE */
4117 #      ifdef UV_RELEASE_TYPE
4118     fprintf (st, "%s", UV_RELEASE_TYPE);
4119 #      endif /* ifdef UV_RELEASE_TYPE */
4120 #     endif /* ifdef UV_VERSION_IS_RELEASE */
4121 #    endif /* ifdef UV_VERSION_SUFFIX */
4122 #   endif /* ifdef UV_VERSION_PATCH */
4123 #  endif /* ifdef UV_VERSION_MINOR */
4124     unsigned int CurrentUvVersion = uv_version();
4125     if (((void *)&CurrentUvVersion != NULL) && (CurrentUvVersion > 0))
4126         if (uv_version_string() != NULL)
4127             fprintf (st, "; %s in use", uv_version_string());
4128 # endif /* ifdef UV_VERSION_MAJOR */
4129 #else
4130     fprintf (st, "    Event loop library: Using libuv (or compatible) library, unknown version");
4131 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4132         *    defined(UV_VERSION_MINOR) &&  \
4133         *    defined(UV_VERSION_PATCH)     \
4134         */
4135 #ifdef DECNUMBERLOC
4136     fprintf (st, "\n");
4137 # ifdef DECVERSION
4138 #  ifdef DECNLAUTHOR
4139     fprintf (st, "          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4140 #  else
4141     fprintf (st, "          Math library: %s", DECVERSION);
4142 #  endif /* ifdef DECNLAUTHOR */
4143 # else
4144     fprintf (st, "          Math library: decNumber, unknown version");
4145 # endif /* ifdef DECVERSION */
4146 #endif /* ifdef DECNUMBERLOC */
4147 #ifdef LOCKLESS
4148     fprintf (st, "\n     Atomic operations: ");
4149 # if defined(AIX_ATOMICS)
4150     fprintf (st, "IBM AIX-style");
4151 # elif defined(BSD_ATOMICS)
4152     fprintf (st, "FreeBSD-style");
4153 # elif defined(GNU_ATOMICS)
4154     fprintf (st, "GNU-style");
4155 # elif defined(SYNC_ATOMICS)
4156     fprintf (st, "GNU sync-style");
4157 # elif defined(ISO_ATOMICS)
4158     fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4159 # elif defined(NT_ATOMICS)
4160     fprintf (st, "Windows NT interlocked operations");
4161 # endif
4162 #endif /* ifdef LOCKLESS */
4163     fprintf (st, "\n          File locking: ");
4164 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4165     fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4166 #endif
4167 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4168     fprintf (st, "POSIX-style fcntl() locking");
4169 #endif
4170 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4171     fprintf (st, "BSD-style flock() locking");
4172 #endif
4173 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4174     fprintf (st, "No file locking available");
4175 #endif
4176     fprintf (st, "\n");
4177     return 0;
4178 }
4179 
show_version(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4180 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4181 {
4182 const char *arch = "";
4183 int dirty = 0;
4184 
4185 if (cptr && (*cptr != 0))
4186     return SCPE_2MARG;
4187 if (flag) {
4188         fprintf (st, " %s Simulator:", sim_name);
4189 #if defined(NO_SUPPORT_VERSION) || \
4190     defined(TESTING)            || \
4191     defined(ISOLTS)
4192 # ifndef NO_SUPPORT_VERSION
4193 #  define NO_SUPPORT_VERSION 1
4194 # endif
4195 #endif
4196 #if defined(NO_SUPPORT_VERSION)
4197         dirty++;
4198 #endif
4199 #if defined(GENERATED_MAKE_VER_H)
4200 # if defined(VER_H_GIT_VERSION)
4201 
4202         /* Dirty if git source is dirty */
4203         if (strstr(VER_H_GIT_VERSION, "*"))
4204           {
4205                 dirty++;
4206           }
4207 
4208         /* Dirty if post-tag patches detected */
4209         if (strstr(VER_H_GIT_VERSION, "+"))
4210           {
4211                 dirty++;
4212           }
4213 
4214         /* Dirty if version contains "Z", "D", "A", or "B" */
4215         if ((strstr(VER_H_GIT_VERSION, "Z")) || \
4216             (strstr(VER_H_GIT_VERSION, "D")) || \
4217             (strstr(VER_H_GIT_VERSION, "A")) || \
4218             (strstr(VER_H_GIT_VERSION, "B"))) {
4219                 dirty++;
4220         }
4221 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4222 #   if defined(VER_H_GIT_HASH)
4223 #    if VER_H_GIT_PATCH_INT < 1
4224     fprintf (st, "\n   Version: %s\n    Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_HASH);
4225 #    else
4226 #     define NO_SUPPORT_VERSION 1
4227     fprintf (st, "\n   Version: %s+%s\n    Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_PATCH, VER_H_GIT_HASH);
4228 #    endif
4229 #   else
4230 #    if VER_H_GIT_PATCH_INT < 1
4231         fprintf (st, "\n   Version: %s", VER_H_GIT_VERSION);
4232 #    else
4233 #     define NO_SUPPORT_VERSION 1
4234         fprintf (st, "\n   Version: %s+%s", VER_H_GIT_VERSION, VER_H_GIT_PATCH);
4235 #    endif
4236 #   endif
4237 #  else
4238 #   if defined(VER_H_GIT_HASH)
4239         fprintf (st, "\n   Version: %s\n    Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_HASH);
4240 #   else
4241         fprintf (st, "\n   Version: %s", VER_H_GIT_VERSION);
4242 #   endif
4243 #  endif
4244 # endif
4245 #endif
4246 #ifdef TESTING
4247     fprintf (st, "\n   Options: ");
4248 # ifndef HAVE_DPSOPT
4249 #  define HAVE_DPSOPT 1
4250 # endif
4251     fprintf (st, "TESTING");
4252 #endif
4253 #ifdef ISOLTS
4254 # ifdef HAVE_DPSOPT
4255     fprintf (st, ", ");
4256 # else
4257     fprintf (st, "\n   Options: ");
4258 # endif
4259 # ifndef HAVE_DPSOPT
4260 #  define HAVE_DPSOPT 1
4261 # endif
4262     fprintf (st, "ISOLTS");
4263 #endif
4264 #ifdef NEED_128
4265 # ifdef HAVE_DPSOPT
4266     fprintf (st, ", ");
4267 # else
4268     fprintf (st, "\n   Options: ");
4269 # endif
4270 # ifndef HAVE_DPSOPT
4271 #  define HAVE_DPSOPT 1
4272 # endif
4273     fprintf (st, "NEED_128");
4274 #endif
4275 #ifdef WAM
4276 # ifdef HAVE_DPSOPT
4277     fprintf (st, ", ");
4278 # else
4279     fprintf (st, "\n   Options: ");
4280 # endif
4281 # ifndef HAVE_DPSOPT
4282 #  define HAVE_DPSOPT 1
4283 # endif
4284     fprintf (st, "WAM");
4285 #endif
4286 #ifdef ROUND_ROBIN
4287 # ifdef HAVE_DPSOPT
4288     fprintf (st, ", ");
4289 # else
4290     fprintf (st, "\n   Options: ");
4291 # endif
4292 # ifndef HAVE_DPSOPT
4293 #  define HAVE_DPSOPT 1
4294 # endif
4295     fprintf (st, "ROUND_ROBIN");
4296 #endif
4297 #ifndef LOCKLESS
4298 # ifdef HAVE_DPSOPT
4299     fprintf (st, ", ");
4300 # else
4301     fprintf (st, "\n   Options: ");
4302 # endif
4303 # ifndef HAVE_DPSOPT
4304 #  define HAVE_DPSOPT 1
4305 # endif
4306     fprintf (st, "NO_LOCKLESS");
4307 #endif
4308 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
4309 # if defined(NO_SUPPORT_VERSION)
4310         fprintf (st, "\n  Modified: %s", VER_H_GIT_DATE);
4311 # else
4312         fprintf (st, "\n  Released: %s", VER_H_GIT_DATE);
4313 # endif
4314 #endif
4315 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
4316     fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
4317 #endif
4318 #ifdef VER_CURRENT_TIME
4319         fprintf (st, "\n  Compiled: %s", VER_CURRENT_TIME);
4320 #endif
4321         if (dirty)
4322                 {
4323                         fprintf (st, "\r\n\r\n ****** THIS BUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******");
4324                 }
4325     fprintf (st, "\n\n Build Information:");
4326 #if defined(VER_H_PREP_OSVN)
4327         fprintf (st, "\n  Build OS: %s", VER_H_PREP_OSVN);
4328 #endif
4329 #if defined (__VERSION__)
4330     char gnumver[2];
4331     char postver[1024];
4332     sprintf(gnumver, "%.1s", __VERSION__);
4333     sprintf(postver, "%.1023s", __VERSION__);
4334     strremove(postver, "(TM)");
4335     strremove(postver, "(R)");
4336     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
4337     strremove(postver, "4.2.1 Compatible ");
4338     strremove(postver, "git@github.com:llvm/llvm-project.git ");
4339     strremove(postver, "https://github.com/llvm/llvm-project.git ");
4340     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
4341     strremove(postver, "https://github.com/yrnkrn/zapcc ");
4342 #endif
4343 #if defined (__GNUC__) && defined (__VERSION__)
4344 # ifndef __clang_version__
4345     if (isdigit(gnumver[0])) {
4346         fprintf (st, "\n  Compiler: GCC %s", postver);
4347     } else {
4348         fprintf (st, "\n  Compiler: %s", postver);
4349     }
4350 # endif
4351 # if defined (__clang_version__) && defined (__VERSION__)
4352     char clangllvmver[1024];
4353     sprintf(clangllvmver, "%.1023s", __clang_version__);
4354     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
4355     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
4356         fprintf (st, "\n  Compiler: Clang %s", clangllvmver);
4357     } else {
4358         fprintf (st, "\n  Compiler: %s", postver);
4359     }
4360 # elif defined (__clang_version__)
4361     fprintf (st, "\n  Compiler: %s", postver);
4362 # endif
4363 #elif defined (_MSC_FULL_VER) && defined (_MSC_BUILD)
4364     fprintf (st, "\n  Compiler: Microsoft C %d.%02d.%05d.%02d", _MSC_FULL_VER/10000000, (_MSC_FULL_VER/100000)%100, _MSC_FULL_VER%100000, _MSC_BUILD);
4365 #elif ( defined (__xlc__) && !defined(__clang_version__) )
4366 # if defined (_AIX) && defined (PASE)
4367     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
4368 # endif
4369 # if defined (_AIX) && !defined (PASE)
4370     fprintf (st, "\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
4371 # endif
4372 # if defined (__linux__) && ( !defined(_AIX) || !defined(PASE) )
4373     fprintf (st, "\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
4374 # endif
4375 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(PASE) && !defined(__linux__) && defined(__xlc__) )
4376     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s", __xlc__);
4377 # endif
4378 #elif defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__SUNPRO_CC_COMPAT)
4379     fprintf (st, "\n  Compiler: Oracle Developer Studio C/C++");
4380 #elif defined (__DMC__)
4381     fprintf (st, "\n  Compiler: Digital Mars C/C++");
4382 #elif defined (__PCC__)
4383     fprintf (st, "\n  Compiler: Portable C Compiler");
4384 #elif defined (KENC) || defined (KENCC) || defined (__KENC__) || defined (__KENCC__)
4385     fprintf (st, "\n  Compiler: Plan 9 Compiler Suite");
4386 #elif defined (__ACK__)
4387     fprintf (st, "\n  Compiler: Amsterdam Compiler Kit");
4388 #elif defined (__COMO__)
4389     fprintf (st, "\n  Compiler: Comeau C++");
4390 #elif defined (__COMPCERT__)
4391     fprintf (st, "\n  Compiler: CompCert C");
4392 #elif defined (__COVERITY__)
4393     fprintf (st, "\n  Compiler: Coverity C/C++ Static Analyzer");
4394 #elif defined (__LCC__)
4395     fprintf (st, "\n  Compiler: Local C Compiler (lcc)");
4396 #elif defined (sgi) || defined (__sgi) || defined (_sgi) || defined (_SGI_COMPILER_VERSION)
4397     fprintf (st, "\n  Compiler: SGI MIPSpro");
4398 #elif defined (__OPEN64__)
4399     fprintf (st, "\n  Compiler: Open64 %s", __OPEN64__);
4400 #elif defined (__PGI) || defined (__PGIC__)
4401     fprintf (st, "\n  Compiler: Portland Group/PGI C/C++");
4402 #elif defined (__VBCC__)
4403     fprintf (st, "\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
4404 #elif defined (__WATCOMC__)
4405     fprintf (st, "\n  Compiler: Watcom C/C++ %d.%d", __WATCOMC__ / 100, __WATCOMC__ % 100);
4406 #elif defined (__xlC__)
4407     fprintf (st, "\n  Compiler: IBM XL C/C++");
4408 #elif defined (SIM_COMPILER)
4409 # define S_xstr(a) S_str(a)
4410 # define S_str(a) #a
4411     fprintf (st, "\n  Compiler: %s", S_xstr(SIM_COMPILER));
4412 # undef S_str
4413 # undef S_xstr
4414 #else
4415     fprintf (st, "\n  Compiler: Unknown");
4416 #endif
4417 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
4418     arch = " x86_64";
4419 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
4420     arch = " x86";
4421 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
4422     arch = " arm64";
4423 #elif defined(_M_ARM) || defined(__arm__)
4424     arch = " arm";
4425 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
4426     arch = " ia64";
4427 #elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
4428 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4429     arch = " ppc64be";
4430 # else
4431     arch = " ppc64el";
4432 # endif
4433 #elif defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__PPC) || defined(__ppc32__) || defined(__PPC32__) || defined(__powerpc32__) || defined(__POWERPC32__) || defined(_M_PPC32) || defined(__PPC32)
4434 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4435     arch = " ppc";
4436 # else
4437     arch = " ppcel";
4438 # endif
4439 #elif defined(__s390x__)
4440     arch = " s390x";
4441 #elif defined(__s390__)
4442     arch = " s390";
4443 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
4444     arch = " j2";
4445 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
4446     arch = " sh4";
4447 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
4448     arch = " sh2";
4449 #elif defined(__alpha__)
4450     arch = " alpha";
4451 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
4452     arch = " hppa";
4453 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
4454     arch = " ice9";
4455 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
4456 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4457     arch = " mips64be";
4458 # else
4459     arch = " mips64el";
4460 # endif
4461 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
4462 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4463     arch = " mipsbe";
4464 # else
4465     arch = " mipsel";
4466 # endif
4467 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
4468     arch = " openrisc";
4469 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
4470     arch = " sparc64";
4471 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
4472     arch = " sparc";
4473 #elif defined(__riscv) || defined(__riscv__)
4474     arch = " riscv";
4475 #elif defined(__myriad2__)
4476     arch = " myriad2";
4477 #else
4478     arch = " ";
4479 #endif
4480     fprintf (st, "%s", arch);
4481 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
4482         fprintf (st, "\n  Built by: %s", VER_H_PREP_USER);
4483 #endif
4484                 fprintf (st, "\n\n Host System Information:");
4485 #if defined(_WIN32)
4486     if (1) {
4487         char *proc_id = getenv ("PROCESSOR_IDENTIFIER");
4488         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
4489         char *procs = getenv ("NUMBER_OF_PROCESSORS");
4490         char *proc_level = getenv ("PROCESSOR_LEVEL");
4491         char *proc_rev = getenv ("PROCESSOR_REVISION");
4492         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
4493         char osversion[PATH_MAX+1] = "";
4494         FILE *f;
4495 
4496         if ((f = _popen ("ver", "r"))) {
4497             memset (osversion, 0, sizeof(osversion));
4498             do {
4499                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
4500                     break;
4501                 sim_trim_endspc (osversion);
4502                 } while (osversion[0] == '\0');
4503             _pclose (f);
4504             }
4505         fprintf (st, "\n   Host OS: %s", osversion);
4506         fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
4507         }
4508 #else
4509     if (1) {
4510         char osversion[2*PATH_MAX+1] = "";
4511         FILE *f;
4512 # ifndef _AIX
4513         if ((f = popen ("uname -mrs 2> /dev/null", "r"))) {
4514 # else
4515         if ((f = popen                                              \
4516           ("sh -c 'command -p env uname -svM   2> /dev/null'        \
4517                                                2> /dev/null    ||   \
4518               /bin/sh -c 'command -p env uname -svM                 \
4519                                                2> /dev/null'        \
4520                                                2> /dev/null    ||   \
4521                   uname -svM                   2> /dev/null    ||   \
4522                     sh -c 'command -p env uname -svp                \
4523                                                2> /dev/null'        \
4524                                                2> /dev/null    ||   \
4525                         /bin/sh -c 'command -p env uname -svp       \
4526                                                2> /dev/null'        \
4527                                                2> /dev/null    ||   \
4528                             uname -svp         2> /dev/null         \
4529                               ", "r")))                             \
4530          {
4531 # endif /* ifndef _AIX */
4532             memset (osversion, 0, sizeof(osversion));
4533             do {
4534                 if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
4535                     break;
4536                 }
4537                 sim_trim_endspc (osversion);
4538                 } while (osversion[0] == '\0');
4539             pclose (f);
4540             strremove(osversion, "0000000000000000 ");
4541             strremove(osversion, " 0000000000000000");
4542             strremove(osversion, "000000000000 ");
4543             strremove(osversion, " 000000000000");
4544             strremove(osversion, "IBM ");
4545             strremove(osversion, " (emulated by qemu)");
4546             strremove(osversion, " (emulated by QEMU)");
4547         }
4548 # ifndef _AIX
4549             fprintf (st, "\n   Host OS: %s", osversion);
4550 # else
4551             strremove(osversion, "AIX ");
4552             fprintf (st, "\n   Host OS: IBM AIX %s", osversion);
4553 # endif /* ifndef _AIX */
4554     } else {
4555 # ifndef _AIX
4556         fprintf (st, "\n   Host OS: Unknown");
4557 # else
4558         fprintf (st, "\n   Host OS: IBM AIX");
4559 # endif /* ifndef _AIX */
4560     }
4561 #endif
4562 #if defined(__APPLE__)
4563     int isRosetta = processIsTranslated();
4564     if (isRosetta == 1) {
4565         sim_printf ("\n\n  ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
4566     }
4567 #endif
4568 fprintf (st, "\n");
4569 fprintf (st, "\n This software is made available under the terms of the ICU License,");
4570 fprintf (st, "\n version 1.8.1 or later.  For complete details, see the \"LICENSE.md\"");
4571 fprintf (st, "\n included or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
4572 fprintf (st, "\n");
4573     }
4574 return SCPE_OK;
4575 }
4576 
4577 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4578 {
4579 int32 i;
4580 DEVICE *dptr;
4581 t_bool only_enabled = (sim_switches & SWMASK ('E'));
4582 
4583 if (cptr && (*cptr != 0))
4584     return SCPE_2MARG;
4585 fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
4586 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4587     if (!only_enabled || !qdisable (dptr))
4588         show_device (st, dptr, flag);
4589 return SCPE_OK;
4590 }
4591 
4592 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4593 {
4594 int32 i;
4595 DEVICE *dptr;
4596 
4597 if (cptr && (*cptr != 0))
4598     return SCPE_2MARG;
4599 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4600     show_dev_logicals (st, dptr, NULL, 1, cptr);
4601 return SCPE_OK;
4602 }
4603 
4604 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4605 {
4606 if (dptr->lname)
4607     fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
4608 else if (!flag)
4609     fputs ("no logical name assigned\n", st);
4610 return SCPE_OK;
4611 }
4612 
4613 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4614 {
4615 DEVICE *dptr;
4616 UNIT *uptr;
4617 int32 accum;
4618 
4619 if (cptr && (*cptr != 0))
4620     return SCPE_2MARG;
4621 if (sim_clock_queue == QUEUE_LIST_END)
4622     fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructios/sec\n",
4623              sim_name, sim_time, sim_timer_inst_per_sec ());
4624 else {
4625     const char *tim;
4626 
4627     fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
4628              sim_name, sim_time, sim_timer_inst_per_sec ());
4629     accum = 0;
4630     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
4631         if (uptr == &sim_step_unit)
4632             fprintf (st, "  Step timer");
4633         else
4634             if (uptr == &sim_expect_unit)
4635                 fprintf (st, "  Expect fired");
4636             else
4637                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
4638                     fprintf (st, "  %s", sim_dname (dptr));
4639                     if (dptr->numunits > 1)
4640                         fprintf (st, " unit %d", (int32) (uptr - dptr->units));
4641                     }
4642                 else
4643                     fprintf (st, "  Unknown");
4644         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
4645         fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
4646                                         (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
4647                                         (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
4648         accum = accum + uptr->time;
4649         }
4650     }
4651 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
4652 return SCPE_OK;
4653 }
4654 
4655 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4656 {
4657 if (cptr && (*cptr != 0))
4658     return SCPE_2MARG;
4659 fprintf (st, "Time:\t%.0f\n", sim_gtime());
4660 return SCPE_OK;
4661 }
4662 
4663 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4664 {
4665 t_stat r;
4666 
4667 if (cptr && (*cptr != 0))
4668     r = ssh_break (st, cptr, 1);  /* more? */
4669 else
4670     r = sim_brk_showall (st, sim_switches);
4671 return r;
4672 }
4673 
4674 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4675 {
4676 fprintf (st, "Radix=%d\n", dptr->dradix);
4677 return SCPE_OK;
4678 }
4679 
4680 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4681 {
4682 int32 any = 0;
4683 DEBTAB *dep;
4684 
4685 if (dptr->flags & DEV_DEBUG) {
4686     if (dptr->dctrl == 0)
4687         fputs ("Debugging disabled", st);
4688     else if (dptr->debflags == NULL)
4689         fputs ("Debugging enabled", st);
4690     else {
4691         uint32 dctrl = dptr->dctrl;
4692 
4693         fputs ("Debug=", st);
4694         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
4695             if ((dctrl & dep->mask) == dep->mask) {
4696                 dctrl &= ~dep->mask;
4697                 if (any)
4698                     fputc (';', st);
4699                 fputs (dep->name, st);
4700                 any = 1;
4701                 }
4702             }
4703         }
4704     fputc ('\n', st);
4705     return SCPE_OK;
4706     }
4707 else return SCPE_NOFNC;
4708 }
4709 
4710 /* Show On actions */
4711 
4712 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4713 {
4714 int32 lvl, i;
4715 
4716 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
4717 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
4718     if (lvl > 0)
4719         fprintf(st, "On Processing at Do Nest Level: %d", lvl);
4720     else
4721         fprintf(st, "On Processing for input commands");
4722     fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
4723     for (i=1; i<SCPE_BASE; ++i) {
4724         if (sim_on_actions[lvl][i])
4725             fprintf(st, "    on %5d    %s\n", i, sim_on_actions[lvl][i]); }
4726     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
4727         if (sim_on_actions[lvl][i])
4728             fprintf(st, "    on %-5s    %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
4729     if (sim_on_actions[lvl][0])
4730         fprintf(st, "    on ERROR    %s\n", sim_on_actions[lvl][0]);
4731     fprintf(st, "\n");
4732     }
4733 if (sim_on_inherit)
4734     fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
4735 return SCPE_OK;
4736 }
4737 
4738 /* Show modifiers */
4739 
4740 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4741 {
4742 int32 i;
4743 DEVICE *dptr;
4744 
4745 if (cptr && (*cptr != 0))                               /* now eol? */
4746     return SCPE_2MARG;
4747 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4748     show_dev_modifiers (st, dptr, NULL, flag, cptr);
4749 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
4750     show_dev_modifiers (st, dptr, NULL, flag, cptr);
4751 return SCPE_OK;
4752 }
4753 
4754 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4755 {
4756 fprint_set_help (st, dptr);
4757 return SCPE_OK;
4758 }
4759 
4760 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
4761 {
4762 MTAB *mptr;
4763 t_stat r = SCPE_OK;
4764 
4765 if (dptr->modifiers == NULL)
4766     return SCPE_OK;
4767 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
4768     if (mptr->pstring &&
4769         ((mptr->mask & MTAB_XTD)?
4770             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
4771             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
4772         if (*toks > 2) {
4773             fprintf (st, "\n");
4774             *toks = 0;
4775             }
4776         if (r == SCPE_OK)
4777             fprint_sep (st, toks);
4778         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
4779         }
4780     }
4781 return SCPE_OK;
4782 }
4783 
4784 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
4785     CONST char *cptr, int32 flag)
4786 {
4787 t_stat r = SCPE_OK;
4788 
4789 if (mptr->disp)
4790     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
4791 else
4792     fputs (mptr->pstring, st);
4793 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
4794     fputc ('\n', st);
4795 return r;
4796 }
4797 
4798 /* Show show commands */
4799 
4800 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4801 {
4802 int32 i;
4803 DEVICE *dptr;
4804 
4805 if (cptr && (*cptr != 0))                               /* now eol? */
4806     return SCPE_2MARG;
4807 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4808     show_dev_show_commands (st, dptr, NULL, flag, cptr);
4809 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
4810     show_dev_show_commands (st, dptr, NULL, flag, cptr);
4811 return SCPE_OK;
4812 }
4813 
4814 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4815 {
4816 fprint_show_help (st, dptr);
4817 return SCPE_OK;
4818 }
4819 
4820 /* Breakpoint commands */
4821 
4822 t_stat brk_cmd (int32 flg, CONST char *cptr)
4823 {
4824 GET_SWITCHES (cptr);                                    /* get switches */
4825 return ssh_break (NULL, cptr, flg);                     /* call common code */
4826 }
4827 
4828 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
4829 {
4830 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
4831 CONST char *tptr, *t1ptr;
4832 DEVICE *dptr = sim_dflt_dev;
4833 UNIT *uptr = dptr ? dptr->units : NULL;
4834 t_stat r;
4835 t_addr lo, hi, max = uptr->capac - 1;
4836 int32 cnt;
4837 
4838 if (sim_brk_types == 0)
4839     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
4840 if ((dptr == NULL) || (uptr == NULL))
4841     return SCPE_IERR;
4842 abuf[sizeof(abuf)-1] = '\0';
4843 strncpy (abuf, cptr, sizeof(abuf)-1);
4844 cptr = abuf;
4845 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
4846     if (flg != SSH_ST)                                  /* only on SET */
4847         return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
4848     *aptr++ = 0;                                        /* separate strings */
4849     }
4850 if (*cptr == 0) {                                       /* no argument? */
4851     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
4852     return ssh_break_one (st, flg, lo, 0, aptr);
4853     }
4854 while (*cptr) {
4855     cptr = get_glyph (cptr, gbuf, ',');
4856     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
4857     if (tptr == NULL)
4858         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
4859     if (*tptr == '[') {
4860         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
4861         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
4862             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
4863         tptr = t1ptr + 1;
4864         }
4865     else cnt = 0;
4866     if (*tptr != 0)
4867         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
4868     if ((lo == 0) && (hi == max)) {
4869         if (flg == SSH_CL)
4870             sim_brk_clrall (sim_switches);
4871         else
4872             if (flg == SSH_SH)
4873                 sim_brk_showall (st, sim_switches);
4874             else
4875                 return SCPE_ARG;
4876         }
4877     else {
4878         for ( ; lo <= hi; lo = lo + 1) {
4879             r = ssh_break_one (st, flg, lo, cnt, aptr);
4880             if (r != SCPE_OK)
4881                 return r;
4882             }
4883         }
4884     }
4885 return SCPE_OK;
4886 }
4887 
4888 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr)
4889 {
4890 if (!sim_brk_types)
4891     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
4892 switch (flg) {
4893 
4894     case SSH_ST:
4895         return sim_brk_set (lo, sim_switches, cnt, aptr);
4896         break;
4897 
4898     case SSH_CL:
4899         return sim_brk_clr (lo, sim_switches);
4900         break;
4901 
4902     case SSH_SH:
4903         return sim_brk_show (st, lo, sim_switches);
4904         break;
4905 
4906     default:
4907         return SCPE_ARG;
4908     }
4909 }
4910 
4911 /* Reset command and routines
4912 
4913    re[set]              reset all devices
4914    re[set] all          reset all devices
4915    re[set] device       reset specific device
4916 */
4917 
4918 static t_bool run_cmd_did_reset = FALSE;
4919 
4920 t_stat reset_cmd (int32 flag, CONST char *cptr)
4921 {
4922 char gbuf[CBUFSIZE];
4923 DEVICE *dptr;
4924 
4925 GET_SWITCHES (cptr);                                    /* get switches */
4926 run_cmd_did_reset = FALSE;
4927 if (*cptr == 0)                                         /* reset(cr) */
4928     return (reset_all (0));
4929 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
4930 if (*cptr != 0)                                         /* now eol? */
4931     return SCPE_2MARG;
4932 if (strcmp (gbuf, "ALL") == 0)
4933     return (reset_all (0));
4934 dptr = find_dev (gbuf);                                 /* locate device */
4935 if (dptr == NULL)                                       /* found it? */
4936     return SCPE_NXDEV;
4937 if (dptr->reset != NULL)
4938     return dptr->reset (dptr);
4939 else return SCPE_OK;
4940 }
4941 
4942 /* Reset devices start..end
4943 
4944    Inputs:
4945         start   =       number of starting device
4946    Outputs:
4947         status  =       error status
4948 */
4949 
4950 t_stat reset_all (uint32 start)
4951 {
4952 DEVICE *dptr;
4953 uint32 i;
4954 t_stat reason;
4955 
4956 for (i = 0; i < start; i++) {
4957     if (sim_devices[i] == NULL)
4958         return SCPE_IERR;
4959     }
4960 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
4961     if (dptr->reset != NULL) {
4962         reason = dptr->reset (dptr);
4963         if (reason != SCPE_OK)
4964             return reason;
4965         }
4966     }
4967 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
4968     if (dptr->reset != NULL) {
4969         reason = dptr->reset (dptr);
4970         if (reason != SCPE_OK)
4971             return reason;
4972         }
4973     }
4974 return SCPE_OK;
4975 }
4976 
4977 /* Reset to powerup state
4978 
4979    Inputs:
4980         start   =       number of starting device
4981    Outputs:
4982         status  =       error status
4983 */
4984 
4985 t_stat reset_all_p (uint32 start)
4986 {
4987 t_stat r;
4988 int32 old_sw = sim_switches;
4989 
4990 sim_switches = SWMASK ('P');
4991 r = reset_all (start);
4992 sim_switches = old_sw;
4993 return r;
4994 }
4995 
4996 /* Attach command
4997 
4998    at[tach] unit file   attach specified unit to file
4999 */
5000 
5001 t_stat attach_cmd (int32 flag, CONST char *cptr)
5002 {
5003 char gbuf[4*CBUFSIZE];
5004 DEVICE *dptr;
5005 UNIT *uptr;
5006 t_stat r;
5007 
5008 GET_SWITCHES (cptr);                                    /* get switches */
5009 if (*cptr == 0)                                         /* must be more */
5010     return SCPE_2FARG;
5011 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5012 GET_SWITCHES (cptr);                                    /* get switches */
5013 if (*cptr == 0)                                         /* now eol? */
5014     return SCPE_2FARG;
5015 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5016 if (dptr == NULL)                                       /* found dev? */
5017     return SCPE_NXDEV;
5018 if (uptr == NULL)                                       /* valid unit? */
5019     return SCPE_NXUN;
5020 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
5021     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
5022         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
5023         r = scp_detach_unit (dptr, uptr);               /* detach it */
5024         if (r != SCPE_OK)                               /* error? */
5025             return r;
5026         }
5027     else {
5028         if (!(uptr->dynflags & UNIT_ATTMULT))
5029             return SCPE_ALATT;                          /* Already attached */
5030         }
5031     }
5032 gbuf[sizeof(gbuf)-1] = '\0';
5033 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5034 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
5035 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
5036 }
5037 
5038 /* Call device-specific or file-oriented attach unit routine */
5039 
5040 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
5041 {
5042 if (dptr->attach != NULL)                               /* device routine? */
5043     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
5044 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
5045 }
5046 
5047 /* Attach unit to file */
5048 
5049 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
5050 {
5051 DEVICE *dptr;
5052 
5053 if (uptr->flags & UNIT_DIS)                             /* disabled? */
5054     return SCPE_UDIS;
5055 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
5056     return SCPE_NOATT;
5057 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5058     return SCPE_NOATT;
5059 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
5060 if (uptr->filename == NULL)
5061     return SCPE_MEM;
5062 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
5063 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
5064     ((uptr->flags & UNIT_RO) != 0)) {
5065     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
5066         ((uptr->flags & UNIT_RO) == 0))
5067         return attach_err (uptr, SCPE_NORO);            /* no, error */
5068     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
5069     if (uptr->fileref == NULL)                          /* open fail? */
5070         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
5071     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
5072     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5073         sim_printf ("%s: unit is read only\n", sim_dname (dptr));
5074         }
5075     }
5076 else {
5077     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
5078         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
5079         if (uptr->fileref == NULL)                      /* open fail? */
5080             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
5081         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5082             sim_printf ("%s: creating new file\n", sim_dname (dptr));
5083             }
5084         }
5085     else {                                              /* normal */
5086         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
5087         if (uptr->fileref == NULL) {                    /* open fail? */
5088 #if defined (EWOULDBLOCK)
5089             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
5090 #else
5091             if ((errno == EAGAIN))
5092 #endif
5093                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5094 
5095 #if defined(EPERM)
5096             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
5097 #else
5098             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
5099 #endif
5100                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
5101                     return attach_err (uptr, SCPE_NORO);/* no error */
5102                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
5103                 if (uptr->fileref == NULL)              /* open fail? */
5104                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5105                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
5106                 if (!sim_quiet) {
5107                     sim_printf ("%s: unit is read only\n", sim_dname (dptr));
5108                     }
5109                 }
5110             else {                                      /* doesn't exist */
5111                 if (sim_switches & SWMASK ('E'))        /* must exist? */
5112                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5113                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
5114                 if (uptr->fileref == NULL)              /* open fail? */
5115                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5116                 if (!sim_quiet) {
5117                     sim_printf ("%s: creating new file\n", sim_dname (dptr));
5118                     }
5119                 }
5120             }                                           /* end if null */
5121         }                                               /* end else */
5122     }
5123 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
5124     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
5125     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
5126         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
5127     if (uptr->filebuf == NULL)                          /* no buffer? */
5128         return attach_err (uptr, SCPE_MEM);             /* error */
5129     if (!sim_quiet) {
5130         sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
5131         }
5132     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
5133         SZ_D (dptr), cap, uptr->fileref);
5134     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
5135     }
5136 uptr->flags = uptr->flags | UNIT_ATT;
5137 uptr->pos = 0;
5138 return SCPE_OK;
5139 }
5140 
5141 t_stat attach_err (UNIT *uptr, t_stat stat)
5142 {
5143 free (uptr->filename);
5144 uptr->filename = NULL;
5145 return stat;
5146 }
5147 
5148 /* Detach command
5149 
5150    det[ach] all         detach all units
5151    det[ach] unit        detach specified unit
5152 */
5153 
5154 t_stat detach_cmd (int32 flag, CONST char *cptr)
5155 {
5156 char gbuf[CBUFSIZE];
5157 DEVICE *dptr;
5158 UNIT *uptr;
5159 
5160 GET_SWITCHES (cptr);                                    /* get switches */
5161 if (*cptr == 0)                                         /* must be more */
5162     return SCPE_2FARG;
5163 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5164 if (*cptr != 0)                                         /* now eol? */
5165     return SCPE_2MARG;
5166 if (strcmp (gbuf, "ALL") == 0)
5167     return (detach_all (0, FALSE));
5168 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5169 if (dptr == NULL)                                       /* found dev? */
5170     return SCPE_NXDEV;
5171 if (uptr == NULL)                                       /* valid unit? */
5172     return SCPE_NXUN;
5173 return scp_detach_unit (dptr, uptr);                    /* detach */
5174 }
5175 
5176 /* Detach devices start..end
5177 
5178    Inputs:
5179         start   =       number of starting device
5180         shutdown =      TRUE if simulator shutting down
5181    Outputs:
5182         status  =       error status
5183 
5184    Note that during shutdown, detach routines for non-attachable devices
5185    will be called.  These routines can implement simulator shutdown.  Error
5186    returns during shutdown are ignored.
5187 */
5188 
5189 t_stat detach_all (int32 start, t_bool shutdown)
5190 {
5191 uint32 i, j;
5192 DEVICE *dptr;
5193 UNIT *uptr;
5194 t_stat r;
5195 
5196 if ((start < 0) || (start > 1))
5197     return SCPE_IERR;
5198 if (shutdown)
5199     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
5200 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5201     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
5202         uptr = (dptr->units) + j;
5203         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
5204             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
5205             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
5206             r = scp_detach_unit (dptr, uptr);           /* detach unit */
5207 
5208             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
5209                 return r;                               /* bail out now with error status */
5210             }
5211         }
5212     }
5213 return SCPE_OK;
5214 }
5215 
5216 /* Call device-specific or file-oriented detach unit routine */
5217 
5218 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
5219 {
5220 if (dptr->detach != NULL)                               /* device routine? */
5221     return dptr->detach (uptr);
5222 return detach_unit (uptr);                              /* no, standard */
5223 }
5224 
5225 /* Detach unit from file */
5226 
5227 t_stat detach_unit (UNIT *uptr)
5228 {
5229 DEVICE *dptr;
5230 
5231 if (uptr == NULL)
5232     return SCPE_IERR;
5233 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
5234     return SCPE_NOATT;
5235 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
5236     if (sim_switches & SIM_SW_REST)                     /* restoring? */
5237         return SCPE_OK;                                 /* allow detach */
5238     else
5239         return SCPE_NOTATT;                             /* complain */
5240     }
5241 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5242     return SCPE_OK;
5243 if (uptr->flags & UNIT_BUF) {
5244     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5245     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
5246         if (!sim_quiet) {
5247             sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
5248             }
5249         rewind (uptr->fileref);
5250         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5251         if (ferror (uptr->fileref))
5252             sim_printf ("%s: I/O error - %s", sim_dname (dptr), strerror (errno));
5253         }
5254     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
5255         free (uptr->filebuf);                           /* free buf */
5256         uptr->filebuf = NULL;
5257         }
5258     uptr->flags = uptr->flags & ~UNIT_BUF;
5259     }
5260 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
5261 free (uptr->filename);
5262 uptr->filename = NULL;
5263 if (fclose (uptr->fileref) == EOF)
5264     return SCPE_IOERR;
5265 return SCPE_OK;
5266 }
5267 
5268 /* Assign command
5269 
5270    as[sign] device name assign logical name to device
5271 */
5272 
5273 t_stat assign_cmd (int32 flag, CONST char *cptr)
5274 {
5275 char gbuf[CBUFSIZE];
5276 DEVICE *dptr;
5277 
5278 GET_SWITCHES (cptr);                                    /* get switches */
5279 if (*cptr == 0)                                         /* must be more */
5280     return SCPE_2FARG;
5281 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5282 GET_SWITCHES (cptr);                                    /* get switches */
5283 if (*cptr == 0)                                         /* now eol? */
5284     return SCPE_2FARG;
5285 dptr = find_dev (gbuf);                                 /* locate device */
5286 if (dptr == NULL)                                       /* found dev? */
5287     return SCPE_NXDEV;
5288 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5289 if (*cptr != 0)                                         /* must be eol */
5290     return SCPE_2MARG;
5291 if (find_dev (gbuf))                                    /* name in use */
5292     return SCPE_ARG;
5293 deassign_device (dptr);                                 /* release current */
5294 return assign_device (dptr, gbuf);
5295 }
5296 
5297 t_stat assign_device (DEVICE *dptr, const char *cptr)
5298 {
5299 dptr->lname = (char *) calloc (1 + strlen (cptr), sizeof (char));
5300 if (dptr->lname == NULL)
5301     return SCPE_MEM;
5302 strcpy (dptr->lname, cptr);
5303 return SCPE_OK;
5304 }
5305 
5306 /* Deassign command
5307 
5308    dea[ssign] device    deassign logical name
5309 */
5310 
5311 t_stat deassign_cmd (int32 flag, CONST char *cptr)
5312 {
5313 char gbuf[CBUFSIZE];
5314 DEVICE *dptr;
5315 
5316 GET_SWITCHES (cptr);                                    /* get switches */
5317 if (*cptr == 0)                                         /* must be more */
5318     return SCPE_2FARG;
5319 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5320 if (*cptr != 0)                                         /* now eol? */
5321     return SCPE_2MARG;
5322 dptr = find_dev (gbuf);                                 /* locate device */
5323 if (dptr == NULL)                                       /* found dev? */
5324     return SCPE_NXDEV;
5325 return deassign_device (dptr);
5326 }
5327 
5328 t_stat deassign_device (DEVICE *dptr)
5329 {
5330 free (dptr->lname);
5331 dptr->lname = NULL;
5332 return SCPE_OK;
5333 }
5334 
5335 /* Get device display name */
5336 
5337 const char *sim_dname (DEVICE *dptr)
5338 {
5339 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
5340 }
5341 
5342 /* Get unit display name */
5343 
5344 const char *sim_uname (UNIT *uptr)
5345 {
5346 DEVICE *d = find_dev_from_unit(uptr);
5347 static char uname[CBUFSIZE];
5348 
5349 if (!d)
5350     return "";
5351 if (d->numunits == 1)
5352     return sim_dname (d);
5353 sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
5354 return uname;
5355 }
5356 
5357 /* Save command
5358 
5359    sa[ve] filename              save state to specified file
5360 */
5361 
5362 t_stat save_cmd (int32 flag, CONST char *cptr)
5363 {
5364 FILE *sfile;
5365 t_stat r;
5366 char gbuf[4*CBUFSIZE];
5367 
5368 GET_SWITCHES (cptr);                                    /* get switches */
5369 if (*cptr == 0)                                         /* must be more */
5370     return SCPE_2FARG;
5371 gbuf[sizeof(gbuf)-1] = '\0';
5372 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5373 sim_trim_endspc (gbuf);
5374 if ((sfile = sim_fopen (gbuf, "wb")) == NULL)
5375     return SCPE_OPENERR;
5376 r = sim_save (sfile);
5377 fclose (sfile);
5378 return r;
5379 }
5380 
5381 t_stat sim_save (FILE *sfile)
5382 {
5383 void *mbuf;
5384 int32 l, t;
5385 uint32 i, j, device_count;
5386 t_addr k, high;
5387 t_value val;
5388 t_stat r;
5389 t_bool zeroflg;
5390 size_t sz;
5391 DEVICE *dptr;
5392 UNIT *uptr;
5393 REG *rptr;
5394 
5395 #define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile)
5396 
5397 /* Don't make changes below without also changing save_vercur above */
5398 
5399 fprintf (sfile, "%s\n%s\n%s\n%s\n%.0f\n",
5400     save_vercur,                                        /* [V2.5] save format */
5401     sim_savename,                                       /* sim name */
5402     sim_si64, sim_sa64,                                 /* [V3.5] options */
5403     sim_time);                                          /* [V3.2] sim time */
5404 WRITE_I (sim_rtime);                                    /* [V2.6] sim rel time */
5405 fprintf (sfile, "simh git commit id: unknown\n");
5406 
5407 for (device_count = 0; sim_devices[device_count]; device_count++);/* count devices */
5408 for (i = 0; i < (device_count + sim_internal_device_count); i++) {/* loop thru devices */
5409     if (i < device_count)
5410         dptr = sim_devices[i];
5411     else
5412         dptr = sim_internal_devices[i - device_count];
5413     if (dptr->flags & DEV_NOSAVE)
5414         continue;
5415     fputs (dptr->name, sfile);                          /* device name */
5416     fputc ('\n', sfile);
5417     if (dptr->lname)                                    /* [V3.0] logical name */
5418         fputs (dptr->lname, sfile);
5419     fputc ('\n', sfile);
5420     WRITE_I (dptr->flags);                              /* [V2.10] flags */
5421     for (j = 0; j < dptr->numunits; j++) {
5422         uptr = dptr->units + j;
5423         t = sim_activate_time (uptr);
5424         WRITE_I (j);                                    /* unit number */
5425         WRITE_I (t);                                    /* activation time */
5426         WRITE_I (uptr->u3);                             /* unit specific */
5427         WRITE_I (uptr->u4);
5428         WRITE_I (uptr->u5);                             /* [V3.0] more unit */
5429         WRITE_I (uptr->u6);
5430         WRITE_I (uptr->flags);                          /* [V2.10] flags */
5431         WRITE_I (uptr->dynflags);
5432         WRITE_I (uptr->wait);
5433         WRITE_I (uptr->buf);
5434         WRITE_I (uptr->capac);                          /* [V3.5] capacity */
5435         if (uptr->flags & UNIT_ATT) {
5436             fputs (uptr->filename, sfile);
5437             if ((uptr->flags & UNIT_BUF) &&             /* writable buffered */
5438                 uptr->hwmark &&                         /* files need to be */
5439                 ((uptr->flags & UNIT_RO) == 0)) {       /* written on save */
5440                 uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5441                 rewind (uptr->fileref);
5442                 sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5443                 fclose (uptr->fileref);                 /* flush data and state */
5444                 uptr->fileref = sim_fopen (uptr->filename, "rb+");/* reopen r/w */
5445                 }
5446             }
5447         fputc ('\n', sfile);
5448         if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&
5449              (dptr->examine != NULL) &&
5450              ((high = uptr->capac) != 0)) {             /* memory-like unit? */
5451             WRITE_I (high);                             /* [V2.5] write size */
5452             sz = SZ_D (dptr);
5453             if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {
5454                 fclose (sfile);
5455                 return SCPE_MEM;
5456                 }
5457             for (k = 0; k < high; ) {                   /* loop thru mem */
5458                 zeroflg = TRUE;
5459                 for (l = 0; (l < SRBSIZ) && (k < high); l++,
5460                      k = k + (dptr->aincr)) {           /* check for 0 block */
5461                     r = dptr->examine (&val, k, uptr, SIM_SW_REST);
5462                     if (r != SCPE_OK) {
5463                         free (mbuf);
5464                         return r;
5465                         }
5466                     if (val) zeroflg = FALSE;
5467                     SZ_STORE (sz, val, mbuf, l);
5468                     }                                   /* end for l */
5469                 if (zeroflg) {                          /* all zero's? */
5470                     l = -l;                             /* invert block count */
5471                     WRITE_I (l);                        /* write only count */
5472                     }
5473                 else {
5474                     WRITE_I (l);                        /* block count */
5475                     sim_fwrite (mbuf, sz, l, sfile);
5476                     }
5477                 }                                       /* end for k */
5478             free (mbuf);                                /* dealloc buffer */
5479             }                                           /* end if mem */
5480         else {                                          /* no memory */
5481             high = 0;                                   /* write 0 */
5482             WRITE_I (high);
5483             }                                           /* end else mem */
5484         }                                               /* end unit loop */
5485     t = -1;                                             /* end units */
5486     WRITE_I (t);                                        /* write marker */
5487     for (rptr = dptr->registers; (rptr != NULL) &&      /* loop thru regs */
5488          (rptr->name != NULL); rptr++) {
5489         fputs (rptr->name, sfile);                      /* name */
5490         fputc ('\n', sfile);
5491         WRITE_I (rptr->depth);                          /* [V2.10] depth */
5492         for (j = 0; j < rptr->depth; j++) {             /* loop thru values */
5493             val = get_rval (rptr, j);                   /* get value */
5494             WRITE_I (val);                              /* store */
5495             }
5496         }
5497     fputc ('\n', sfile);                                /* end registers */
5498     }
5499 fputc ('\n', sfile);                                    /* end devices */
5500 return (ferror (sfile))? SCPE_IOERR: SCPE_OK;           /* error during save? */
5501 }
5502 
5503 /* Restore command
5504 
5505    re[store] filename           restore state from specified file
5506 */
5507 
5508 t_stat restore_cmd (int32 flag, CONST char *cptr)
5509 {
5510 FILE *rfile;
5511 t_stat r;
5512 char gbuf[4*CBUFSIZE];
5513 
5514 GET_SWITCHES (cptr);                                    /* get switches */
5515 if (*cptr == 0)                                         /* must be more */
5516     return SCPE_2FARG;
5517 gbuf[sizeof(gbuf)-1] = '\0';
5518 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5519 sim_trim_endspc (gbuf);
5520 if ((rfile = sim_fopen (gbuf, "rb")) == NULL)
5521     return SCPE_OPENERR;
5522 r = sim_rest (rfile);
5523 fclose (rfile);
5524 return r;
5525 }
5526 
5527 t_stat sim_rest (FILE *rfile)
5528 {
5529 char buf[CBUFSIZE];
5530 char **attnames = NULL;
5531 UNIT **attunits = NULL;
5532 int32 *attswitches = NULL;
5533 int32 attcnt = 0;
5534 void *mbuf;
5535 int32 j, blkcnt, limit, unitno, time, flg;
5536 uint32 us, depth;
5537 t_addr k, high, old_capac;
5538 t_value val, mask;
5539 t_stat r;
5540 size_t sz;
5541 t_bool v40, v35, v32;
5542 DEVICE *dptr;
5543 UNIT *uptr;
5544 REG *rptr;
5545 struct stat rstat;
5546 t_bool force_restore = ((sim_switches & SWMASK ('F')) != 0);
5547 t_bool dont_detach_attach = ((sim_switches & SWMASK ('D')) != 0);
5548 t_bool suppress_warning = ((sim_switches & SWMASK ('Q')) != 0);
5549 t_bool warned = FALSE;
5550 
5551 sim_switches &= ~(SWMASK ('F') | SWMASK ('D') | SWMASK ('Q'));  /* remove digested switches */
5552 #define READ_S(xx) if (read_line ((xx), sizeof(xx), rfile) == NULL) \
5553     return SCPE_IOERR;
5554 #define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
5555     return SCPE_IOERR;
5556 
5557 fstat (fileno (rfile), &rstat);
5558 READ_S (buf);                                           /* [V2.5+] read version */
5559 v40 = v35 = v32 = FALSE;
5560 if (strcmp (buf, save_ver40) == 0)                      /* version 4.0? */
5561     v40 = v35 = v32 = TRUE;
5562 else if (strcmp (buf, save_ver35) == 0)                 /* version 3.5? */
5563     v35 = v32 = TRUE;
5564 else if (strcmp (buf, save_ver32) == 0)                 /* version 3.2? */
5565     v32 = TRUE;
5566 else if (strcmp (buf, save_ver30) != 0) {               /* version 3.0? */
5567     sim_printf ("Invalid file version: %s\n", buf);
5568     return SCPE_INCOMP;
5569     }
5570 if ((strcmp (buf, save_ver40) != 0) && (!sim_quiet) && (!suppress_warning)) {
5571     sim_printf ("warning - attempting to restore a saved simulator image in %s image format.\n", buf);
5572     warned = TRUE;
5573     }
5574 READ_S (buf);                                           /* read sim name */
5575 if (strcmp (buf, sim_savename)) {                       /* name match? */
5576     sim_printf ("Wrong system type: %s\n", buf);
5577     return SCPE_INCOMP;
5578     }
5579 if (v35) {                                              /* [V3.5+] options */
5580     READ_S (buf);                                       /* integer size */
5581     if (strcmp (buf, sim_si64) != 0) {
5582         sim_printf ("Incompatible integer size, save file = %s\n", buf);
5583         return SCPE_INCOMP;
5584         }
5585     READ_S (buf);                                       /* address size */
5586     if (strcmp (buf, sim_sa64) != 0) {
5587         sim_printf ("Incompatible address size, save file = %s\n", buf);
5588         return SCPE_INCOMP;
5589         }
5590     READ_S (buf);
5591     }
5592 if (v32) {                                              /* [V3.2+] time as string */
5593     READ_S (buf);
5594     sscanf (buf, "%lf", &sim_time);
5595     }
5596 else READ_I (sim_time);                                 /* sim time */
5597 READ_I (sim_rtime);                                     /* [V2.6+] sim rel time */
5598 if (v40) {
5599     READ_S (buf);                                       /* read git commit id */
5600     }
5601 if (!dont_detach_attach)
5602     detach_all (0, 0);                                  /* Detach everything to start from a consistent state */
5603 else {
5604     if (!suppress_warning) {
5605         uint32 i, j;
5606 
5607         for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5608             for (j = 0; j < dptr->numunits; j++) {      /* loop thru units */
5609                 uptr = (dptr->units) + j;
5610                 if (uptr->flags & UNIT_ATT) {           /* attached? */
5611                     sim_printf ("warning - leaving %s attached to '%s'\n", sim_uname (uptr), uptr->filename);
5612                     warned = TRUE;
5613                     }
5614                 }
5615             }
5616         }
5617     }
5618 for ( ;; ) {                                            /* device loop */
5619     READ_S (buf);                                       /* read device name */
5620     if (buf[0] == 0)                                    /* last? */
5621         break;
5622     if ((dptr = find_dev (buf)) == NULL) {              /* locate device */
5623         sim_printf ("Invalid device name: %s\n", buf);
5624         return SCPE_INCOMP;
5625         }
5626     READ_S (buf);                                       /* [V3.0+] logical name */
5627     deassign_device (dptr);                             /* delete old name */
5628     if ((buf[0] != 0) &&
5629         ((r = assign_device (dptr, buf)) != SCPE_OK))
5630         return r;
5631     READ_I (flg);                                       /* [V2.10+] ctlr flags */
5632     if (!v32)
5633         flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) |
5634             (flg & ~DEV_UFMASK_31);                     /* [V3.2+] flags moved */
5635     dptr->flags = (dptr->flags & ~DEV_RFLAGS) |         /* restore ctlr flags */
5636          (flg & DEV_RFLAGS);
5637     for ( ;; ) {                                        /* unit loop */
5638         sim_switches = SIM_SW_REST;                     /* flag rstr, clr RO */
5639         READ_I (unitno);                                /* unit number */
5640         if (unitno < 0)                                 /* end units? */
5641             break;
5642         if ((uint32) unitno >= dptr->numunits) {        /* too big? */
5643             sim_printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno);
5644             return SCPE_INCOMP;
5645             }
5646         READ_I (time);                                  /* event time */
5647         uptr = (dptr->units) + unitno;
5648         sim_cancel (uptr);
5649         if (time > 0)
5650             sim_activate (uptr, time - 1);
5651         READ_I (uptr->u3);                              /* device specific */
5652         READ_I (uptr->u4);
5653         READ_I (uptr->u5);                              /* [V3.0+] more dev spec */
5654         READ_I (uptr->u6);
5655         READ_I (flg);                                   /* [V2.10+] unit flags */
5656         if (v40) {                                      /* [V4.0+] dynflags */
5657             READ_I (uptr->dynflags);
5658             READ_I (uptr->wait);
5659             READ_I (uptr->buf);
5660             }
5661         old_capac = uptr->capac;                        /* save current capacity */
5662         if (v35) {                                      /* [V3.5+] capacity */
5663             READ_I (uptr->capac);
5664             }
5665         if (!v32)
5666             flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) |
5667                 (flg & ~UNIT_UFMASK_31);                /* [V3.2+] flags moved */
5668         uptr->flags = (uptr->flags & ~UNIT_RFLAGS) |
5669             (flg & UNIT_RFLAGS);                        /* restore */
5670         READ_S (buf);                                   /* attached file */
5671         if ((uptr->flags & UNIT_ATT) &&                 /* unit currently attached? */
5672             (!dont_detach_attach)) {
5673             r = scp_detach_unit (dptr, uptr);           /* detach it */
5674             if (r != SCPE_OK)
5675                 return r;
5676             }
5677         if ((buf[0] != '\0') &&                         /* unit to be reattached? */
5678             ((uptr->flags & UNIT_ATTABLE) ||            /*  and unit is attachable */
5679              (dptr->attach != NULL))) {                 /*    or VM attach routine provided? */
5680             uptr->flags = uptr->flags & ~UNIT_DIS;      /* ensure device is enabled */
5681             if (flg & UNIT_RO)                          /* [V2.10+] saved flgs & RO? */
5682                 sim_switches |= SWMASK ('R');           /* RO attach */
5683             /* add unit to list of units to attach after registers are read */
5684             attunits = (UNIT **)realloc (attunits, sizeof (*attunits)*(attcnt+1));
5685             attunits[attcnt] = uptr;
5686             attnames = (char **)realloc (attnames, sizeof (*attnames)*(attcnt+1));
5687             attnames[attcnt] = (char *)malloc(1+strlen(buf));
5688             strcpy (attnames[attcnt], buf);
5689             attswitches = (int32 *)realloc (attswitches, sizeof (*attswitches)*(attcnt+1));
5690             attswitches[attcnt] = sim_switches;
5691             ++attcnt;
5692             }
5693         READ_I (high);                                  /* memory capacity */
5694         if (high > 0) {                                 /* [V2.5+] any memory? */
5695             if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||
5696                  (dptr->deposit == NULL)) {
5697                 sim_printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno);
5698                 return SCPE_INCOMP;
5699                 }
5700             if (high != old_capac) {                    /* size change? */
5701                 uptr->capac = old_capac;                /* temp restore old */
5702                 if ((dptr->flags & DEV_DYNM) &&
5703                     ((dptr->msize == NULL) ||
5704                      (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) {
5705                     sim_printf ("Can't change memory size: %s%d\n",
5706                                 sim_dname (dptr), unitno);
5707                     return SCPE_INCOMP;
5708                     }
5709                 uptr->capac = high;                     /* new memory size */
5710                 sim_printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno);
5711                 fprint_capac (stdout, dptr, uptr);
5712                 if (sim_log)
5713                     fprint_capac (sim_log, dptr, uptr);
5714                 sim_printf ("\n");
5715                 }
5716             sz = SZ_D (dptr);                           /* allocate buffer */
5717             if ((mbuf = calloc (SRBSIZ, sz)) == NULL)
5718                 return SCPE_MEM;
5719             for (k = 0; k < high; ) {                   /* loop thru mem */
5720                 if (sim_fread (&blkcnt, sizeof (blkcnt), 1, rfile) == 0) {/* block count */
5721                     free (mbuf);
5722                     return SCPE_IOERR;
5723                     }
5724                 if (blkcnt < 0)                         /* compressed? */
5725                     limit = -blkcnt;
5726                 else limit = (int32)sim_fread (mbuf, sz, blkcnt, rfile);
5727                 if (limit <= 0) {                       /* invalid or err? */
5728                     free (mbuf);
5729                     return SCPE_IOERR;
5730                     }
5731                 for (j = 0; j < limit; j++, k = k + (dptr->aincr)) {
5732                     if (blkcnt < 0)                     /* compressed? */
5733                         val = 0;
5734                     else SZ_LOAD (sz, val, mbuf, j);    /* saved value */
5735                     r = dptr->deposit (val, k, uptr, SIM_SW_REST);
5736                     if (r != SCPE_OK) {
5737                         free (mbuf);
5738                         return r;
5739                         }
5740                     }                                   /* end for j */
5741                 }                                       /* end for k */
5742             free (mbuf);                                /* dealloc buffer */
5743             }                                           /* end if high */
5744         }                                               /* end unit loop */
5745     for ( ;; ) {                                        /* register loop */
5746         READ_S (buf);                                   /* read reg name */
5747         if (buf[0] == 0)                                /* last? */
5748             break;
5749         READ_I (depth);                                 /* [V2.10+] depth */
5750         if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {
5751             sim_printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf);
5752             for (us = 0; us < depth; us++) {            /* skip values */
5753                 READ_I (val);
5754                 }
5755             continue;
5756             }
5757         if (depth != rptr->depth) {                      /* [V2.10+] mismatch? */
5758             sim_printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n",
5759                         sim_dname (dptr), buf, depth, rptr->depth);
5760             }
5761         mask = width_mask[rptr->width];                 /* get mask */
5762         for (us = 0; us < depth; us++) {                /* loop thru values */
5763             READ_I (val);                               /* read value */
5764             if (val > mask) {                           /* value ok? */
5765                 sim_printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf);
5766                 }
5767             else if (us < rptr->depth)                  /* in range? */
5768                 put_rval (rptr, us, val);
5769             }
5770         }                                               /* end register loop */
5771     }                                                   /* end device loop */
5772 /* Now that all of the register state has been imported, we can attach
5773    units which were originally attached.  Some of these attach operations
5774    may depend on the state of the device (in registers) to work correctly */
5775 for (j=0, r = SCPE_OK; j<attcnt; j++) {
5776     if ((r == SCPE_OK) && (!dont_detach_attach)) {
5777         struct stat fstat;
5778         t_addr saved_pos;
5779 
5780         dptr = find_dev_from_unit (attunits[j]);
5781         if ((!force_restore) &&
5782             (!stat(attnames[j], &fstat)))
5783             if (fstat.st_mtime > rstat.st_mtime + 30) {
5784                 r = SCPE_INCOMP;
5785                 sim_printf ("Error Attaching %s to %s - the restore state is %d seconds older than the attach file\n", sim_dname (dptr), attnames[j], (int)(fstat.st_mtime - rstat.st_mtime));
5786                 sim_printf ("restore with the -F switch to override this sanity check\n");
5787                 continue;
5788                 }
5789         saved_pos = attunits[j]->pos;
5790         sim_switches = attswitches[j];
5791         r = scp_attach_unit (dptr, attunits[j], attnames[j]);/* reattach unit */
5792         attunits[j]->pos = saved_pos;
5793         if (r != SCPE_OK)
5794             sim_printf ("Error Attaching %s to %s\n", sim_dname (dptr), attnames[j]);
5795         }
5796     else {
5797         if ((r == SCPE_OK) && (dont_detach_attach)) {
5798             if ((!suppress_warning) &&
5799                 ((!attunits[j]->filename) || (strcmp (attunits[j]->filename, attnames[j]) != 0))) {
5800                 warned = TRUE;
5801                 sim_printf ("warning - %s was attached to '%s'", sim_uname (attunits[j]), attnames[j]);
5802                 if (attunits[j]->filename)
5803                     sim_printf (", now attached to '%s'\n", attunits[j]->filename);
5804                 else
5805                     sim_printf (", now unattached\n");
5806                 }
5807             }
5808         }
5809     free (attnames[j]);
5810     }
5811 free (attnames);
5812 free (attunits);
5813 free (attswitches);
5814 if (warned)
5815     sim_printf ("restore with the -Q switch to suppress warning messages\n");
5816 return r;
5817 }
5818 
5819 /* Run, go, boot, cont, step, next commands
5820 
5821    ru[n] [new PC]       reset and start simulation
5822    go [new PC]          start simulation
5823    co[nt]               start simulation
5824    s[tep] [step limit]  start simulation for 'limit' instructions
5825    next                 start simulation for 1 instruction
5826                         stepping over subroutine calls
5827    b[oot] device        bootstrap from device and start simulation
5828 
5829    switches:
5830     -Q                  quiet return status
5831     -T                  (only for step), causes the step limit to
5832                         be a number of microseconds to run for
5833 */
5834 
5835 t_stat run_cmd (int32 flag, CONST char *cptr)
5836 {
5837 char gbuf[CBUFSIZE] = "";
5838 CONST char *tptr;
5839 uint32 i, j;
5840 int32 sim_next = 0;
5841 int32 unitno;
5842 t_value pcv, orig_pcv;
5843 t_stat r;
5844 DEVICE *dptr;
5845 UNIT *uptr;
5846 
5847 GET_SWITCHES (cptr);                                    /* get switches */
5848 sim_step = 0;
5849 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
5850     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
5851     if (*cptr != 0) {                                   /* argument? */
5852         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5853         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
5854             if (sim_dflt_dev && sim_vm_parse_addr)                      /* address parser? */
5855                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
5856             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
5857             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
5858                 (pcv > width_mask[sim_PC->width]))
5859                 return SCPE_ARG;
5860             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
5861             }
5862         }
5863     if ((flag == RU_RUN) &&                             /* run? */
5864         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
5865         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
5866         return r;
5867         }
5868     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) {  /* should be end */
5869         int32 saved_switches = sim_switches;
5870 
5871         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5872             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
5873         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5874             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
5875                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
5876         sim_switches = 0;
5877         GET_SWITCHES (cptr);
5878         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
5879             r = expect_cmd (1, cptr);
5880             if (r != SCPE_OK)
5881                 return r;
5882             }
5883         else {                                          /* BREAK UNTIL condition */
5884             if (sim_switches == 0)
5885                 sim_switches = sim_brk_dflt;
5886             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
5887             sim_brk_types |= BRK_TYP_TEMP;
5888             r = ssh_break (NULL, cptr, SSH_ST);
5889             if (r != SCPE_OK)
5890                 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
5891             }
5892         sim_switches = saved_switches;
5893         }
5894     }
5895 
5896 else if ((flag == RU_STEP) ||
5897          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
5898     static t_bool not_implemented_message = FALSE;
5899 
5900     if ((!not_implemented_message) && (flag == RU_NEXT)) {
5901         sim_printf ("This simulator does not have subroutine call detection.\nPerforming a STEP instead\n");
5902         not_implemented_message = TRUE;
5903         flag = RU_STEP;
5904         }
5905     if (*cptr != 0) {                                   /* argument? */
5906         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5907         if (*cptr != 0)                                 /* should be end */
5908             return SCPE_2MARG;
5909         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5910         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
5911             return SCPE_ARG;
5912         }
5913     else sim_step = 1;
5914     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
5915         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
5916     }
5917 else if (flag == RU_NEXT) {                             /* next */
5918     t_addr *addrs;
5919 
5920     if (*cptr != 0) {                                   /* argument? */
5921         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5922         if (*cptr != 0)                                 /* should be end */
5923             return SCPE_2MARG;
5924         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5925         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
5926             return SCPE_ARG;
5927         }
5928     else sim_next = 1;
5929     if (sim_vm_is_subroutine_call(&addrs)) {
5930         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
5931         for (i=0; addrs[i]; i++)
5932             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
5933         }
5934     else
5935         sim_step = 1;
5936     }
5937 else if (flag == RU_BOOT) {                             /* boot */
5938     if (*cptr == 0)                                     /* must be more */
5939         return SCPE_2FARG;
5940     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
5941     if (*cptr != 0)                                     /* should be end */
5942         return SCPE_2MARG;
5943     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
5944     if (dptr == NULL)                                   /* found dev? */
5945         return SCPE_NXDEV;
5946     if (uptr == NULL)                                   /* valid unit? */
5947         return SCPE_NXUN;
5948     if (dptr->boot == NULL)                             /* can it boot? */
5949         return SCPE_NOFNC;
5950     if (uptr->flags & UNIT_DIS)                         /* disabled? */
5951         return SCPE_UDIS;
5952     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
5953         !(uptr->flags & UNIT_ATT))
5954         return SCPE_UNATT;
5955     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
5956     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
5957         return r;
5958     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
5959         return r;
5960     }
5961 
5962 else
5963     if (flag != RU_CONT)                                /* must be cont */
5964         return SCPE_IERR;
5965     else                                                /* CONTINUE command */
5966         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
5967             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
5968 
5969 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
5970     return SCPE_OK;
5971 
5972 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
5973     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
5974         uptr = dptr->units + j;
5975         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
5976             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
5977         }
5978     }
5979 stop_cpu = 0;
5980 sim_is_running = 1;                                     /* flag running */
5981 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
5982     sim_is_running = 0;                                 /* flag idle */
5983     sim_ttcmd ();
5984     return SCPE_TTYERR;
5985     }
5986 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
5987     sim_is_running = 0;                                 /* flag idle */
5988     sim_ttcmd ();
5989     return r;
5990     }
5991 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
5992     sim_is_running = 0;                                 /* flag idle */
5993     sim_ttcmd ();
5994     return SCPE_SIGERR;
5995     }
5996 #ifdef SIGHUP
5997 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
5998     sim_is_running = 0;                                 /* flag idle */
5999     sim_ttcmd ();
6000     return SCPE_SIGERR;
6001     }
6002 #endif
6003 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
6004     sim_is_running = 0;                                 /* flag idle */
6005     sim_ttcmd ();
6006     return SCPE_SIGERR;
6007     }
6008 if (sim_step)                                           /* set step timer */
6009     sim_activate (&sim_step_unit, sim_step);
6010 fflush(stdout);                                         /* flush stdout */
6011 if (sim_log)                                            /* flush log if enabled */
6012     fflush (sim_log);
6013 sim_rtcn_init_all ();                                   /* re-init clocks */
6014 sim_start_timer_services ();                            /* enable wall clock timing */
6015 
6016 do {
6017     t_addr *addrs;
6018 
6019     while (1) {
6020         r = sim_instr();
6021         if (r != SCPE_REMOTE)
6022             break;
6023         sim_remote_process_command ();                  /* Process the command and resume processing */
6024         }
6025     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
6026         (--sim_next <=0))
6027         break;
6028     if (sim_step == 0) {                                /* doing a NEXT? */
6029         t_addr val;
6030         BRKTAB *bp;
6031 
6032         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
6033             break;
6034         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
6035             val = (t_addr)(*sim_vm_pc_value)();
6036         else
6037             val = (t_addr)get_rval (sim_PC, 0);
6038         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6039             break;
6040         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
6041         }
6042     else {
6043         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
6044             break;
6045         }
6046     /* setup another next/step */
6047     sim_step = 0;
6048     if (sim_vm_is_subroutine_call(&addrs)) {
6049         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6050         for (i=0; addrs[i]; i++)
6051             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6052         }
6053     else
6054         sim_step = 1;
6055     if (sim_step)                                       /* set step timer */
6056         sim_activate (&sim_step_unit, sim_step);
6057     } while (1);
6058 
6059 sim_is_running = 0;                                     /* flag idle */
6060 sim_stop_timer_services ();                             /* disable wall clock timing */
6061 sim_ttcmd ();                                           /* restore console */
6062 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
6063 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
6064 #ifdef SIGHUP
6065 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
6066 #endif
6067 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
6068 if (sim_log)                                            /* flush console log */
6069     fflush (sim_log);
6070 if (sim_deb)                                            /* flush debug log */
6071     sim_debug_flush ();
6072 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
6073     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
6074         uptr = dptr->units + j;
6075         if (uptr->flags & UNIT_ATT) {                   /* attached, */
6076             if (uptr->io_flush)                         /* unit specific flush routine */
6077                 uptr->io_flush (uptr);                  /* call it */
6078             else {
6079                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
6080                     (uptr->fileref) &&                  /* real file, */
6081                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
6082                     !(uptr->flags & UNIT_RO))           /* not read only? */
6083                     fflush (uptr->fileref);
6084                 }
6085             }
6086         }
6087     }
6088 sim_cancel (&sim_step_unit);                            /* cancel step timer */
6089 UPDATE_SIM_TIME;                                        /* update sim time */
6090 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6091 }
6092 
6093 /* run command message handler */
6094 
6095 void
6096 run_cmd_message (const char *unechoed_cmdline, t_stat r)
6097 {
6098 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6099     sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
6100 fprint_stopped (stdout, r);                         /* print msg */
6101 if (sim_log && (sim_log != stdout))                 /* log if enabled */
6102     fprint_stopped (sim_log, r);
6103 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6104     fprint_stopped (sim_deb, r);
6105 }
6106 
6107 /* Common setup for RUN or BOOT */
6108 
6109 t_stat sim_run_boot_prep (int32 flag)
6110 {
6111 UNIT *uptr;
6112 t_stat r;
6113 
6114 sim_interval = 0;                                       /* reset queue */
6115 sim_time = sim_rtime = 0;
6116 noqueue_time = 0;
6117 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6118     sim_clock_queue = uptr->next;
6119     uptr->next = NULL;
6120     }
6121 r = reset_all (0);
6122 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6123     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6124         sim_printf ("Resetting all devices...  This may not have been your intention.\n");
6125         sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
6126         }
6127     run_cmd_did_reset = TRUE;
6128     }
6129 return r;
6130 }
6131 
6132 /* Print stopped message
6133  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6134  * call the indicated routine to print additional information after the message
6135  * and before the PC value is printed.  If the routine returns FALSE, skip
6136  * printing the PC and its related instruction.
6137  */
6138 
6139 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
6140 {
6141 int32 i;
6142 t_stat r = 0;
6143 t_addr k;
6144 t_value pcval;
6145 
6146 fputc ('\n', st);                                       /* start on a new line */
6147 
6148 if (v >= SCPE_BASE)                                     /* SCP error? */
6149     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
6150 else {                                                  /* VM error */
6151     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6152 
6153     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6154         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6155         return;                                         /*     we're done */
6156     }
6157 
6158 fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6159 
6160 pcval = get_rval (pc, 0);
6161 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6162     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6163 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6164     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6165 if ((dptr != NULL) && (dptr->examine != NULL)) {
6166     for (i = 0; i < sim_emax; i++)
6167         sim_eval[i] = 0;
6168     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6169         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6170             break;
6171         }
6172     if ((r == SCPE_OK) || (i > 0)) {
6173         fprintf (st, " (");
6174         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6175             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6176         fprintf (st, ")");
6177         }
6178     }
6179 fprintf (st, "\n");
6180 return;
6181 }
6182 
6183 void fprint_stopped (FILE *st, t_stat v)
6184 {
6185 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6186 return;
6187 }
6188 
6189 /* Unit service for step timeout, originally scheduled by STEP n command
6190    Return step timeout SCP code, will cause simulation to stop */
6191 
6192 t_stat step_svc (UNIT *uptr)
6193 {
6194 return SCPE_STEP;
6195 }
6196 
6197 /* Unit service to facilitate expect matching to stop simulation.
6198    Return expect SCP code, will cause simulation to stop */
6199 
6200 t_stat expect_svc (UNIT *uptr)
6201 {
6202 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6203 }
6204 
6205 /* Signal handler for ^C signal - set stop simulation flag */
6206 
6207 void int_handler (int sig)
6208 {
6209 stop_cpu = 1;
6210 return;
6211 }
6212 
6213 /* Examine/deposit commands
6214 
6215    ex[amine] [modifiers] list           examine
6216    de[posit] [modifiers] list val       deposit
6217    ie[xamine] [modifiers] list          interactive examine
6218    id[eposit] [modifiers] list          interactive deposit
6219 
6220    modifiers
6221         @filename                       output file
6222         -letter(s)                      switches
6223         devname'n                       device name and unit number
6224         [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification
6225 
6226    list                                 list of addresses and registers
6227         addr[:addr|-addr]               address range
6228         ALL                             all addresses
6229         register[:register|-register]   register range
6230         register[index]                 register array element
6231         register[start:end]             register array range
6232         STATE                           all registers
6233 */
6234 
6235 t_stat exdep_cmd (int32 flag, CONST char *cptr)
6236 {
6237 char gbuf[CBUFSIZE];
6238 CONST char *gptr;
6239 CONST char *tptr = NULL;
6240 int32 opt;
6241 t_addr low, high;
6242 t_stat reason;
6243 DEVICE *tdptr;
6244 REG *lowr, *highr;
6245 FILE *ofile;
6246 
6247 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6248 if (flag == EX_E)                                       /* extra for EX */
6249     opt = opt | CMD_OPT_OF;
6250 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6251 if (!cptr)                                              /* error? */
6252     return reason;
6253 if (*cptr == 0)                                         /* must be more */
6254     return SCPE_2FARG;
6255 if (sim_dfunit == NULL)                                 /* got a unit? */
6256     return SCPE_NXUN;
6257 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6258 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6259 
6260     return SCPE_2FARG;
6261 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6262 
6263 for (gptr = gbuf, reason = SCPE_OK;
6264     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6265     tdptr = sim_dfdev;                                  /* working dptr */
6266     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6267         tptr = gptr + strlen ("STATE");
6268         if (*tptr && (*tptr++ != ','))
6269             return SCPE_ARG;
6270         if ((lowr = sim_dfdev->registers) == NULL)
6271             return SCPE_NXREG;
6272         for (highr = lowr; highr->name != NULL; highr++) ;
6273         sim_switches = sim_switches | SIM_SW_HIDE;
6274         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6275             lowr, --highr, 0, 0);
6276         continue;
6277         }
6278 
6279     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6280         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6281         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6282         low = high = 0;
6283         if ((*tptr == '-') || (*tptr == ':')) {
6284             highr = find_reg (tptr + 1, &tptr, tdptr);
6285             if (highr == NULL)
6286                 return SCPE_NXREG;
6287             }
6288         else {
6289             highr = lowr;
6290             if (*tptr == '[') {
6291                 if (lowr->depth <= 1)
6292                     return SCPE_ARG;
6293                 tptr = get_range (NULL, tptr + 1, &low, &high,
6294                     10, lowr->depth - 1, ']');
6295                 if (tptr == NULL)
6296                     return SCPE_ARG;
6297                 }
6298             }
6299         if (*tptr && (*tptr++ != ','))
6300             return SCPE_ARG;
6301         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6302             lowr, highr, (uint32) low, (uint32) high);
6303         continue;
6304         }
6305 
6306     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6307         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6308         sim_dfunit->capac - sim_dfdev->aincr), 0);
6309     if (tptr == NULL)
6310         return SCPE_ARG;
6311     if (*tptr && (*tptr++ != ','))
6312         return SCPE_ARG;
6313     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6314         sim_dfdev, sim_dfunit);
6315     }                                                   /* end for */
6316 if (sim_ofile)                                          /* close output file */
6317     fclose (sim_ofile);
6318 return reason;
6319 }
6320 
6321 /* Loop controllers for examine/deposit
6322 
6323    exdep_reg_loop       examine/deposit range of registers
6324    exdep_addr_loop      examine/deposit range of addresses
6325 */
6326 
6327 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
6328     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6329 {
6330 t_stat reason;
6331 uint32 idx, val_start=lows;
6332 t_value val, last_val;
6333 REG *rptr;
6334 
6335 if ((lowr == NULL) || (highr == NULL))
6336     return SCPE_IERR;
6337 if (lowr > highr)
6338     return SCPE_ARG;
6339 for (rptr = lowr; rptr <= highr; rptr++) {
6340     if ((sim_switches & SIM_SW_HIDE) &&
6341         (rptr->flags & REG_HIDDEN))
6342         continue;
6343     val = last_val = 0;
6344     for (idx = lows; idx <= highs; idx++) {
6345         if (idx >= rptr->depth)
6346             return SCPE_SUB;
6347         val = get_rval (rptr, idx);
6348         if (schptr && !test_search (&val, schptr))
6349             continue;
6350         if (flag == EX_E) {
6351             if ((idx > lows) && (val == last_val))
6352                 continue;
6353             if (idx > val_start+1) {
6354                 if (idx-1 == val_start+1) {
6355                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6356                     if (reason != SCPE_OK)
6357                         return reason;
6358                     if (sim_log && (ofile == stdout))
6359                         ex_reg (sim_log, val, flag, rptr, idx-1);
6360                     }
6361                 else {
6362                     if (val_start+1 != idx-1) {
6363                         Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6364                         if (sim_log && (ofile == stdout))
6365                             Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6366                         }
6367                     else {
6368                         Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6369                         if (sim_log && (ofile == stdout))
6370                             Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6371                         }
6372                     }
6373                 }
6374             sim_last_val = last_val = val;
6375             val_start = idx;
6376             reason = ex_reg (ofile, val, flag, rptr, idx);
6377             if (reason != SCPE_OK)
6378                 return reason;
6379             if (sim_log && (ofile == stdout))
6380                 ex_reg (sim_log, val, flag, rptr, idx);
6381             }
6382         if (flag != EX_E) {
6383             reason = dep_reg (flag, cptr, rptr, idx);
6384             if (reason != SCPE_OK)
6385                 return reason;
6386             }
6387         }
6388     if ((flag == EX_E) && (val_start != highs)) {
6389         if (highs == val_start+1) {
6390             reason = ex_reg (ofile, val, flag, rptr, highs);
6391             if (reason != SCPE_OK)
6392                 return reason;
6393             if (sim_log && (ofile == stdout))
6394                 ex_reg (sim_log, val, flag, rptr, highs);
6395             }
6396         else {
6397             if (val_start+1 != highs) {
6398                 Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6399                 if (sim_log && (ofile == stdout))
6400                     Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6401                 }
6402             else {
6403                 Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6404                 if (sim_log && (ofile == stdout))
6405                     Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6406                 }
6407             }
6408         }
6409     }
6410 return SCPE_OK;
6411 }
6412 
6413 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
6414     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6415 {
6416 t_addr i, mask;
6417 t_stat reason;
6418 
6419 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6420     return SCPE_UDIS;
6421 mask = (t_addr) width_mask[dptr->awidth];
6422 if ((low > mask) || (high > mask) || (low > high))
6423     return SCPE_ARG;
6424 for (i = low; i <= high; ) {                            /* all paths must incr!! */
6425     reason = get_aval (i, dptr, uptr);                  /* get data */
6426     if (reason != SCPE_OK)                              /* return if error */
6427         return reason;
6428     if (schptr && !test_search (sim_eval, schptr))
6429         i = i + dptr->aincr;                            /* sch fails, incr */
6430     else {                                              /* no sch or success */
6431         if (flag != EX_D) {                             /* ex, ie, or id? */
6432             reason = ex_addr (ofile, flag, i, dptr, uptr);
6433             if (reason > SCPE_OK)
6434                 return reason;
6435             if (sim_log && (ofile == stdout))
6436                 ex_addr (sim_log, flag, i, dptr, uptr);
6437             }
6438         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
6439         if (flag != EX_E) {                             /* ie, id, or d? */
6440             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6441             if (reason > SCPE_OK)
6442                 return reason;
6443             }
6444         i = i + (1 - reason);                           /* incr */
6445         }
6446     }
6447 return SCPE_OK;
6448 }
6449 
6450 /* Examine register routine
6451 
6452    Inputs:
6453         ofile   =       output stream
6454         val     =       current register value
6455         flag    =       type of ex/mod command (ex, iex, idep)
6456         rptr    =       pointer to register descriptor
6457         idx     =       index
6458    Outputs:
6459         return  =       error status
6460 */
6461 
6462 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
6463 {
6464 int32 rdx;
6465 
6466 if (rptr == NULL)
6467     return SCPE_IERR;
6468 if (rptr->depth > 1)
6469     Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6470 else Fprintf (ofile, "%s:\t", rptr->name);
6471 if (!(flag & EX_E))
6472     return SCPE_OK;
6473 GET_RADIX (rdx, rptr->radix);
6474 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6475     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6476 else if (!(rptr->flags & REG_VMFLAGS) ||
6477     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6478                  NULL, sim_switches | SIM_SW_REG) > 0)) {
6479         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6480         if (rptr->fields) {
6481             Fprintf (ofile, "\t");
6482             fprint_fields (ofile, val, val, rptr->fields);
6483             }
6484         }
6485 if (flag & EX_I)
6486     Fprintf (ofile, "\t");
6487 else Fprintf (ofile, "\n");
6488 return SCPE_OK;
6489 }
6490 
6491 /* Get register value
6492 
6493    Inputs:
6494         rptr    =       pointer to register descriptor
6495         idx     =       index
6496    Outputs:
6497         return  =       register value
6498 */
6499 
6500 t_value get_rval (REG *rptr, uint32 idx)
6501 {
6502 size_t sz;
6503 t_value val;
6504 uint32 *ptr;
6505 
6506 sz = SZ_R (rptr);
6507 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6508     idx = idx + rptr->qptr;
6509     if (idx >= rptr->depth) idx = idx - rptr->depth;
6510     }
6511 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6512     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6513     if (sz <= sizeof (uint32))
6514         val = *ptr;
6515     else val = *((t_uint64 *) ptr);
6516     }
6517 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6518     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6519     if (sz <= sizeof (uint32))
6520         val = *ptr;
6521     else val = *((t_uint64 *) ptr);
6522     }
6523 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6524     (sz == sizeof (uint8)))
6525     val = *(((uint8 *) rptr->loc) + idx);
6526 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6527     (sz == sizeof (uint16)))
6528     val = *(((uint16 *) rptr->loc) + idx);
6529 else if (sz <= sizeof (uint32))
6530      val = *(((uint32 *) rptr->loc) + idx);
6531 else val = *(((t_uint64 *) rptr->loc) + idx);
6532 val = (val >> rptr->offset) & width_mask[rptr->width];
6533 return val;
6534 }
6535 
6536 /* Deposit register routine
6537 
6538    Inputs:
6539         flag    =       type of deposit (normal/interactive)
6540         cptr    =       pointer to input string
6541         rptr    =       pointer to register descriptor
6542         idx     =       index
6543    Outputs:
6544         return  =       error status
6545 */
6546 
6547 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
6548 {
6549 t_stat r;
6550 t_value val, mask;
6551 int32 rdx;
6552 CONST char *tptr;
6553 char gbuf[CBUFSIZE];
6554 
6555 if ((cptr == NULL) || (rptr == NULL))
6556     return SCPE_IERR;
6557 if (rptr->flags & REG_RO)
6558     return SCPE_RO;
6559 if (flag & EX_I) {
6560     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6561     if (sim_log)
6562         fprintf (sim_log, "%s\n", cptr? cptr: "");
6563     if (cptr == NULL)                                   /* force exit */
6564         return 1;
6565     if (*cptr == 0)                                     /* success */
6566         return SCPE_OK;
6567     }
6568 mask = width_mask[rptr->width];
6569 GET_RADIX (rdx, rptr->radix);
6570 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
6571     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
6572     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
6573         return SCPE_ARG;
6574     }
6575 else
6576     if (!(rptr->flags & REG_VMFLAGS) ||                 /* dont use sym? */
6577         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
6578                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
6579     val = get_uint (cptr, rdx, mask, &r);
6580     if (r != SCPE_OK)
6581         return SCPE_ARG;
6582     }
6583 if ((rptr->flags & REG_NZ) && (val == 0))
6584     return SCPE_ARG;
6585 put_rval (rptr, idx, val);
6586 return SCPE_OK;
6587 }
6588 
6589 /* Put register value
6590 
6591    Inputs:
6592         rptr    =       pointer to register descriptor
6593         idx     =       index
6594         val     =       new value
6595         mask    =       mask
6596    Outputs:
6597         none
6598 */
6599 
6600 void put_rval (REG *rptr, uint32 idx, t_value val)
6601 {
6602 size_t sz;
6603 t_value mask;
6604 uint32 *ptr;
6605 
6606 #define PUT_RVAL(sz,rp,id,v,m) \
6607     *(((sz *) rp->loc) + id) = \
6608         (sz)((*(((sz *) rp->loc) + id) & \
6609             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
6610 
6611 if (rptr == sim_PC)
6612     sim_brk_npc (0);
6613 sz = SZ_R (rptr);
6614 mask = width_mask[rptr->width];
6615 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6616     idx = idx + rptr->qptr;
6617     if (idx >= rptr->depth)
6618         idx = idx - rptr->depth;
6619     }
6620 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6621     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6622     if (sz <= sizeof (uint32))
6623         *ptr = (*ptr &
6624         ~(((uint32) mask) << rptr->offset)) |
6625         (((uint32) val) << rptr->offset);
6626     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6627         & ~(mask << rptr->offset)) | (val << rptr->offset);
6628     }
6629 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6630     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6631     if (sz <= sizeof (uint32))
6632         *((uint32 *) ptr) = (*((uint32 *) ptr) &
6633         ~(((uint32) mask) << rptr->offset)) |
6634         (((uint32) val) << rptr->offset);
6635     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6636         & ~(mask << rptr->offset)) | (val << rptr->offset);
6637     }
6638 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6639     (sz == sizeof (uint8)))
6640     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
6641 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6642     (sz == sizeof (uint16)))
6643     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
6644 else if (sz <= sizeof (uint32))
6645     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
6646 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
6647 return;
6648 }
6649 
6650 /* Examine address routine
6651 
6652    Inputs: (sim_eval is an implicit argument)
6653         ofile   =       output stream
6654         flag    =       type of ex/mod command (ex, iex, idep)
6655         addr    =       address to examine
6656         dptr    =       pointer to device
6657         uptr    =       pointer to unit
6658    Outputs:
6659         return  =       if > 0, error status
6660                         if <= 0,-number of extra addr units retired
6661 */
6662 
6663 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
6664 {
6665 t_stat reason;
6666 int32 rdx;
6667 
6668 if (sim_vm_fprint_addr)
6669     sim_vm_fprint_addr (ofile, dptr, addr);
6670 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
6671 Fprintf (ofile, ":\t");
6672 if (!(flag & EX_E))
6673     return (1 - dptr->aincr);
6674 
6675 GET_RADIX (rdx, dptr->dradix);
6676 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
6677     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
6678     reason = 1 - dptr->aincr;
6679     }
6680 if (flag & EX_I)
6681     Fprintf (ofile, "\t");
6682 else Fprintf (ofile, "\n");
6683 return reason;
6684 }
6685 
6686 /* Get address routine
6687 
6688    Inputs:
6689         flag    =       type of ex/mod command (ex, iex, idep)
6690         addr    =       address to examine
6691         dptr    =       pointer to device
6692         uptr    =       pointer to unit
6693    Outputs: (sim_eval is an implicit output)
6694         return  =       error status
6695 */
6696 
6697 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
6698 {
6699 int32 i;
6700 t_value mask;
6701 t_addr j, loc;
6702 size_t sz;
6703 t_stat reason = SCPE_OK;
6704 
6705 if ((dptr == NULL) || (uptr == NULL))
6706     return SCPE_IERR;
6707 mask = width_mask[dptr->dwidth];
6708 for (i = 0; i < sim_emax; i++)
6709     sim_eval[i] = 0;
6710 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
6711     if (dptr->examine != NULL) {
6712         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
6713         if (reason != SCPE_OK)
6714             break;
6715         }
6716     else {
6717         if (!(uptr->flags & UNIT_ATT))
6718             return SCPE_UNATT;
6719         if (uptr->dynflags & UNIT_NO_FIO)
6720             return SCPE_NOFNC;
6721         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
6722             reason = SCPE_NXM;
6723             break;
6724             }
6725         sz = SZ_D (dptr);
6726         loc = j / dptr->aincr;
6727         if (uptr->flags & UNIT_BUF) {
6728             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
6729             }
6730         else {
6731             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6732             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
6733             if ((feof (uptr->fileref)) &&
6734                !(uptr->flags & UNIT_FIX)) {
6735                 reason = SCPE_EOF;
6736                 break;
6737                 }
6738             else if (ferror (uptr->fileref)) {
6739                 clearerr (uptr->fileref);
6740                 reason = SCPE_IOERR;
6741                 break;
6742                 }
6743             }
6744         }
6745     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
6746     }
6747 if ((reason != SCPE_OK) && (i == 0))
6748     return reason;
6749 return SCPE_OK;
6750 }
6751 
6752 /* Deposit address routine
6753 
6754    Inputs:
6755         flag    =       type of deposit (normal/interactive)
6756         cptr    =       pointer to input string
6757         addr    =       address to examine
6758         dptr    =       pointer to device
6759         uptr    =       pointer to unit
6760         dfltinc =       value to return on cr input
6761    Outputs:
6762         return  =       if > 0, error status
6763                         if <= 0, -number of extra address units retired
6764 */
6765 
6766 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
6767     UNIT *uptr, int32 dfltinc)
6768 {
6769 int32 i, count, rdx;
6770 t_addr j, loc;
6771 t_stat r, reason;
6772 t_value mask;
6773 size_t sz;
6774 char gbuf[CBUFSIZE];
6775 
6776 if (dptr == NULL)
6777     return SCPE_IERR;
6778 if (flag & EX_I) {
6779     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6780     if (sim_log)
6781         fprintf (sim_log, "%s\n", cptr? cptr: "");
6782     if (cptr == NULL)                                   /* force exit */
6783         return 1;
6784     if (*cptr == 0)                                     /* success */
6785         return dfltinc;
6786     }
6787 if (uptr->flags & UNIT_RO)                              /* read only? */
6788     return SCPE_RO;
6789 mask = width_mask[dptr->dwidth];
6790 
6791 GET_RADIX (rdx, dptr->dradix);
6792 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
6793     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
6794     if (reason != SCPE_OK)
6795         return reason;
6796     reason = dfltinc;
6797     }
6798 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
6799 
6800 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
6801     sim_eval[i] = sim_eval[i] & mask;
6802     if (dptr->deposit != NULL) {
6803         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
6804         if (r != SCPE_OK)
6805             return r;
6806         }
6807     else {
6808         if (!(uptr->flags & UNIT_ATT))
6809             return SCPE_UNATT;
6810         if (uptr->dynflags & UNIT_NO_FIO)
6811             return SCPE_NOFNC;
6812         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
6813             return SCPE_NXM;
6814         sz = SZ_D (dptr);
6815         loc = j / dptr->aincr;
6816         if (uptr->flags & UNIT_BUF) {
6817             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
6818             if (loc >= uptr->hwmark)
6819                 uptr->hwmark = (uint32) loc + 1;
6820             }
6821         else {
6822             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6823             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
6824             if (ferror (uptr->fileref)) {
6825                 clearerr (uptr->fileref);
6826                 return SCPE_IOERR;
6827                 }
6828             }
6829         }
6830     }
6831 return reason;
6832 }
6833 
6834 /* Evaluate command */
6835 
6836 t_stat eval_cmd (int32 flg, CONST char *cptr)
6837 {
6838 if (!sim_dflt_dev)
6839   return SCPE_ARG;
6840 DEVICE *dptr = sim_dflt_dev;
6841 int32 i, rdx, a, lim;
6842 t_stat r;
6843 
6844 GET_SWITCHES (cptr);
6845 GET_RADIX (rdx, dptr->dradix);
6846 for (i = 0; i < sim_emax; i++)
6847 sim_eval[i] = 0;
6848 if (*cptr == 0)
6849     return SCPE_2FARG;
6850 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
6851     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
6852     if (r != SCPE_OK)
6853         return r;
6854     }
6855 lim = 1 - r;
6856 for (i = a = 0; a < lim; ) {
6857     sim_printf ("%d:\t", a);
6858     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6859         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6860     if (sim_log) {
6861         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6862             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6863         }
6864     sim_printf ("\n");
6865     if (r < 0)
6866         a = a + 1 - r;
6867     else a = a + dptr->aincr;
6868     i = a / dptr->aincr;
6869     }
6870 return SCPE_OK;
6871 }
6872 
6873 /* String processing routines
6874 
6875    read_line            read line
6876 
6877    Inputs:
6878         cptr    =       pointer to buffer
6879         size    =       maximum size
6880         stream  =       pointer to input stream
6881    Outputs:
6882         optr    =       pointer to first non-blank character
6883                         NULL if EOF
6884 */
6885 
6886 char *read_line (char *cptr, int32 size, FILE *stream)
6887 {
6888 return read_line_p (NULL, cptr, size, stream);
6889 }
6890 
6891 /* read_line_p          read line with prompt
6892 
6893    Inputs:
6894         prompt  =       pointer to prompt string
6895         cptr    =       pointer to buffer
6896         size    =       maximum size
6897         stream  =       pointer to input stream
6898    Outputs:
6899         optr    =       pointer to first non-blank character
6900                         NULL if EOF
6901 */
6902 
6903 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
6904 {
6905 char *tptr;
6906 
6907 if (prompt) {                                           /* interactive? */
6908 #ifdef HAVE_LINEHISTORY
6909         char *tmpc = linenoise (prompt);                /* get cmd line */
6910         if (tmpc == NULL)                               /* bad result? */
6911             cptr = NULL;
6912         else {
6913             strncpy (cptr, tmpc, size-1);               /* copy result */
6914             linenoiseHistoryAdd (tmpc);                 /* add to history */
6915             free (tmpc);                                /* free temp */
6916             }
6917         }
6918 #else
6919         fflush (stdout);                                /* flush output */
6920         printf ("%s", prompt);                          /* display prompt */
6921         fflush (stdout);                                /* flush output */
6922         cptr = fgets (cptr, size, stream);              /* get cmd line */
6923         }
6924 #endif /* ifdef HAVE_LINEHISTORY */
6925 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
6926 
6927 if (cptr == NULL) {
6928     clearerr (stream);                                  /* clear error */
6929     return NULL;                                        /* ignore EOF */
6930     }
6931 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
6932     if ((*tptr == '\n') || (*tptr == '\r') ||
6933         (tptr == (cptr + size - 1))) {                  /* str max length? */
6934         *tptr = 0;                                      /* terminate */
6935         break;
6936         }
6937     }
6938 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
6939     memmove (cptr, cptr + 3, strlen (cptr + 3));
6940 while (sim_isspace (*cptr))                             /* trim leading spc */
6941     cptr++;
6942 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
6943     if (sim_do_echo)                                    /* echo comments if -v */
6944         sim_printf("%s> %s\n", do_position(), cptr);
6945     *cptr = 0;
6946     }
6947 
6948 return cptr;
6949 }
6950 
6951 /* get_glyph            get next glyph (force upper case)
6952    get_glyph_nc         get next glyph (no conversion)
6953    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
6954    get_glyph_cmd        get command glyph (force upper case, extract leading !)
6955    get_glyph_gen        get next glyph (general case)
6956 
6957    Inputs:
6958         iptr        =   pointer to input string
6959         optr        =   pointer to output string
6960         mchar       =   optional end of glyph character
6961         uc          =   TRUE for convert to upper case (_gen only)
6962         quote       =   TRUE to allow quote enclosing values (_gen only)
6963         escape_char =   optional escape character within quoted strings (_gen only)
6964 
6965    Outputs
6966         result      =   pointer to next character in input string
6967 */
6968 
6969 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char)
6970 {
6971 t_bool quoting = FALSE;
6972 t_bool escaping = FALSE;
6973 char quote_char = 0;
6974 
6975 while ((*iptr != 0) &&
6976        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
6977     if (quote) {
6978         if (quoting) {
6979             if (!escaping) {
6980                 if (*iptr == escape_char)
6981                     escaping = TRUE;
6982                 else
6983                     if (*iptr == quote_char)
6984                         quoting = FALSE;
6985                 }
6986             else
6987                 escaping = FALSE;
6988             }
6989         else {
6990             if ((*iptr == '"') || (*iptr == '\'')) {
6991                 quoting = TRUE;
6992                 quote_char = *iptr;
6993                 }
6994             }
6995         }
6996     if (sim_islower (*iptr) && uc)
6997         *optr = (char)toupper (*iptr);
6998     else *optr = *iptr;
6999     iptr++; optr++;
7000     }
7001 if (mchar && (*iptr == mchar))              /* skip input terminator */
7002     iptr++;
7003 *optr = 0;                                  /* terminate result string */
7004 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
7005     iptr++;
7006 return iptr;
7007 }
7008 
7009 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
7010 {
7011 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7012 }
7013 
7014 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
7015 {
7016 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7017 }
7018 
7019 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
7020 {
7021 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7022 }
7023 
7024 CONST char *get_glyph_cmd (const char *iptr, char *optr)
7025 {
7026 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7027 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7028     strcpy (optr, "!");                     /* return ! as command glyph */
7029     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
7030     }
7031 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7032 }
7033 
7034 /* Trim trailing spaces from a string
7035 
7036     Inputs:
7037         cptr    =       pointer to string
7038     Outputs:
7039         cptr    =       pointer to string
7040 */
7041 
7042 char *sim_trim_endspc (char *cptr)
7043 {
7044 char *tptr;
7045 
7046 tptr = cptr + strlen (cptr);
7047 while ((--tptr >= cptr) && sim_isspace (*tptr))
7048     *tptr = 0;
7049 return cptr;
7050 }
7051 
7052 int sim_isspace (char c)
7053 {
7054 return (c & 0x80) ? 0 : isspace (c);
7055 }
7056 
7057 int sim_islower (char c)
7058 {
7059 return (c & 0x80) ? 0 : islower (c);
7060 }
7061 
7062 int sim_isalpha (char c)
7063 {
7064 return (c & 0x80) ? 0 : isalpha (c);
7065 }
7066 
7067 int sim_isprint (char c)
7068 {
7069 return (c & 0x80) ? 0 : isprint (c);
7070 }
7071 
7072 int sim_isdigit (char c)
7073 {
7074 return (c & 0x80) ? 0 : isdigit (c);
7075 }
7076 
7077 int sim_isgraph (char c)
7078 {
7079 return (c & 0x80) ? 0 : isgraph (c);
7080 }
7081 
7082 int sim_isalnum (char c)
7083 {
7084 return (c & 0x80) ? 0 : isalnum (c);
7085 }
7086 
7087 /* get_uint             unsigned number
7088 
7089    Inputs:
7090         cptr    =       pointer to input string
7091         radix   =       input radix
7092         max     =       maximum acceptable value
7093         *status =       pointer to error status
7094    Outputs:
7095         val     =       value
7096 */
7097 
7098 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
7099 {
7100 t_value val;
7101 CONST char *tptr;
7102 
7103 *status = SCPE_OK;
7104 val = strtotv ((CONST char *)cptr, &tptr, radix);
7105 if ((cptr == tptr) || (val > max))
7106     *status = SCPE_ARG;
7107 else {
7108     while (sim_isspace (*tptr)) tptr++;
7109     if (*tptr != 0)
7110         *status = SCPE_ARG;
7111     }
7112 return val;
7113 }
7114 
7115 /* get_range            range specification
7116 
7117    Inputs:
7118         dptr    =       pointer to device (NULL if none)
7119         cptr    =       pointer to input string
7120         *lo     =       pointer to low result
7121         *hi     =       pointer to high result
7122         aradix  =       radix
7123         max     =       default high value
7124         term    =       terminating character, 0 if none
7125    Outputs:
7126         tptr    =       input pointer after processing
7127                         NULL if error
7128 */
7129 
7130 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
7131     uint32 rdx, t_addr max, char term)
7132 {
7133 CONST char *tptr;
7134 
7135 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
7136     tptr = cptr + strlen ("ALL");
7137     *lo = 0;
7138     *hi = max;
7139     }
7140 else {
7141     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
7142         ((cptr[1] == '\0') ||
7143          (cptr[1] == '-')  ||
7144          (cptr[1] == ':')  ||
7145          (cptr[1] == '/'))) {
7146         tptr = cptr + strlen (".");
7147         *lo = *hi = sim_last_addr;
7148         }
7149     else {
7150         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
7151             tptr = cptr + strlen ("$");
7152             *hi = *lo = (t_addr)sim_last_val;
7153             }
7154         else {
7155             if (dptr && sim_vm_parse_addr)                      /* get low */
7156                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7157             else
7158                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7159             if (cptr == tptr)                                   /* error? */
7160                     return NULL;
7161             }
7162         }
7163     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
7164         cptr = tptr + 1;
7165         if (dptr && sim_vm_parse_addr)                  /* get high */
7166             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7167         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7168         if (cptr == tptr)
7169             return NULL;
7170         if (*lo > *hi)
7171             return NULL;
7172         }
7173     else if (*tptr == '/') {                            /* relative? */
7174         cptr = tptr + 1;
7175         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7176         if ((cptr == tptr) || (*hi == 0))
7177             return NULL;
7178         *hi = *lo + *hi - 1;
7179         }
7180     else *hi = *lo;
7181     }
7182 sim_last_addr = *hi;
7183 if (term && (*tptr++ != term))
7184     return NULL;
7185 return tptr;
7186 }
7187 
7188 /* sim_decode_quoted_string
7189 
7190    Inputs:
7191         iptr        =   pointer to input string
7192         optr        =   pointer to output buffer
7193                         the output buffer must be allocated by the caller
7194                         and to avoid overrunat it must be at least as big
7195                         as the input string.
7196 
7197    Outputs
7198         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7199         osize       =   size of the data in the optr buffer
7200 
7201    The input string must be quoted.  Quotes may be either single or
7202    double but the opening anc closing quote characters must match.
7203    Within quotes C style character escapes are allowed.
7204 
7205    The following character escapes are explicitly supported:
7206         \r  ASCII Carriage Return character (Decimal value 13)
7207         \n  ASCII Linefeed character (Decimal value 10)
7208         \f  ASCII Formfeed character (Decimal value 12)
7209         \t  ASCII Horizontal Tab character (Decimal value 9)
7210         \v  ASCII Vertical Tab character (Decimal value 11)
7211         \b  ASCII Backspace character (Decimal value 8)
7212         \\  ASCII Backslash character (Decimal value 92)
7213         \'  ASCII Single Quote character (Decimal value 39)
7214         \"  ASCII Double Quote character (Decimal value 34)
7215         \?  ASCII Question Mark character (Decimal value 63)
7216         \e  ASCII Escape character (Decimal value 27)
7217      as well as octal character values of the form:
7218         \n{n{n}} where each n is an octal digit (0-7)
7219      and hext character values of the form:
7220         \xh{h} where each h is a hex digit (0-9A-Fa-f)
7221 */
7222 
7223 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
7224 {
7225 char quote_char;
7226 uint8 *ostart = optr;
7227 
7228 *osize = 0;
7229 if ((strlen(iptr) == 1) ||
7230     (iptr[0] != iptr[strlen(iptr)-1]) ||
7231     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7232     return SCPE_ARG;            /* String must be quote delimited */
7233 quote_char = *iptr++;           /* Save quote character */
7234 while (iptr[1]) {               /* Skip trailing quote */
7235     if (*iptr != '\\') {
7236         if (*iptr == quote_char)
7237             return SCPE_ARG;    /* Imbedded quotes must be escaped */
7238         *(optr++) = (uint8)(*(iptr++));
7239         continue;
7240         }
7241     ++iptr; /* Skip backslash */
7242     switch (*iptr) {
7243         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7244             *(optr++) = 13; ++iptr;
7245             break;
7246         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7247             *(optr++) = 10; ++iptr;
7248             break;
7249         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7250             *(optr++) = 12; ++iptr;
7251             break;
7252         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7253             *(optr++) = 9; ++iptr;
7254             break;
7255         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7256             *(optr++) = 11; ++iptr;
7257             break;
7258         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7259             *(optr++) = 8; ++iptr;
7260             break;
7261         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7262             *(optr++) = 92; ++iptr;
7263             break;
7264         case 'e':   /* ASCII Escape character (Decimal value 27) */
7265             *(optr++) = 27; ++iptr;
7266             break;
7267         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7268             *(optr++) = 39; ++iptr;
7269             break;
7270         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7271             *(optr++) = 34; ++iptr;
7272             break;
7273         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7274             *(optr++) = 63; ++iptr;
7275             break;
7276         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7277             *optr = *(iptr++) - '0';
7278             if ((*iptr >= '0') && (*iptr <= '7'))
7279                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7280             if ((*iptr >= '0') && (*iptr <= '7'))
7281                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7282             ++optr;
7283             break;
7284         case 'x':
7285             if (1) {
7286                 static const char *hex_digits = "0123456789ABCDEF";
7287                 const char *c;
7288 
7289                 ++iptr;
7290                 *optr = 0;
7291                 c = strchr (hex_digits, toupper(*iptr));
7292                 if (c) {
7293                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7294                     ++iptr;
7295                     }
7296                 c = strchr (hex_digits, toupper(*iptr));
7297                 if (c) {
7298                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7299                     ++iptr;
7300                     }
7301                 ++optr;
7302                 }
7303             break;
7304         default:
7305             return SCPE_ARG;    /* Invalid escape */
7306         }
7307     }
7308 *optr = '\0';
7309 *osize = (uint32)(optr-ostart);
7310 return SCPE_OK;
7311 }
7312 
7313 /* sim_encode_quoted_string
7314 
7315    Inputs:
7316         iptr        =   pointer to input buffer
7317         size        =   number of bytes of data in the buffer
7318 
7319    Outputs
7320         optr        =   pointer to output buffer
7321                         the output buffer must be freed by the caller
7322 
7323    The input data will be encoded into a simply printable form.
7324    Control and other non-printable data will be escaped using the
7325    following rules:
7326 
7327    The following character escapes are explicitly supported:
7328         \r  ASCII Carriage Return character (Decimal value 13)
7329         \n  ASCII Linefeed character (Decimal value 10)
7330         \f  ASCII Formfeed character (Decimal value 12)
7331         \t  ASCII Horizontal Tab character (Decimal value 9)
7332         \v  ASCII Vertical Tab character (Decimal value 11)
7333         \b  ASCII Backspace character (Decimal value 8)
7334         \\  ASCII Backslash character (Decimal value 92)
7335         \'  ASCII Single Quote character (Decimal value 39)
7336         \"  ASCII Double Quote character (Decimal value 34)
7337         \?  ASCII Question Mark character (Decimal value 63)
7338         \e  ASCII Escape character (Decimal value 27)
7339      as well as octal character values of the form:
7340         \n{n{n}} where each n is an octal digit (0-7)
7341      and hext character values of the form:
7342         \xh{h} where each h is a hex digit (0-9A-Fa-f)
7343 */
7344 
7345 char *sim_encode_quoted_string (const uint8 *iptr, uint32 size)
7346 {
7347 uint32 i;
7348 t_bool double_quote_found = FALSE;
7349 t_bool single_quote_found = FALSE;
7350 char quote = '"';
7351 char *tptr, *optr;
7352 
7353 optr = (char *)malloc (4*size + 3);
7354 if (optr == NULL)
7355     return NULL;
7356 tptr = optr;
7357 for (i=0; i<size; i++)
7358     switch ((char)iptr[i]) {
7359         case '"':
7360             double_quote_found = TRUE;
7361             break;
7362         case '\'':
7363             single_quote_found = TRUE;
7364             break;
7365         }
7366 if (double_quote_found && (!single_quote_found))
7367     quote = '\'';
7368 *tptr++ = quote;
7369 while (size--) {
7370     switch (*iptr) {
7371         case '\r':
7372             *tptr++ = '\\'; *tptr++ = 'r'; break;
7373         case '\n':
7374             *tptr++ = '\\'; *tptr++ = 'n'; break;
7375         case '\f':
7376             *tptr++ = '\\'; *tptr++ = 'f'; break;
7377         case '\t':
7378             *tptr++ = '\\'; *tptr++ = 't'; break;
7379         case '\v':
7380             *tptr++ = '\\'; *tptr++ = 'v'; break;
7381         case '\b':
7382             *tptr++ = '\\'; *tptr++ = 'b'; break;
7383         case '\\':
7384             *tptr++ = '\\'; *tptr++ = '\\'; break;
7385         case '"':
7386         case '\'':
7387             if (quote == *iptr)
7388                 *tptr++ = '\\';
7389         default:
7390             if (sim_isprint (*iptr))
7391                 *tptr++ = *iptr;
7392             else {
7393                 sprintf (tptr, "\\%03o", *iptr);
7394                 tptr += 4;
7395                 }
7396             break;
7397         }
7398     ++iptr;
7399     }
7400 *tptr++ = quote;
7401 *tptr++ = '\0';
7402 return optr;
7403 }
7404 
7405 void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size)
7406 {
7407 char *string;
7408 
7409 string = sim_encode_quoted_string (buf, size);
7410 fprintf (st, "%s", string);
7411 free (string);
7412 }
7413 
7414 /* Find_device          find device matching input string
7415 
7416    Inputs:
7417         cptr    =       pointer to input string
7418    Outputs:
7419         result  =       pointer to device
7420 */
7421 
7422 DEVICE *find_dev (const char *cptr)
7423 {
7424 int32 i;
7425 DEVICE *dptr;
7426 
7427 if (cptr == NULL)
7428     return NULL;
7429 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7430     if ((strcmp (cptr, dptr->name) == 0) ||
7431         (dptr->lname &&
7432         (strcmp (cptr, dptr->lname) == 0)))
7433         return dptr;
7434     }
7435 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7436     if ((strcmp (cptr, dptr->name) == 0) ||
7437         (dptr->lname &&
7438         (strcmp (cptr, dptr->lname) == 0)))
7439         return dptr;
7440     }
7441 return NULL;
7442 }
7443 
7444 /* Find_unit            find unit matching input string
7445 
7446    Inputs:
7447         cptr    =       pointer to input string
7448         uptr    =       pointer to unit pointer
7449    Outputs:
7450         result  =       pointer to device (null if no dev)
7451         *iptr   =       pointer to unit (null if nx unit)
7452 
7453 */
7454 
7455 DEVICE *find_unit (const char *cptr, UNIT **uptr)
7456 {
7457 uint32 i, u;
7458 const char *nptr;
7459 const char *tptr;
7460 t_stat r;
7461 DEVICE *dptr;
7462 
7463 if (uptr == NULL)                                       /* arg error? */
7464     return NULL;
7465 *uptr = NULL;
7466 if ((dptr = find_dev (cptr))) {                         /* exact match? */
7467     if (qdisable (dptr))                                /* disabled? */
7468         return NULL;
7469     *uptr = dptr->units;                                /* unit 0 */
7470     return dptr;
7471     }
7472 
7473 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
7474     if (qdisable (dptr))                                /* device disabled? */
7475         continue;
7476     if (dptr->numunits &&                               /* any units? */
7477         (((nptr = dptr->name) &&
7478           (strncmp (cptr, nptr, strlen (nptr)) == 0)) ||
7479          ((nptr = dptr->lname) &&
7480           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7481         tptr = cptr + strlen (nptr);
7482         if (sim_isdigit (*tptr)) {
7483             if (qdisable (dptr))                        /* disabled? */
7484                 return NULL;
7485             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7486             if (r != SCPE_OK)                           /* error? */
7487                 *uptr = NULL;
7488             else
7489                 *uptr = dptr->units + u;
7490             return dptr;
7491             }
7492         }
7493     for (u = 0; u < dptr->numunits; u++) {
7494         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7495             *uptr = &dptr->units[u];
7496             return dptr;
7497             }
7498         }
7499     }
7500 return NULL;
7501 }
7502 
7503 /* sim_register_internal_device   Add device to internal device list
7504 
7505    Inputs:
7506         dptr    =       pointer to device
7507 */
7508 
7509 t_stat sim_register_internal_device (DEVICE *dptr)
7510 {
7511 uint32 i;
7512 
7513 for (i = 0; (sim_devices[i] != NULL); i++)
7514     if (sim_devices[i] == dptr)
7515         return SCPE_OK;
7516 for (i = 0; i < sim_internal_device_count; i++)
7517     if (sim_internal_devices[i] == dptr)
7518         return SCPE_OK;
7519 ++sim_internal_device_count;
7520 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7521 sim_internal_devices[sim_internal_device_count-1] = dptr;
7522 sim_internal_devices[sim_internal_device_count] = NULL;
7523 return SCPE_OK;
7524 }
7525 
7526 /* Find_dev_from_unit   find device for unit
7527 
7528    Inputs:
7529         uptr    =       pointer to unit
7530    Outputs:
7531         result  =       pointer to device
7532 */
7533 
7534 DEVICE *find_dev_from_unit (UNIT *uptr)
7535 {
7536 DEVICE *dptr;
7537 uint32 i, j;
7538 
7539 if (uptr == NULL)
7540     return NULL;
7541 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7542     for (j = 0; j < dptr->numunits; j++) {
7543         if (uptr == (dptr->units + j))
7544             return dptr;
7545         }
7546     }
7547 for (i = 0; i<sim_internal_device_count; i++) {
7548     dptr = sim_internal_devices[i];
7549     for (j = 0; j < dptr->numunits; j++) {
7550         if (uptr == (dptr->units + j))
7551             return dptr;
7552         }
7553     }
7554 return NULL;
7555 }
7556 
7557 /* Test for disabled device */
7558 
7559 t_bool qdisable (DEVICE *dptr)
7560 {
7561 return (dptr->flags & DEV_DIS? TRUE: FALSE);
7562 }
7563 
7564 /* find_reg_glob        find globally unique register
7565 
7566    Inputs:
7567         cptr    =       pointer to input string
7568         optr    =       pointer to output pointer (can be null)
7569         gdptr   =       pointer to global device
7570    Outputs:
7571         result  =       pointer to register, NULL if error
7572         *optr   =       pointer to next character in input string
7573         *gdptr  =       pointer to device where found
7574 */
7575 
7576 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
7577 {
7578 int32 i;
7579 DEVICE *dptr;
7580 REG *rptr, *srptr = NULL;
7581 
7582 *gdptr = NULL;
7583 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
7584     if (dptr->flags & DEV_DIS)                          /* skip disabled */
7585         continue;
7586     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
7587         if (srptr)                                      /* ambig? err */
7588             return NULL;
7589         srptr = rptr;                                   /* save reg */
7590         *gdptr = dptr;                                  /* save unit */
7591         }
7592     }
7593 return srptr;
7594 }
7595 
7596 /* find_reg             find register matching input string
7597 
7598    Inputs:
7599         cptr    =       pointer to input string
7600         optr    =       pointer to output pointer (can be null)
7601         dptr    =       pointer to device
7602    Outputs:
7603         result  =       pointer to register, NULL if error
7604         *optr   =       pointer to next character in input string
7605 */
7606 
7607 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
7608 {
7609 CONST char *tptr;
7610 REG *rptr;
7611 size_t slnt;
7612 
7613 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
7614     return NULL;
7615 tptr = cptr;
7616 do {
7617     tptr++;
7618     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
7619 slnt = tptr - cptr;
7620 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
7621     if ((slnt == strlen (rptr->name)) &&
7622         (strncmp (cptr, rptr->name, slnt) == 0)) {
7623         if (optr != NULL)
7624             *optr = tptr;
7625         return rptr;
7626         }
7627     }
7628 return NULL;
7629 }
7630 
7631 /* get_switches         get switches from input string
7632 
7633    Inputs:
7634         cptr    =       pointer to input string
7635    Outputs:
7636         sw      =       switch bit mask
7637                         0 if no switches, -1 if error
7638 */
7639 
7640 int32 get_switches (const char *cptr)
7641 {
7642 int32 sw;
7643 
7644 if (*cptr != '-')
7645     return 0;
7646 sw = 0;
7647 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
7648     if (sim_isalpha (*cptr) == 0)
7649         return -1;
7650     sw = sw | SWMASK (toupper (*cptr));
7651     }
7652 return sw;
7653 }
7654 
7655 /* get_sim_sw           accumulate sim_switches
7656 
7657    Inputs:
7658         cptr    =       pointer to input string
7659    Outputs:
7660         ptr     =       pointer to first non-string glyph
7661                         NULL if error
7662 */
7663 
7664 CONST char *get_sim_sw (CONST char *cptr)
7665 {
7666 int32 lsw;
7667 char gbuf[CBUFSIZE];
7668 
7669 while (*cptr == '-') {                                  /* while switches */
7670     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
7671     lsw = get_switches (gbuf);                          /* parse */
7672     if (lsw <= 0)                                       /* invalid? */
7673         return NULL;
7674     sim_switches = sim_switches | lsw;                  /* accumulate */
7675     }
7676 return cptr;
7677 }
7678 
7679 /* get_sim_opt          get simulator command options
7680 
7681    Inputs:
7682         opt     =       command options
7683         cptr    =       pointer to input string
7684    Outputs:
7685         ptr     =       pointer to next glypsh, NULL if error
7686         *stat   =       error status
7687 */
7688 
7689 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
7690 {
7691 int32 t;
7692 char gbuf[CBUFSIZE];
7693 CONST char *svptr;
7694 DEVICE *tdptr;
7695 UNIT *tuptr;
7696 
7697 sim_switches = 0;                                       /* no switches */
7698 sim_ofile = NULL;                                       /* no output file */
7699 sim_schrptr = NULL;                                     /* no search */
7700 sim_schaptr = NULL;                                     /* no search */
7701 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
7702 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
7703 sim_stabr.count = 1;
7704 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
7705 memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
7706 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
7707 memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
7708 sim_staba.count = sim_emax;
7709 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
7710 memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
7711 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
7712 memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
7713 if (! sim_dflt_dev)
7714   return NULL;
7715 sim_dfdev = sim_dflt_dev;
7716 sim_dfunit = sim_dfdev->units;
7717 sim_opt_out = 0;                                        /* no options yet */
7718 *st = SCPE_OK;
7719 while (*cptr) {                                         /* loop through modifiers */
7720     svptr = cptr;                                       /* save current position */
7721     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
7722         if (sim_ofile) {                                /* already got one? */
7723             fclose (sim_ofile);                         /* one per customer */
7724             *st = SCPE_ARG;
7725             return NULL;
7726             }
7727         cptr = get_glyph (cptr + 1, gbuf, 0);
7728         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
7729         if (sim_ofile == NULL) {                        /* open failed? */
7730             *st = SCPE_OPENERR;
7731             return NULL;
7732             }
7733         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
7734         continue;
7735         }
7736     cptr = get_glyph (cptr, gbuf, 0);
7737     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
7738         if (t < 0) {                                    /* err if bad switch */
7739             *st = SCPE_INVSW;
7740             return NULL;
7741             }
7742         sim_switches = sim_switches | t;                /* or in new switches */
7743         }
7744     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
7745         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
7746         sim_schrptr = &sim_stabr;                       /* set search */
7747         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
7748         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
7749         }
7750     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
7751         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
7752         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
7753         (tuptr != NULL)) {
7754         sim_dfdev = tdptr;                              /* set as default */
7755         sim_dfunit = tuptr;
7756         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
7757         }
7758     else return svptr;                                  /* not rec, break out */
7759     }
7760 return cptr;
7761 }
7762 
7763 /* put_switches         put switches into string
7764 
7765    Inputs:
7766         buf     =       pointer to string buffer
7767         bufsize =       size of string buffer
7768         sw      =       switch bit mask
7769    Outputs:
7770         buf     =       buffer with switches converted to text
7771 */
7772 
7773 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
7774 {
7775 char *optr = buf;
7776 int32 bit;
7777 
7778 memset (buf, 0, bufsize);
7779 if ((sw == 0) || (bufsize < 3))
7780     return buf;
7781 --bufsize;                          /* leave room for terminating NUL */
7782 *optr++ = '-';
7783 for (bit=0; bit <= ('Z'-'A'); bit++)
7784     if (sw & (1 << bit))
7785         if ((size_t)(optr - buf) < bufsize)
7786             *optr++ = 'A' + bit;
7787 return buf;
7788 }
7789 
7790 /* Get register search specification
7791 
7792    Inputs:
7793         cptr    =       pointer to input string
7794         radix   =       radix for numbers
7795         schptr =        pointer to search table
7796    Outputs:
7797         return =        NULL if error
7798                         schptr if valid search specification
7799 */
7800 
7801 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
7802 {
7803 int32 c, logop, cmpop;
7804 t_value logval, cmpval;
7805 const char *sptr;
7806 CONST char *tptr;
7807 const char logstr[] = "|&^", cmpstr[] = "=!><";
7808 
7809 logval = cmpval = 0;
7810 if (*cptr == 0)                                         /* check for clause */
7811     return NULL;
7812 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7813     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7814         logop = (int32)(sptr - logstr);
7815         logval = strtotv (cptr, &tptr, radix);
7816         if (cptr == tptr)
7817             return NULL;
7818         cptr = tptr;
7819         }
7820     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7821         cmpop = (int32)(sptr - cmpstr);
7822         if (*cptr == '=') {
7823             cmpop = cmpop + strlen (cmpstr);
7824             cptr++;
7825             }
7826         cmpval = strtotv (cptr, &tptr, radix);
7827         if (cptr == tptr)
7828             return NULL;
7829         cptr = tptr;
7830         }
7831     else return NULL;
7832     }                                                   /* end for */
7833 if (schptr->count != 1) {
7834     free (schptr->mask);
7835     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7836     free (schptr->comp);
7837     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7838     }
7839 if (logop >= 0) {
7840     schptr->logic = logop;
7841     schptr->mask[0] = logval;
7842     }
7843 if (cmpop >= 0) {
7844     schptr->boolop = cmpop;
7845     schptr->comp[0] = cmpval;
7846     }
7847 schptr->count = 1;
7848 return schptr;
7849 }
7850 
7851 /* Get memory search specification
7852 
7853    Inputs:
7854         cptr    =       pointer to input string
7855         radix   =       radix for numbers
7856         schptr =        pointer to search table
7857    Outputs:
7858         return =        NULL if error
7859                         schptr if valid search specification
7860 */
7861 
7862 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
7863 {
7864 int32 c, logop, cmpop;
7865 t_value *logval, *cmpval;
7866 t_stat reason = SCPE_OK;
7867 CONST char *ocptr = cptr;
7868 const char *sptr;
7869 char gbuf[CBUFSIZE];
7870 const char logstr[] = "|&^", cmpstr[] = "=!><";
7871 
7872 if (*cptr == 0)                                         /* check for clause */
7873     return NULL;
7874 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
7875 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
7876 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7877     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7878         logop = (int32)(sptr - logstr);
7879         cptr = get_glyph (cptr, gbuf, 0);
7880         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
7881         if (reason > 0) {
7882             free (logval);
7883             free (cmpval);
7884             return get_rsearch (ocptr, radix, schptr);
7885             }
7886         }
7887     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7888         cmpop = (int32)(sptr - cmpstr);
7889         if (*cptr == '=') {
7890             cmpop = cmpop + strlen (cmpstr);
7891             cptr++;
7892             }
7893         cptr = get_glyph (cptr, gbuf, 0);
7894         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
7895         if (reason > 0) {
7896             free (logval);
7897             free (cmpval);
7898             return get_rsearch (ocptr, radix, schptr);
7899             }
7900         }
7901     else {
7902         free (logval);
7903         free (cmpval);
7904         return NULL;
7905         }
7906     }                                                   /* end for */
7907 if (schptr->count != (uint32)(1 - reason)) {
7908     schptr->count = 1 - reason;
7909     free (schptr->mask);
7910     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7911     free (schptr->comp);
7912     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7913     }
7914 if (logop >= 0) {
7915     schptr->logic = logop;
7916     free (schptr->mask);
7917     schptr->mask = logval;
7918     }
7919 else {
7920     free (logval);
7921     }
7922 if (cmpop >= 0) {
7923     schptr->boolop = cmpop;
7924     free (schptr->comp);
7925     schptr->comp = cmpval;
7926     }
7927 else {
7928     free (cmpval);
7929     }
7930 return schptr;
7931 }
7932 
7933 /* Test value against search specification
7934 
7935    Inputs:
7936         val    =        value list to test
7937         schptr =        pointer to search table
7938    Outputs:
7939         return =        1 if value passes search criteria, 0 if not
7940 */
7941 
7942 int32 test_search (t_value *values, SCHTAB *schptr)
7943 {
7944 t_value *val = NULL;
7945 int32 i, updown;
7946 int32 ret = 0;
7947 
7948 if (schptr == NULL)
7949     return ret;
7950 
7951 val = (t_value *)malloc (schptr->count * sizeof (*values));
7952 
7953 for (i=0; i<(int32)schptr->count; i++) {
7954     val[i] = values[i];
7955     switch (schptr->logic) {                            /* case on logical */
7956 
7957         case SCH_OR:
7958             val[i] = val[i] | schptr->mask[i];
7959             break;
7960 
7961         case SCH_AND:
7962             val[i] = val[i] & schptr->mask[i];
7963             break;
7964 
7965         case SCH_XOR:
7966             val[i] = val[i] ^ schptr->mask[i];
7967             break;
7968             }
7969     }
7970 
7971 ret = 1;
7972 if (1) {    /* Little Endian VM */
7973     updown = -1;
7974     i=schptr->count-1;
7975     }
7976 else {      /* Big Endian VM */
7977     updown = 1;
7978     i=0;
7979     }
7980 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
7981     switch (schptr->boolop) {                           /* case on comparison */
7982 
7983         case SCH_E: case SCH_EE:
7984             if (val[i] != schptr->comp[i])
7985                 ret = 0;
7986             break;
7987 
7988         case SCH_N: case SCH_NE:
7989             if (val[i] == schptr->comp[i])
7990                 ret = 0;
7991             break;
7992 
7993         case SCH_G:
7994             if (val[i] <= schptr->comp[i])
7995                 ret = 0;
7996             break;
7997 
7998         case SCH_GE:
7999             if (val[i] < schptr->comp[i])
8000                 ret = 0;
8001             break;
8002 
8003         case SCH_L:
8004             if (val[i] >= schptr->comp[i])
8005                 ret = 0;
8006             break;
8007 
8008         case SCH_LE:
8009             if (val[i] > schptr->comp[i])
8010                 ret = 0;
8011             break;
8012         }
8013     }
8014 free (val);
8015 return ret;
8016 }
8017 
8018 /* Radix independent input/output package
8019 
8020    strtotv - general radix input routine
8021 
8022    Inputs:
8023         inptr   =       string to convert
8024         endptr  =       pointer to first unconverted character
8025         radix   =       radix for input
8026    Outputs:
8027         value   =       converted value
8028 
8029    On an error, the endptr will equal the inptr.
8030 */
8031 
8032 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
8033 {
8034 int32 nodigit;
8035 t_value val;
8036 uint32 c, digit;
8037 
8038 *endptr = inptr;                                        /* assume fails */
8039 if ((radix < 2) || (radix > 36))
8040     return 0;
8041 while (sim_isspace (*inptr))                            /* bypass white space */
8042     inptr++;
8043 val = 0;
8044 nodigit = 1;
8045 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
8046     if (sim_islower (c))
8047         c = toupper (c);
8048     if (sim_isdigit (c))                                /* digit? */
8049         digit = c - (uint32) '0';
8050     else if (radix <= 10)                               /* stop if not expected */
8051         break;
8052     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
8053     if (digit >= radix)                                 /* valid in radix? */
8054         return 0;
8055     val = (val * radix) + digit;                        /* add to value */
8056     nodigit = 0;
8057     }
8058 if (nodigit)                                            /* no digits? */
8059     return 0;
8060 *endptr = inptr;                                        /* result pointer */
8061 return val;
8062 }
8063 
8064 /* fprint_val - general radix printing routine
8065 
8066    Inputs:
8067         stream  =       stream designator
8068         val     =       value to print
8069         radix   =       radix to print
8070         width   =       width to print
8071         format  =       leading zeroes format
8072    Outputs:
8073         status  =       error status
8074         if stream is NULL, returns length of output that would
8075         have been generated.
8076 */
8077 
8078 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
8079     uint32 width, uint32 format)
8080 {
8081 #define MAX_WIDTH ((int) ((CHAR_BIT * sizeof (t_value) * 4 + 3)/3))
8082 t_value owtest, wtest;
8083 int32 d, digit, ndigits, commas = 0;
8084 char dbuf[MAX_WIDTH + 1];
8085 
8086 for (d = 0; d < MAX_WIDTH; d++)
8087     dbuf[d] = (format == PV_RZRO)? '0': ' ';
8088 dbuf[MAX_WIDTH] = 0;
8089 d = MAX_WIDTH;
8090 do {
8091     d = d - 1;
8092     digit = (int32) (val % radix);
8093     val = val / radix;
8094     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8095     } while ((d > 0) && (val != 0));
8096 
8097 switch (format) {
8098     case PV_LEFT:
8099         break;
8100     case PV_RCOMMA:
8101         for (digit = 0; digit < MAX_WIDTH; digit++)
8102             if (dbuf[digit] != ' ')
8103                 break;
8104         ndigits = MAX_WIDTH - digit;
8105         commas = (ndigits - 1)/3;
8106         for (digit=0; digit<ndigits-3; digit++)
8107             dbuf[MAX_WIDTH + (digit - ndigits) - (ndigits - digit - 1)/3] = dbuf[MAX_WIDTH + (digit - ndigits)];
8108         for (digit=1; digit<=commas; digit++)
8109             dbuf[MAX_WIDTH - (digit * 4)] = ',';
8110         d = d - commas;
8111         if (width > MAX_WIDTH) {
8112             if (!buffer)
8113                 return width;
8114             sprintf (buffer, "%*s", -((int)width), dbuf);
8115             return SCPE_OK;
8116             }
8117         else
8118             if (width > 0)
8119                 d = MAX_WIDTH - width;
8120         break;
8121     case PV_RZRO:
8122     case PV_RSPC:
8123         wtest = owtest = radix;
8124         ndigits = 1;
8125         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8126             owtest = wtest;
8127             wtest = wtest * radix;
8128             ndigits = ndigits + 1;
8129             }
8130         if ((MAX_WIDTH - (ndigits + commas)) < d)
8131             d = MAX_WIDTH - (ndigits + commas);
8132         break;
8133     }
8134 if (!buffer)
8135     return strlen(dbuf+d);
8136 *buffer = '\0';
8137 if (width < strlen(dbuf+d))
8138     return SCPE_IOERR;
8139 strcpy(buffer, dbuf+d);
8140 return SCPE_OK;
8141 }
8142 
8143 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
8144     uint32 width, uint32 format)
8145 {
8146 char dbuf[MAX_WIDTH + 1];
8147 
8148 if (!stream)
8149     return sprint_val (NULL, val, radix, width, format);
8150 if (width > MAX_WIDTH)
8151     width = MAX_WIDTH;
8152 sprint_val (dbuf, val, radix, width, format);
8153 if (Fprintf (stream, "%s", dbuf) < 0)
8154     return SCPE_IOERR;
8155 return SCPE_OK;
8156 }
8157 
8158 const char *sim_fmt_secs (double seconds)
8159 {
8160 static char buf[60];
8161 char frac[16] = "";
8162 const char *sign = "";
8163 double val = seconds;
8164 double days, hours, mins, secs, msecs, usecs;
8165 
8166 if (val == 0.0)
8167     return "";
8168 if (val < 0.0) {
8169     sign = "-";
8170     val = -val;
8171     }
8172 days = floor (val / (24.0*60.0*60.0));
8173 val -= (days * 24.0*60.0*60.0);
8174 hours = floor (val / (60.0*60.0));
8175 val -= (hours * 60.0 * 60.0);
8176 mins = floor (val / 60.0);
8177 val -= (mins * 60.0);
8178 secs = floor (val);
8179 val -= secs;
8180 val *= 1000.0;
8181 msecs = floor (val);
8182 val -= msecs;
8183 val *= 1000.0;
8184 usecs = floor (val+0.5);
8185 if (usecs == 1000.0) {
8186     usecs = 0.0;
8187     msecs += 1;
8188     }
8189 if ((msecs > 0.0) || (usecs > 0.0)) {
8190     sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8191     while (frac[strlen (frac) - 1] == '0')
8192         frac[strlen (frac) - 1] = '\0';
8193     if (strlen (frac) == 1)
8194         frac[0] = '\0';
8195     }
8196 if (days > 0)
8197     sprintf (buf, "%s%.0f day%s %02.0f:%02.0f:%02.0f%s hour%s", sign, days, (days != 1)? "s" : "", hours, mins, secs, frac, (days == 1) ? "s" : "");
8198 else
8199     if (hours > 0)
8200         sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour", sign, hours, mins, secs, frac);
8201     else
8202         if (mins > 0)
8203             sprintf (buf, "%s%.0f:%02.0f%s minute", sign, mins, secs, frac);
8204         else
8205             if (secs > 0)
8206                 sprintf (buf, "%s%.0f%s second", sign, secs, frac);
8207             else
8208                 if (msecs > 0) {
8209                     if (usecs > 0)
8210                         sprintf (buf, "%s%.0f.%s msec", sign, msecs, frac+4);
8211                     else
8212                         sprintf (buf, "%s%.0f msec", sign, msecs);
8213                     }
8214                 else
8215                     sprintf (buf, "%s%.0f usec", sign, usecs);
8216 if (0 != strncmp ("1 ", buf, 2))
8217     strcpy (&buf[strlen (buf)], "s");
8218 return buf;
8219 }
8220 
8221 /* Event queue package
8222 
8223         sim_activate            add entry to event queue
8224         sim_activate_abs        add entry to event queue even if event already scheduled
8225         sim_activate_after      add entry to event queue after a specified amount of wall time
8226         sim_cancel              remove entry from event queue
8227         sim_process_event       process entries on event queue
8228         sim_is_active           see if entry is on event queue
8229         sim_activate_time       return time until activation
8230         sim_atime               return absolute time for an entry
8231         sim_gtime               return global time
8232         sim_qcount              return event queue entry count
8233 
8234    Asynchronous events are set up by queueing a unit data structure
8235    to the event queue with a timeout (in simulator units, relative
8236    to the current time).  Each simulator 'times' these events by
8237    counting down interval counter sim_interval.  When this reaches
8238    zero the simulator calls sim_process_event to process the event
8239    and to see if further events need to be processed, or sim_interval
8240    reset to count the next one.
8241 
8242    The event queue is maintained in clock order; entry timeouts are
8243    RELATIVE to the time in the previous entry.
8244 
8245    sim_process_event - process event
8246 
8247    Inputs:
8248         none
8249    Outputs:
8250         reason  =       reason code returned by any event processor,
8251                         or 0 (SCPE_OK) if no exceptions
8252 */
8253 
8254 t_stat sim_process_event (void)
8255 {
8256 UNIT *uptr;
8257 t_stat reason;
8258 
8259 if (stop_cpu)                                           /* stop CPU? */
8260     return SCPE_STOP;
8261 UPDATE_SIM_TIME;                                        /* update sim time */
8262 
8263 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8264     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8265     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Empty New Interval = %d\n", sim_interval);
8266     return SCPE_OK;
8267     }
8268 sim_processing_event = TRUE;
8269 do {
8270     uptr = sim_clock_queue;                             /* get first */
8271     sim_clock_queue = uptr->next;                       /* remove first */
8272     uptr->next = NULL;                                  /* hygiene */
8273     sim_interval -= uptr->time;
8274     uptr->time = 0;
8275     if (sim_clock_queue != QUEUE_LIST_END)
8276         sim_interval = sim_clock_queue->time;
8277     else
8278         sim_interval = noqueue_time = NOQUEUE_WAIT;
8279     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr));
8280     if (uptr->action != NULL)
8281         reason = uptr->action (uptr);
8282     else
8283         reason = SCPE_OK;
8284     } while ((reason == SCPE_OK) &&
8285              (sim_interval <= 0) &&
8286              (sim_clock_queue != QUEUE_LIST_END) &&
8287              (!stop_cpu));
8288 
8289 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8290     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8291     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval);
8292     }
8293 else
8294     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue));
8295 
8296 if ((reason == SCPE_OK) && stop_cpu)
8297     reason = SCPE_STOP;
8298 sim_processing_event = FALSE;
8299 return reason;
8300 }
8301 
8302 /* sim_activate - activate (queue) event
8303 
8304    Inputs:
8305         uptr    =       pointer to unit
8306         event_time =    relative timeout
8307    Outputs:
8308         reason  =       result (SCPE_OK if ok)
8309 */
8310 
8311 t_stat sim_activate (UNIT *uptr, int32 event_time)
8312 {
8313 if (uptr->dynflags & UNIT_TMR_UNIT)
8314     return sim_timer_activate (uptr, event_time);
8315 return _sim_activate (uptr, event_time);
8316 }
8317 
8318 t_stat _sim_activate (UNIT *uptr, int32 event_time)
8319 {
8320 UNIT *cptr, *prvptr;
8321 int32 accum;
8322 
8323 if (sim_is_active (uptr))                               /* already active? */
8324     return SCPE_OK;
8325 UPDATE_SIM_TIME;                                        /* update sim time */
8326 
8327 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8328 
8329 prvptr = NULL;
8330 accum = 0;
8331 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8332     if (event_time < (accum + cptr->time))
8333         break;
8334     accum = accum + cptr->time;
8335     prvptr = cptr;
8336     }
8337 if (prvptr == NULL) {                                   /* insert at head */
8338     cptr = uptr->next = sim_clock_queue;
8339     sim_clock_queue = uptr;
8340     }
8341 else {
8342     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
8343     prvptr->next = uptr;
8344     }
8345 uptr->time = event_time - accum;
8346 if (cptr != QUEUE_LIST_END)
8347     cptr->time = cptr->time - uptr->time;
8348 sim_interval = sim_clock_queue->time;
8349 return SCPE_OK;
8350 }
8351 
8352 /* sim_activate_abs - activate (queue) event even if event already scheduled
8353 
8354    Inputs:
8355         uptr    =       pointer to unit
8356         event_time =    relative timeout
8357    Outputs:
8358         reason  =       result (SCPE_OK if ok)
8359 */
8360 
8361 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
8362 {
8363 sim_cancel (uptr);
8364 return _sim_activate (uptr, event_time);
8365 }
8366 
8367 /* sim_activate_after - activate (queue) event
8368 
8369    Inputs:
8370         uptr    =       pointer to unit
8371         usec_delay =    relative timeout (in microseconds)
8372    Outputs:
8373         reason  =       result (SCPE_OK if ok)
8374 */
8375 
8376 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
8377 {
8378 return _sim_activate_after (uptr, usec_delay);
8379 }
8380 
8381 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
8382 {
8383 if (sim_is_active (uptr))                               /* already active? */
8384     return SCPE_OK;
8385 return sim_timer_activate_after (uptr, usec_delay);
8386 }
8387 
8388 /* sim_cancel - cancel (dequeue) event
8389 
8390    Inputs:
8391         uptr    =       pointer to unit
8392    Outputs:
8393         reason  =       result (SCPE_OK if ok)
8394 
8395 */
8396 
8397 t_stat sim_cancel (UNIT *uptr)
8398 {
8399 UNIT *cptr, *nptr;
8400 
8401 if (sim_clock_queue == QUEUE_LIST_END)
8402     return SCPE_OK;
8403 if (!sim_is_active (uptr))
8404     return SCPE_OK;
8405 UPDATE_SIM_TIME;                                        /* update sim time */
8406 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8407 nptr = QUEUE_LIST_END;
8408 
8409 if (sim_clock_queue == uptr) {
8410     nptr = sim_clock_queue = uptr->next;
8411     uptr->next = NULL;                                  /* hygiene */
8412     }
8413 else {
8414     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8415         if (cptr->next == uptr) {
8416             nptr = cptr->next = uptr->next;
8417             uptr->next = NULL;                          /* hygiene */
8418             break;                                      /* end queue scan */
8419             }
8420         }
8421     }
8422 if (nptr != QUEUE_LIST_END)
8423     nptr->time += (uptr->next) ? 0 : uptr->time;
8424 if (!uptr->next)
8425     uptr->time = 0;
8426 if (sim_clock_queue != QUEUE_LIST_END)
8427     sim_interval = sim_clock_queue->time;
8428 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8429 if (uptr->next) {
8430     sim_printf ("Cancel failed for %s\n", sim_uname(uptr));
8431     if (sim_deb)
8432         fclose(sim_deb);
8433     abort ();
8434     }
8435 return SCPE_OK;
8436 }
8437 
8438 /* sim_is_active - test for entry in queue
8439 
8440    Inputs:
8441         uptr    =       pointer to unit
8442    Outputs:
8443         result =        TRUE if unit is busy, FALSE inactive
8444 */
8445 
8446 t_bool sim_is_active (UNIT *uptr)
8447 {
8448 if (uptr->next == NULL)
8449   return FALSE;
8450 else
8451 return TRUE;
8452 }
8453 
8454 /* sim_activate_time - return activation time
8455 
8456    Inputs:
8457         uptr    =       pointer to unit
8458    Outputs:
8459         result =        absolute activation time + 1, 0 if inactive
8460 */
8461 
8462 int32 sim_activate_time (UNIT *uptr)
8463 {
8464 UNIT *cptr;
8465 int32 accum = 0;
8466 
8467 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8468     if (cptr == sim_clock_queue) {
8469         if (sim_interval > 0)
8470             accum = accum + sim_interval;
8471         }
8472     else
8473         accum = accum + cptr->time;
8474     if (cptr == uptr)
8475         return accum + 1;
8476     }
8477 return 0;
8478 }
8479 
8480 /* sim_gtime - return global time
8481 
8482    Inputs: none
8483    Outputs:
8484         time    =       global time
8485 */
8486 
8487 double sim_gtime (void)
8488 {
8489 return sim_time;
8490 }
8491 
8492 /* sim_qcount - return queue entry count
8493 
8494    Inputs: none
8495    Outputs:
8496         count   =       number of entries on the queue
8497 */
8498 
8499 int32 sim_qcount (void)
8500 {
8501 int32 cnt;
8502 UNIT *uptr;
8503 
8504 cnt = 0;
8505 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
8506     cnt++;
8507 return cnt;
8508 }
8509 
8510 /* Breakpoint package.  This module replaces the VM-implemented one
8511    instruction breakpoint capability.
8512 
8513    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
8514    efficient binary searching.  A breakpoint consists of a six entry structure:
8515 
8516         addr                    address of the breakpoint
8517         type                    types of breakpoints set on the address
8518                                 a bit mask representing letters A-Z
8519         cnt                     number of iterations before breakp is taken
8520         action                  pointer command string to be executed
8521                                 when break is taken
8522         next                    list of other breakpoints with the same addr specifier
8523         time_fired              array of when this breakpoint was fired for each class
8524 
8525    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
8526    is the bitwise OR of all the type fields).  A simulator need only check for
8527    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
8528 
8529    The package contains the following public routines:
8530 
8531         sim_brk_init            initialize
8532         sim_brk_set             set breakpoint
8533         sim_brk_clr             clear breakpoint
8534         sim_brk_clrall          clear all breakpoints
8535         sim_brk_show            show breakpoint
8536         sim_brk_showall         show all breakpoints
8537         sim_brk_test            test for breakpoint
8538         sim_brk_npc             PC has been changed
8539         sim_brk_getact          get next action
8540         sim_brk_clract          clear pending actions
8541 
8542    Initialize breakpoint system.
8543 */
8544 
8545 t_stat sim_brk_init (void)
8546 {
8547 int32 i;
8548 
8549 for (i=0; i<sim_brk_lnt; i++) {
8550     BRKTAB *bp = sim_brk_tab[i];
8551 
8552     while (bp) {
8553         BRKTAB *bpt = bp->next;
8554 
8555         free (bp->act);
8556         free (bp);
8557         bp = bpt;
8558         }
8559     }
8560 if (sim_brk_tab != NULL)
8561     memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8562 sim_brk_lnt = SIM_BRK_INILNT;
8563 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
8564 if (sim_brk_tab == NULL)
8565     return SCPE_MEM;
8566 memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8567 sim_brk_ent = sim_brk_ins = 0;
8568 sim_brk_clract ();
8569 sim_brk_npc (0);
8570 return SCPE_OK;
8571 }
8572 
8573 /* Search for a breakpoint in the sorted breakpoint table */
8574 
8575 BRKTAB *sim_brk_fnd (t_addr loc)
8576 {
8577 int32 lo, hi, p;
8578 BRKTAB *bp;
8579 
8580 if (sim_brk_ent == 0) {                                 /* table empty? */
8581     sim_brk_ins = 0;                                    /* insrt at head */
8582     return NULL;                                        /* sch fails */
8583     }
8584 lo = 0;                                                 /* initial bounds */
8585 hi = sim_brk_ent - 1;
8586 do {
8587     p = (lo + hi) >> 1;                                 /* probe */
8588     bp = sim_brk_tab[p];                                /* table addr */
8589     if (loc == bp->addr) {                              /* match? */
8590         sim_brk_ins = p;
8591         return bp;
8592         }
8593     else if (loc < bp->addr)                            /* go down? p is upper */
8594         hi = p - 1;
8595     else lo = p + 1;                                    /* go up? p is lower */
8596     } while (lo <= hi);
8597 if (loc < bp->addr)                                     /* insrt before or */
8598     sim_brk_ins = p;
8599 else sim_brk_ins = p + 1;                               /* after last sch */
8600 return NULL;
8601 }
8602 
8603 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
8604 {
8605 BRKTAB *bp = sim_brk_fnd (loc);
8606 
8607 while (bp) {
8608     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
8609                   (bp->typ == btyp))
8610         return bp;
8611     bp = bp->next;
8612     }
8613 return bp;
8614 }
8615 
8616 /* Insert a breakpoint */
8617 
8618 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
8619 {
8620 int32 i, t;
8621 BRKTAB *bp, **newp;
8622 
8623 if (sim_brk_ins < 0)
8624     return NULL;
8625 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
8626     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
8627     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
8628     if (newp == NULL)                                   /* can't extend */
8629         return NULL;
8630     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
8631     memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
8632     free (sim_brk_tab);                                 /* free old table */
8633     sim_brk_tab = newp;                                 /* new base, lnt */
8634     sim_brk_lnt = t;
8635     }
8636 if ((sim_brk_ins == sim_brk_ent) ||
8637     ((sim_brk_ins != sim_brk_ent) &&
8638      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
8639     for (i = sim_brk_ent; i > sim_brk_ins; --i)
8640         sim_brk_tab[i] = sim_brk_tab[i - 1];
8641     sim_brk_tab[sim_brk_ins] = NULL;
8642     }
8643 bp = (BRKTAB *)calloc (1, sizeof (*bp));
8644 bp->next = sim_brk_tab[sim_brk_ins];
8645 sim_brk_tab[sim_brk_ins] = bp;
8646 if (bp->next == NULL)
8647     sim_brk_ent += 1;
8648 bp->addr = loc;
8649 bp->typ = btyp;
8650 bp->cnt = 0;
8651 bp->act = NULL;
8652 for (i = 0; i < SIM_BKPT_N_SPC; i++)
8653     bp->time_fired[i] = -1.0;
8654 return bp;
8655 }
8656 
8657 /* Set a breakpoint of type sw */
8658 
8659 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
8660 {
8661 BRKTAB *bp;
8662 
8663 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
8664     sw |= sim_brk_dflt;
8665 if (~sim_brk_types & sw) {
8666     char gbuf[CBUFSIZE];
8667 
8668     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
8669     }
8670 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
8671     return SCPE_ARG;
8672 bp = sim_brk_fnd (loc);                                 /* loc present? */
8673 if (!bp)                                                /* no, allocate */
8674     bp = sim_brk_new (loc, sw);
8675 else {
8676     while (bp && (bp->typ != (uint32)sw))
8677         bp = bp->next;
8678     if (!bp)
8679         bp = sim_brk_new (loc, sw);
8680     }
8681 if (!bp)                                                /* still no? mem err */
8682     return SCPE_MEM;
8683 bp->cnt = ncnt;                                         /* set count */
8684 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
8685     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
8686     free (bp->act);                                     /* deallocate */
8687     bp->act = NULL;                                     /* now no action */
8688     }
8689 if ((act != NULL) && (*act != 0)) {                     /* new action? */
8690     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
8691     if (newp == NULL)                                   /* mem err? */
8692         return SCPE_MEM;
8693     strncpy (newp, act, CBUFSIZE);                      /* copy action */
8694     bp->act = newp;                                     /* set pointer */
8695     }
8696 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
8697 return SCPE_OK;
8698 }
8699 
8700 /* Clear a breakpoint */
8701 
8702 t_stat sim_brk_clr (t_addr loc, int32 sw)
8703 {
8704 BRKTAB *bpl = NULL;
8705 BRKTAB *bp = sim_brk_fnd (loc);
8706 int32 i;
8707 
8708 if (!bp)                                                /* not there? ok */
8709     return SCPE_OK;
8710 if (sw == 0)
8711     sw = SIM_BRK_ALLTYP;
8712 
8713 while (bp) {
8714     if (bp->typ == (bp->typ & sw)) {
8715         free (bp->act);                                 /* deallocate action */
8716         if (bp == sim_brk_tab[sim_brk_ins])
8717             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
8718         else
8719             bpl->next = bp->next;
8720         free (bp);
8721         bp = bpl;
8722         }
8723     else {
8724         bpl = bp;
8725         bp = bp->next;
8726         }
8727     }
8728 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
8729     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
8730     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
8731         sim_brk_tab[i] = sim_brk_tab[i+1];
8732     }
8733 sim_brk_summ = 0;                                       /* recalc summary */
8734 for (i = 0; i < sim_brk_ent; i++) {
8735     bp = sim_brk_tab[i];
8736     while (bp) {
8737         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
8738         bp = bp->next;
8739         }
8740     }
8741 return SCPE_OK;
8742 }
8743 
8744 /* Clear all breakpoints */
8745 
8746 t_stat sim_brk_clrall (int32 sw)
8747 {
8748 int32 i;
8749 
8750 if (sw == 0)
8751     sw = SIM_BRK_ALLTYP;
8752 for (i = 0; i < sim_brk_ent;) {
8753     t_addr loc = sim_brk_tab[i]->addr;
8754     sim_brk_clr (loc, sw);
8755     if ((i < sim_brk_ent) &&
8756         (loc == sim_brk_tab[i]->addr))
8757         ++i;
8758     }
8759 return SCPE_OK;
8760 }
8761 
8762 /* Show a breakpoint */
8763 
8764 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
8765 {
8766 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
8767 DEVICE *dptr;
8768 int32 i, any;
8769 
8770 if ((sw == 0) || (sw == SWMASK ('C')))
8771     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8772 if (!bp || (!(bp->typ & sw)))
8773     return SCPE_OK;
8774 dptr = sim_dflt_dev;
8775 if (dptr == NULL)
8776     return SCPE_OK;
8777 if (sw & SWMASK ('C'))
8778     fprintf (st, "SET BREAK ");
8779 else {
8780     if (sim_vm_fprint_addr)
8781         sim_vm_fprint_addr (st, dptr, loc);
8782     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8783     fprintf (st, ":\t");
8784     }
8785 for (i = any = 0; i < 26; i++) {
8786     if ((bp->typ >> i) & 1) {
8787         if ((sw & SWMASK ('C')) == 0) {
8788             if (any)
8789                 fprintf (st, ", ");
8790             fputc (i + 'A', st);
8791             }
8792         else
8793             fprintf (st, "-%c", i + 'A');
8794         any = 1;
8795         }
8796     }
8797 if (sw & SWMASK ('C')) {
8798     fprintf (st, " ");
8799     if (sim_vm_fprint_addr)
8800         sim_vm_fprint_addr (st, dptr, loc);
8801     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8802     }
8803 if (bp->cnt > 0)
8804     fprintf (st, "[%d]", bp->cnt);
8805 if (bp->act != NULL)
8806     fprintf (st, "; %s", bp->act);
8807 fprintf (st, "\n");
8808 return SCPE_OK;
8809 }
8810 
8811 /* Show all breakpoints */
8812 
8813 t_stat sim_brk_showall (FILE *st, int32 sw)
8814 {
8815 int32 bit, mask, types;
8816 BRKTAB **bpt;
8817 
8818 if ((sw == 0) || (sw == SWMASK ('C')))
8819     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8820 for (types=bit=0; bit <= ('Z'-'A'); bit++)
8821     if (sim_brk_types & (1 << bit))
8822         ++types;
8823 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
8824     fprintf (st, "Supported Breakpoint Types:");
8825     for (bit=0; bit <= ('Z'-'A'); bit++)
8826         if (sim_brk_types & (1 << bit))
8827             fprintf (st, " -%c", 'A' + bit);
8828     fprintf (st, "\n");
8829     }
8830 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
8831     mask = (sw & sim_brk_types);
8832     fprintf (st, "Displaying Breakpoint Types:");
8833     for (bit=0; bit <= ('Z'-'A'); bit++)
8834         if (mask & (1 << bit))
8835             fprintf (st, " -%c", 'A' + bit);
8836     fprintf (st, "\n");
8837     }
8838 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8839     BRKTAB *prev = NULL;
8840     BRKTAB *cur = *bpt;
8841     BRKTAB *next;
8842     /* First reverse the list */
8843     while (cur) {
8844         next = cur->next;
8845         cur->next = prev;
8846         prev = cur;
8847         cur = next;
8848         }
8849     /* save reversed list in the head pointer so lookups work */
8850     *bpt = prev;
8851     /* Walk the reversed list and print it in the order it was defined in */
8852     cur = prev;
8853     while (cur) {
8854         if (cur->typ & sw)
8855             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
8856         cur = cur->next;
8857         }
8858     /* reversing the list again */
8859     cur = prev;
8860     prev = NULL;
8861     while (cur) {
8862         next = cur->next;
8863         cur->next = prev;
8864         prev = cur;
8865         cur = next;
8866         }
8867     /* restore original list */
8868     *bpt = prev;
8869     }
8870 return SCPE_OK;
8871 }
8872 
8873 /* Test for breakpoint */
8874 
8875 uint32 sim_brk_test (t_addr loc, uint32 btyp)
8876 {
8877 BRKTAB *bp;
8878 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
8879 
8880 if (sim_brk_summ & BRK_TYP_DYN_ALL)
8881     btyp |= BRK_TYP_DYN_ALL;
8882 
8883 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
8884     double s_gtime = sim_gtime ();                      /* get time now */
8885 
8886     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
8887         return 0;
8888     bp->time_fired[spc] = s_gtime;                      /* remember match time */
8889     if (--bp->cnt > 0)                                  /* count > 0? */
8890         return 0;
8891     bp->cnt = 0;                                        /* reset count */
8892     sim_brk_setact (bp->act);                           /* set up actions */
8893     sim_brk_match_type = btyp & bp->typ;                /* set return value */
8894     if (bp->typ & BRK_TYP_TEMP)
8895         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
8896     sim_brk_match_addr = loc;
8897     return sim_brk_match_type;
8898     }
8899 return 0;
8900 }
8901 
8902 /* Get next pending action, if any */
8903 
8904 CONST char *sim_brk_getact (char *buf, int32 size)
8905 {
8906 char *ep;
8907 size_t lnt;
8908 
8909 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
8910     return NULL;
8911 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
8912     sim_brk_act[sim_do_depth]++;
8913 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
8914     return sim_brk_clract ();
8915     }
8916 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
8917     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
8918     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
8919     buf[lnt] = 0;                                       /* erase ; */
8920     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
8921     }
8922 else {
8923     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
8924     sim_brk_clract ();                                  /* no more */
8925     }
8926 return buf;
8927 }
8928 
8929 /* Clear pending actions */
8930 
8931 char *sim_brk_clract (void)
8932 {
8933 free (sim_brk_act_buf[sim_do_depth]);
8934 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
8935 }
8936 
8937 /* Set up pending actions */
8938 
8939 void sim_brk_setact (const char *action)
8940 {
8941 if (action) {
8942     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
8943     strcpy (sim_brk_act_buf[sim_do_depth], action);
8944     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
8945     }
8946 else
8947     sim_brk_clract ();
8948 }
8949 
8950 /* New PC */
8951 
8952 void sim_brk_npc (uint32 cnt)
8953 {
8954 uint32 spc;
8955 BRKTAB **bpt, *bp;
8956 
8957 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
8958     cnt = SIM_BKPT_N_SPC;
8959 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8960     for (bp = *bpt; bp; bp = bp->next) {
8961         for (spc = 0; spc < cnt; spc++)
8962             bp->time_fired[spc] = -1.0;
8963         }
8964     }
8965 }
8966 
8967 /* Clear breakpoint space */
8968 
8969 void sim_brk_clrspc (uint32 spc, uint32 btyp)
8970 {
8971 BRKTAB **bpt, *bp;
8972 
8973 if (spc < SIM_BKPT_N_SPC) {
8974     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8975         for (bp = *bpt; bp; bp = bp->next) {
8976             if (bp->typ & btyp)
8977                 bp->time_fired[spc] = -1.0;
8978             }
8979         }
8980     }
8981 }
8982 
8983 const char *sim_brk_message(void)
8984 {
8985 static char msg[256];
8986 char addr[65];
8987 char buf[32];
8988 
8989 msg[0] = '\0';
8990 if (sim_dflt_dev) {
8991   if (sim_vm_sprint_addr)
8992     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
8993   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
8994 }
8995 if (sim_brk_type_desc) {
8996     BRKTYPTAB *brk = sim_brk_type_desc;
8997 
8998     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
8999         if (brk->btyp == sim_brk_match_type) {
9000             sprintf (msg, "%s: %s", brk->desc, addr);
9001             break;
9002             }
9003         brk++;
9004         }
9005     }
9006 if (!msg[0])
9007     sprintf (msg, "%s Breakpoint at: %s\n", put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9008 
9009 return msg;
9010 }
9011 
9012 /* Expect package.  This code provides a mechanism to stop and control simulator
9013    execution based on traffic coming out of simulated ports and as well as a means
9014    to inject data into those ports.  It can conceptually viewed as a string
9015    breakpoint package.
9016 
9017    Expect rules are stored in tables associated with each port which can use this
9018    facility.  An expect rule consists of a five entry structure:
9019 
9020         match                   the expect match string
9021         size                    the number of bytes in the match string
9022         match_pattern           the expect match string in display format
9023         cnt                     number of iterations before match is declared
9024         action                  command string to be executed when match occurs
9025 
9026    All active expect rules are contained in an expect match context structure.
9027 
9028         rules                   the match rules
9029         size                    the count of match rules
9030         buf                     the buffer of output data which has been produced
9031         buf_ins                 the buffer insertion point for the next output data
9032         buf_size                the buffer size
9033 
9034    The package contains the following public routines:
9035 
9036         sim_set_expect          expect command parser and intializer
9037         sim_set_noexpect        noexpect command parser
9038         sim_exp_set             set or add an expect rule
9039         sim_exp_clr             clear or delete an expect rule
9040         sim_exp_clrall          clear all expect rules
9041         sim_exp_show            show an expect rule
9042         sim_exp_showall         show all expect rules
9043         sim_exp_check           test for rule match
9044 */
9045 
9046 /* Set expect */
9047 
9048 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
9049 {
9050 char gbuf[CBUFSIZE];
9051 CONST char *tptr;
9052 CONST char *c1ptr;
9053 t_bool after_set = FALSE;
9054 uint32 after = exp->after;
9055 int32 cnt = 0;
9056 t_stat r;
9057 
9058 if ((cptr == NULL) || (*cptr == 0))
9059     return SCPE_2FARG;
9060 if (*cptr == '[') {
9061     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9062     if ((cptr == c1ptr) || (*c1ptr != ']'))
9063         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
9064     cptr = c1ptr + 1;
9065     while (sim_isspace(*cptr))
9066         ++cptr;
9067     }
9068 tptr = get_glyph (cptr, gbuf, ',');
9069 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9070     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9071     if (r != SCPE_OK)
9072         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
9073     after_set = TRUE;
9074     cptr = tptr;
9075     }
9076 if ((*cptr != '"') && (*cptr != '\''))
9077     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9078 cptr = get_glyph_quoted (cptr, gbuf, 0);
9079 
9080 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9081 }
9082 
9083 /* Clear expect */
9084 
9085 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
9086 {
9087 char gbuf[CBUFSIZE];
9088 
9089 if (!cptr || !*cptr)
9090     return sim_exp_clrall (exp);                    /* clear all rules */
9091 if ((*cptr != '"') && (*cptr != '\''))
9092     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9093 cptr = get_glyph_quoted (cptr, gbuf, 0);
9094 if (*cptr != '\0')
9095     return SCPE_2MARG;                              /* No more arguments */
9096 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9097 }
9098 
9099 /* Search for an expect rule in an expect context */
9100 
9101 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, int32 start_rule)
9102 {
9103 int32 i;
9104 
9105 if (!exp->rules)
9106     return NULL;
9107 for (i=start_rule; i<exp->size; i++)
9108     if (!strcmp (exp->rules[i].match_pattern, match))
9109         return &exp->rules[i];
9110 return NULL;
9111 }
9112 
9113 /* Clear (delete) an expect rule */
9114 
9115 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
9116 {
9117 int32 i;
9118 
9119 if (!ep)                                                /* not there? ok */
9120     return SCPE_OK;
9121 free (ep->match);                                       /* deallocate match string */
9122 free (ep->match_pattern);                               /* deallocate the display format match string */
9123 free (ep->act);                                         /* deallocate action */
9124 exp->size -= 1;                                         /* decrement count */
9125 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9126     exp->rules[i] = exp->rules[i+1];
9127 if (exp->size == 0) {                                   /* No rules left? */
9128     free (exp->rules);
9129     exp->rules = NULL;
9130     }
9131 return SCPE_OK;
9132 }
9133 
9134 t_stat sim_exp_clr (EXPECT *exp, const char *match)
9135 {
9136 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9137 
9138 while (ep) {
9139     sim_exp_clr_tab (exp, ep);
9140     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9141     }
9142 return SCPE_OK;
9143 }
9144 
9145 /* Clear all expect rules */
9146 
9147 t_stat sim_exp_clrall (EXPECT *exp)
9148 {
9149 int32 i;
9150 
9151 for (i=0; i<exp->size; i++) {
9152     free (exp->rules[i].match);                         /* deallocate match string */
9153     free (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9154     free (exp->rules[i].act);                           /* deallocate action */
9155     }
9156 free (exp->rules);
9157 exp->rules = NULL;
9158 exp->size = 0;
9159 free (exp->buf);
9160 exp->buf = NULL;
9161 exp->buf_size = 0;
9162 exp->buf_ins = 0;
9163 return SCPE_OK;
9164 }
9165 
9166 /* Set/Add an expect rule */
9167 
9168 t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act)
9169 {
9170 EXPTAB *ep;
9171 uint8 *match_buf;
9172 uint32 match_size;
9173 int i;
9174 
9175 /* Validate the match string */
9176 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9177 if (!match_buf)
9178     return SCPE_MEM;
9179 if (switches & EXP_TYP_REGEX) {
9180     free (match_buf);
9181     return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9182     }
9183 else {
9184     if (switches & EXP_TYP_REGEX_I) {
9185         free (match_buf);
9186         return sim_messagef (SCPE_ARG, "Case independed matching is only valid for RegEx expect rules\n");
9187         }
9188     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9189     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9190         free (match_buf);
9191         return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9192         }
9193     }
9194 free (match_buf);
9195 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9196     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9197         (exp->rules[i].switches & EXP_TYP_PERSIST))
9198         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9199     }
9200 if (after && exp->size)
9201     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9202 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9203 ep = &exp->rules[exp->size];
9204 exp->size += 1;
9205 exp->after = after;                                     /* set halt after value */
9206 memset (ep, 0, sizeof(*ep));
9207 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9208 if (ep->match_pattern)
9209     strcpy (ep->match_pattern, match);
9210 ep->cnt = cnt;                                          /* set proceed count */
9211 ep->switches = switches;                                /* set switches */
9212 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9213 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9214     sim_exp_clr_tab (exp, ep);                          /* clear it */
9215     return SCPE_MEM;
9216     }
9217 if (switches & EXP_TYP_REGEX) {
9218     free (match_buf);
9219     match_buf = NULL;
9220     }
9221 else {
9222     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9223     sim_decode_quoted_string (match, match_buf, &match_size);
9224     ep->match = match_buf;
9225     ep->size = match_size;
9226     }
9227 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9228 strcpy (ep->match_pattern, match);
9229 if (ep->act) {                                          /* replace old action? */
9230     free (ep->act);                                     /* deallocate */
9231     ep->act = NULL;                                     /* now no action */
9232     }
9233 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
9234 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9235     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9236     if (newp == NULL)                                   /* mem err? */
9237         return SCPE_MEM;
9238     strcpy (newp, act);                                 /* copy action */
9239     ep->act = newp;                                     /* set pointer */
9240     }
9241 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9242 for (i=0; i<exp->size; i++) {
9243     uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9244     if (compare_size >= exp->buf_size) {
9245         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9246         exp->buf_size = compare_size + 1;
9247         }
9248     }
9249 return SCPE_OK;
9250 }
9251 
9252 /* Show an expect rule */
9253 
9254 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
9255 {
9256 if (!ep)
9257     return SCPE_OK;
9258 fprintf (st, "EXPECT");
9259 if (ep->switches & EXP_TYP_PERSIST)
9260     fprintf (st, " -p");
9261 if (ep->switches & EXP_TYP_CLEARALL)
9262     fprintf (st, " -c");
9263 if (ep->switches & EXP_TYP_REGEX)
9264     fprintf (st, " -r");
9265 if (ep->switches & EXP_TYP_REGEX_I)
9266     fprintf (st, " -i");
9267 fprintf (st, " %s", ep->match_pattern);
9268 if (ep->cnt > 0)
9269     fprintf (st, " [%d]", ep->cnt);
9270 if (ep->act)
9271     fprintf (st, " %s", ep->act);
9272 fprintf (st, "\n");
9273 return SCPE_OK;
9274 }
9275 
9276 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
9277 {
9278 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9279 
9280 if (exp->buf_size) {
9281     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9282 
9283     fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
9284     fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
9285     fprintf (st, "Buffer Contents: %s\n", bstr);
9286     free (bstr);
9287     }
9288 if (exp->after)
9289     fprintf (st, "Halt After: %d instructions\n", exp->after);
9290 if (exp->dptr && exp->dbit)
9291     fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
9292 fprintf (st, "Match Rules:\n");
9293 if (!*match)
9294     return sim_exp_showall (st, exp);
9295 if (!ep) {
9296     fprintf (st, "No Rules match '%s'\n", match);
9297     return SCPE_ARG;
9298     }
9299 do {
9300     sim_exp_show_tab (st, exp, ep);
9301     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9302     } while (ep);
9303 return SCPE_OK;
9304 }
9305 
9306 /* Show all expect rules */
9307 
9308 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
9309 {
9310 int32 i;
9311 
9312 for (i=0; i < exp->size; i++)
9313     sim_exp_show_tab (st, exp, &exp->rules[i]);
9314 return SCPE_OK;
9315 }
9316 
9317 /* Test for expect match */
9318 
9319 t_stat sim_exp_check (EXPECT *exp, uint8 data)
9320 {
9321 int32 i;
9322 EXPTAB *ep = NULL;
9323 int regex_checks = 0;
9324 char *tstr = NULL;
9325 
9326 if ((!exp) || (!exp->rules))                            /* Anying to check? */
9327     return SCPE_OK;
9328 
9329 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
9330 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
9331 
9332 for (i=0; i < exp->size; i++) {
9333     ep = &exp->rules[i];
9334     if (ep->switches & EXP_TYP_REGEX) {
9335         }
9336     else {
9337         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
9338             /*
9339              * First compare the newly deposited data at the beginning
9340              * of buffer with the end of the match string
9341              */
9342             if (exp->buf_ins) {
9343                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9344                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9345                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9346 
9347                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%d]: %s\n", exp->buf_ins, estr);
9348                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9349                     free (estr);
9350                     free (mstr);
9351                     }
9352                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9353                     continue;
9354                 }
9355             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9356                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9357                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9358 
9359                 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_size-(ep->size-exp->buf_ins), ep->size-exp->buf_ins, estr);
9360                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9361                 free (estr);
9362                 free (mstr);
9363                 }
9364             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9365                 continue;
9366             break;
9367             }
9368         else {
9369             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9370                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9371                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9372 
9373                 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_ins-ep->size, ep->size, estr);
9374                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9375                 free (estr);
9376                 free (mstr);
9377                 }
9378             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9379                 continue;
9380             break;
9381             }
9382         }
9383     }
9384 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
9385     if (regex_checks) {
9386         /* When processing regular expressions, let the match buffer fill
9387            up and then shuffle the buffer contents down by half the buffer size
9388            so that the regular expression has a single contiguous buffer to
9389            match against instead of the wrapping buffer paradigm which is
9390            used when no regular expression rules are in effect */
9391         memmove (exp->buf, &exp->buf[exp->buf_size/2], exp->buf_size-(exp->buf_size/2));
9392         exp->buf_ins -= exp->buf_size/2;
9393         sim_debug (exp->dbit, exp->dptr, "Buffer Full - sliding the last %d bytes to start of buffer new insert at: %d\n", (exp->buf_size/2), exp->buf_ins);
9394         }
9395     else {
9396         exp->buf_ins = 0;                               /* wrap around to beginning */
9397         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9398         }
9399     }
9400 if (i != exp->size) {                                   /* Found? */
9401     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern: %s\n", ep->match_pattern);
9402     setenv ("_EXPECT_MATCH_PATTERN", ep->match_pattern, 1);   /* Make the match detail available as an environment variable */
9403     if (ep->cnt > 0) {
9404         ep->cnt -= 1;
9405         sim_debug (exp->dbit, exp->dptr, "Waiting for %d more match%s before stopping\n",
9406                                          ep->cnt, (ep->cnt == 1) ? "" : "es");
9407         }
9408     else {
9409         if (ep->act && *ep->act) {
9410             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
9411             }
9412         else {
9413             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
9414             }
9415         sim_brk_setact (ep->act);                       /* set up actions */
9416         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
9417             sim_exp_clrall (exp);                       /* delete all rules */
9418         else {
9419             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
9420                 sim_exp_clr_tab (exp, ep);              /* delete it */
9421             }
9422         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
9423                       (ep->switches & EXP_TYP_TIME) ?
9424                             (int32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
9425                             exp->after);
9426         }
9427     /* Matched data is no longer available for future matching */
9428     exp->buf_ins = 0;
9429     }
9430 free (tstr);
9431 return SCPE_OK;
9432 }
9433 
9434 /* Queue input data for sending */
9435 
9436 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
9437 {
9438 if (snd->extoff != 0) {
9439     if (snd->insoff-snd->extoff > 0)
9440         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9441     snd->insoff -= snd->extoff;
9442     snd->extoff -= snd->extoff;
9443     }
9444 if (snd->insoff+size > snd->bufsize) {
9445     snd->bufsize = snd->insoff+size;
9446     snd->buffer = (uint8 *)realloc(snd->buffer, snd->bufsize);
9447     }
9448 memcpy(snd->buffer+snd->insoff, data, size);
9449 snd->insoff += size;
9450 if (delay)
9451     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
9452 if (after)
9453     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
9454 if (snd->after == 0)
9455     snd->after = snd->delay;
9456 snd->next_time = sim_gtime() + snd->after;
9457 return SCPE_OK;
9458 }
9459 
9460 /* Cancel Queued input data */
9461 t_stat sim_send_clear (SEND *snd)
9462 {
9463 snd->insoff = 0;
9464 snd->extoff = 0;
9465 return SCPE_OK;
9466 }
9467 
9468 /* Display console Queued input data status */
9469 
9470 t_stat sim_show_send_input (FILE *st, const SEND *snd)
9471 {
9472 if (snd->extoff < snd->insoff) {
9473     fprintf (st, "%d bytes of pending input Data:\n    ", snd->insoff-snd->extoff);
9474     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9475     fprintf (st, "\n");
9476     }
9477 else
9478     fprintf (st, "No Pending Input Data\n");
9479 if ((snd->next_time - sim_gtime()) > 0) {
9480     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
9481         fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
9482         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
9483     else
9484         fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
9485     }
9486 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
9487     fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
9488 else
9489     fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay);
9490 if (snd->dptr && snd->dbit)
9491     fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
9492 return SCPE_OK;
9493 }
9494 
9495 /* Poll for Queued input data */
9496 
9497 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
9498 {
9499 if (snd && (snd->extoff < snd->insoff)) {               /* pending input characters available? */
9500     if (sim_gtime() < snd->next_time) {                 /* too soon? */
9501         *stat = SCPE_OK;
9502         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
9503         }
9504     else {
9505         char dstr[8] = "";
9506         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
9507         snd->next_time = sim_gtime() + snd->delay;
9508         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
9509             sprintf (dstr, " '%c'", *stat & 0xFF);
9510         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
9511         }
9512     return TRUE;
9513     }
9514 return FALSE;
9515 }
9516 
9517 /* Message Text */
9518 
9519 const char *sim_error_text (t_stat stat)
9520 {
9521 static char msgbuf[64];
9522 
9523 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
9524 if (stat == SCPE_OK)
9525     return "No Error";
9526 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
9527     return scp_errors[stat-SCPE_BASE].message;
9528 sprintf(msgbuf, "Error %d", stat);
9529 return msgbuf;
9530 }
9531 
9532 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
9533 {
9534 char gbuf[CBUFSIZE];
9535 int32 cond;
9536 
9537 *stat = SCPE_ARG;
9538 cptr = get_glyph (cptr, gbuf, 0);
9539 if (0 == memcmp("SCPE_", gbuf, 5))
9540     strcpy (gbuf, gbuf+5);   /* skip leading SCPE_ */
9541 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
9542     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
9543         cond += SCPE_BASE;
9544         break;
9545         }
9546 if (0 == strcmp(gbuf, "OK"))
9547     cond = SCPE_OK;
9548 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
9549     if (0 == (cond = strtol(gbuf, NULL, 0)))  /* try explicit number */
9550         return SCPE_ARG;
9551     }
9552 if (cond > SCPE_MAX_ERR)
9553     return SCPE_ARG;
9554 *stat = cond;
9555 return SCPE_OK;
9556 }
9557 
9558 /* Debug printout routines, from Dave Hittner */
9559 
9560 const char* debug_bstates = "01_^";
9561 char debug_line_prefix[256];
9562 int32 debug_unterm  = 0;
9563 
9564 /* Finds debug phrase matching bitmask from from device DEBTAB table */
9565 
9566 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
9567 {
9568 static const char *debtab_none    = "DEBTAB_ISNULL";
9569 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
9570 const char *some_match = NULL;
9571 int32 offset = 0;
9572 
9573 if (dptr->debflags == 0)
9574     return debtab_none;
9575 
9576 dbits &= dptr->dctrl;                           /* Look for just the bits tha matched */
9577 
9578 /* Find matching words for bitmask */
9579 
9580 while (dptr->debflags[offset].name && (offset < 32)) {
9581     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
9582         return dptr->debflags[offset].name;
9583     if (dptr->debflags[offset].mask & dbits)
9584         some_match = dptr->debflags[offset].name;
9585     offset++;
9586     }
9587 return some_match ? some_match : debtab_nomatch;
9588 }
9589 
9590 /* Prints standard debug prefix unless previous call unterminated */
9591 
9592 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
9593 {
9594 const char* debug_type = get_dbg_verb (dbits, dptr);
9595 char tim_t[32] = "";
9596 char tim_a[32] = "";
9597 char pc_s[64] = "";
9598 struct timespec time_now;
9599 
9600 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
9601     clock_gettime(CLOCK_REALTIME, &time_now);
9602     if (sim_deb_switches & SWMASK ('R'))
9603         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
9604     if (sim_deb_switches & SWMASK ('T')) {
9605         time_t tnow = (time_t)time_now.tv_sec;
9606         struct tm *now = gmtime(&tnow);
9607         sprintf(tim_t, "%02d:%02d:%02d.%03d ", now->tm_hour, now->tm_min, now->tm_sec, (int)(time_now.tv_nsec/1000000));
9608         }
9609     if (sim_deb_switches & SWMASK ('A')) {
9610         sprintf(tim_t, "%" LL_FMT "d.%03d ", (long long)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000));
9611         }
9612     }
9613 if (sim_deb_switches & SWMASK ('P')) {
9614     t_value val;
9615 
9616     /* Some simulators expose the PC as a register, some don't expose it or expose a register
9617        which is not a variable which is updated during instruction execution (i.e. only upon
9618        exit of sim_instr()).  For the -P debug option to be effective, such a simulator should
9619        provide a routine which returns the value of the current PC and set the sim_vm_pc_value
9620        routine pointer to that routine.
9621      */
9622     if (sim_vm_pc_value)
9623         val = (*sim_vm_pc_value)();
9624     else
9625         val = get_rval (sim_PC, 0);
9626     sprintf(pc_s, "-%s:", sim_PC->name);
9627     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
9628     }
9629 sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s,  "", dptr->name, debug_type);
9630 return debug_line_prefix;
9631 }
9632 
9633 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
9634 {
9635 int32 i, fields, offset;
9636 uint32 value, beforevalue, mask;
9637 
9638 for (fields=offset=0; bitdefs[fields].name; ++fields) {
9639     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
9640         bitdefs[fields].offset = offset;
9641     offset += bitdefs[fields].width;
9642     }
9643 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
9644     if (bitdefs[i].name[0] == '\0')
9645         continue;
9646     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
9647         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
9648         Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
9649         }
9650     else {
9651         const char *delta = "";
9652         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
9653         value = (uint32)((after >> bitdefs[i].offset) & mask);
9654         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
9655         if (value < beforevalue)
9656             delta = "_";
9657         if (value > beforevalue)
9658             delta = "^";
9659         if (bitdefs[i].valuenames)
9660             Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
9661         else
9662             if (bitdefs[i].format) {
9663                 Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
9664                 Fprintf(stream, bitdefs[i].format, value);
9665                 Fprintf(stream, " ");
9666                 }
9667             else
9668                 Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
9669         }
9670     }
9671 }
9672 
9673 /* Prints state of a register: bit translation + state (0,1,_,^)
9674    indicating the state and transition of the bit and bitfields. States:
9675    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
9676 
9677 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
9678     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
9679 {
9680 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
9681     if (!debug_unterm)
9682         fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
9683     if (header)
9684         fprintf(sim_deb, "%s: ", header);
9685     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
9686     if (terminate)
9687         fprintf(sim_deb, "\r\n");
9688     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
9689     }
9690 }
9691 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
9692     uint32 before, uint32 after, int terminate)
9693 {
9694 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
9695 }
9696 
9697 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9698 void sim_printf (const char* fmt, ...)
9699 {
9700 char stackbuf[STACKBUFSIZE];
9701 int32 bufsize = sizeof(stackbuf);
9702 char *buf = stackbuf;
9703 int32 len;
9704 va_list arglist;
9705 
9706 while (1) {                                         /* format passed string, args */
9707     va_start (arglist, fmt);
9708     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9709     va_end (arglist);
9710 
9711 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9712 
9713     if ((len < 0) || (len >= bufsize-1)) {
9714         if (buf != stackbuf)
9715             free (buf);
9716         bufsize = bufsize * 2;
9717         if (bufsize < len + 2)
9718             bufsize = len + 2;
9719         buf = (char *) malloc (bufsize);
9720         if (buf == NULL)                            /* out of memory */
9721             return;
9722         buf[bufsize-1] = '\0';
9723         continue;
9724         }
9725     break;
9726     }
9727 
9728 if (sim_is_running) {
9729     char *c, *remnant = buf;
9730     while ((c = strchr(remnant, '\n'))) {
9731         if ((c != buf) && (*(c - 1) != '\r'))
9732             printf("%.*s\r\n", (int)(c-remnant), remnant);
9733         else
9734             printf("%.*s\n", (int)(c-remnant), remnant);
9735         remnant = c + 1;
9736         }
9737     printf("%s", remnant);
9738     }
9739 else
9740     printf("%s", buf);
9741 if (sim_log && (sim_log != stdout))
9742     fprintf (sim_log, "%s", buf);
9743 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
9744     fprintf (sim_deb, "%s", buf);
9745 
9746 if (buf != stackbuf)
9747     free (buf);
9748 }
9749 
9750 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9751 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
9752 {
9753 char stackbuf[STACKBUFSIZE];
9754 int32 bufsize = sizeof(stackbuf);
9755 char *buf = stackbuf;
9756 int32 len;
9757 va_list arglist;
9758 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
9759 
9760 while (1) {                                         /* format passed string, args */
9761     va_start (arglist, fmt);
9762     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9763     va_end (arglist);
9764 
9765 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9766 
9767     if ((len < 0) || (len >= bufsize-1)) {
9768         if (buf != stackbuf)
9769             free (buf);
9770         bufsize = bufsize * 2;
9771         if (bufsize < len + 2)
9772             bufsize = len + 2;
9773         buf = (char *) malloc (bufsize);
9774         if (buf == NULL)                            /* out of memory */
9775             return SCPE_MEM;
9776         buf[bufsize-1] = '\0';
9777         continue;
9778         }
9779     break;
9780     }
9781 
9782 if (sim_do_ocptr[sim_do_depth]) {
9783     if (!sim_do_echo && !sim_quiet && !inhibit_message)
9784         sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9785     else {
9786         if (sim_deb)                        /* Always put context in debug output */
9787             fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9788         }
9789     }
9790 if (sim_is_running && !inhibit_message) {
9791     char *c, *remnant = buf;
9792     while ((c = strchr(remnant, '\n'))) {
9793         if ((c != buf) && (*(c - 1) != '\r'))
9794             printf("%.*s\r\n", (int)(c-remnant), remnant);
9795         else
9796             printf("%.*s\n", (int)(c-remnant), remnant);
9797         remnant = c + 1;
9798         }
9799     printf("%s", remnant);
9800     }
9801 else {
9802     if (!inhibit_message)
9803         printf("%s", buf);
9804     }
9805 if (sim_log && (sim_log != stdout) && !inhibit_message)
9806     fprintf (sim_log, "%s", buf);
9807 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
9808     fprintf (sim_deb, "%s", buf);
9809 
9810 if (buf != stackbuf)
9811     free (buf);
9812 return stat | SCPE_NOMESSAGE;
9813 }
9814 
9815 /* Inline debugging - will print debug message if debug file is
9816    set and the bitmask matches the current device debug options.
9817    Extra returns are added for un*x systems, since the output
9818    device is set into 'raw' mode when the cpu is booted,
9819    and the extra returns don't hurt any other systems.
9820    Callers should be calling sim_debug() which is a macro
9821    defined in scp.h which evaluates the action condition before
9822    incurring call overhead. */
9823 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
9824 {
9825 DEVICE *dptr = (DEVICE *)vdptr;
9826 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
9827     char stackbuf[STACKBUFSIZE];
9828     int32 bufsize = sizeof(stackbuf);
9829     char *buf = stackbuf;
9830     va_list arglist;
9831     int32 i, j, len;
9832     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
9833 
9834     buf[bufsize-1] = '\0';
9835     while (1) {                                         /* format passed string, args */
9836         va_start (arglist, fmt);
9837         len = vsnprintf (buf, bufsize-1, fmt, arglist);
9838         va_end (arglist);
9839 
9840 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9841 
9842         if ((len < 0) || (len >= bufsize-1)) {
9843             if (buf != stackbuf)
9844                 free (buf);
9845             bufsize = bufsize * 2;
9846             if (bufsize < len + 2)
9847                 bufsize = len + 2;
9848             buf = (char *) malloc (bufsize);
9849             if (buf == NULL)                            /* out of memory */
9850                 return;
9851             buf[bufsize-1] = '\0';
9852             continue;
9853             }
9854         break;
9855         }
9856 
9857 /* Output the formatted data expanding newlines where they exist */
9858 
9859     for (i = j = 0; i < len; ++i) {
9860         if ('\n' == buf[i]) {
9861             if (i >= j) {
9862                 if ((i != j) || (i == 0)) {
9863                     if (debug_unterm)
9864                         fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
9865                     else                                /* print prefix when required */
9866                         fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
9867                     }
9868                 debug_unterm = 0;
9869                 }
9870             j = i + 1;
9871             }
9872         }
9873     if (i > j) {
9874         if (debug_unterm)
9875             fprintf (sim_deb, "%.*s", i-j, &buf[j]);
9876         else                                        /* print prefix when required */
9877             fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
9878         }
9879 
9880 /* Set unterminated flag for next time */
9881 
9882     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
9883     if (buf != stackbuf)
9884         free (buf);
9885     }
9886 return;
9887 }
9888 void _sim_err (const char* fmt, ...)
9889 {
9890 
9891     char stackbuf[STACKBUFSIZE];
9892     int32 bufsize = sizeof(stackbuf);
9893     char *buf = stackbuf;
9894     va_list arglist;
9895     int32 i, j, len;
9896     //sprintf(debug_line_prefix, "DBG(%lld)> ERR ERR: ", sim_gtime());
9897     sprintf(debug_line_prefix, "DBG(%.0f)> ERR ERR: ", sim_gtime());
9898 
9899     buf[bufsize-1] = '\0';
9900     while (1) {                                         /* format passed string, args */
9901         va_start (arglist, fmt);
9902         len = vsnprintf (buf, bufsize-1, fmt, arglist);
9903         va_end (arglist);
9904 
9905 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9906 
9907         if ((len < 0) || (len >= bufsize-1)) {
9908             if (buf != stackbuf)
9909                 free (buf);
9910             bufsize = bufsize * 2;
9911             buf = (char *) malloc (bufsize);
9912             if (buf == NULL)                            /* out of memory */
9913                 return;
9914             buf[bufsize-1] = '\0';
9915             continue;
9916             }
9917         break;
9918         }
9919 
9920     for (i = j = 0; i < len; ++i) {
9921         if ('\n' == buf[i]) {
9922             if (i >= j) {
9923                 if ((i != j) || (i == 0)) {
9924                     if (debug_unterm)
9925                         fprintf (sim_deb ? sim_deb : stderr, "%.*s\r\n", i-j, &buf[j]);
9926                     else                                /* print prefix when required */
9927                         fprintf (sim_deb ? sim_deb : stderr, "%s%.*s\r\n", debug_line_prefix, i-j, &buf[j]);
9928                     }
9929                 debug_unterm = 0;
9930                 }
9931             j = i + 1;
9932             }
9933         }
9934     if (i > j) {
9935         if (debug_unterm)
9936             fprintf (sim_deb ? sim_deb : stderr, "%.*s", i-j, &buf[j]);
9937         else                                        /* print prefix when required */
9938             fprintf (sim_deb ? sim_deb : stderr, "%s%.*s", debug_line_prefix, i-j, &buf[j]);
9939         }
9940 
9941 /* Set unterminated flag for next time */
9942 
9943     debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1;
9944     if (buf != stackbuf)
9945         free (buf);
9946 return;
9947 }
9948 
9949 void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason)
9950 {
9951 
9952 if (sim_deb && (dptr->dctrl & reason)) {
9953     sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
9954     if (data && len) {
9955         unsigned int i, same, group, sidx, oidx, ridx, eidx, soff;
9956         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
9957         static char hex[] = "0123456789ABCDEF";
9958         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
9959         static unsigned char ebcdic2ascii[] = {
9960             0000,0001,0002,0003,0234,0011,0206,0177,
9961             0227,0215,0216,0013,0014,0015,0016,0017,
9962             0020,0021,0022,0023,0235,0205,0010,0207,
9963             0030,0031,0222,0217,0034,0035,0036,0037,
9964             0200,0201,0202,0203,0204,0012,0027,0033,
9965             0210,0211,0212,0213,0214,0005,0006,0007,
9966             0220,0221,0026,0223,0224,0225,0226,0004,
9967             0230,0231,0232,0233,0024,0025,0236,0032,
9968             0040,0240,0241,0242,0243,0244,0245,0246,
9969             0247,0250,0133,0056,0074,0050,0053,0041,
9970             0046,0251,0252,0253,0254,0255,0256,0257,
9971             0260,0261,0135,0044,0052,0051,0073,0136,
9972             0055,0057,0262,0263,0264,0265,0266,0267,
9973             0270,0271,0174,0054,0045,0137,0076,0077,
9974             0272,0273,0274,0275,0276,0277,0300,0301,
9975             0302,0140,0072,0043,0100,0047,0075,0042,
9976             0303,0141,0142,0143,0144,0145,0146,0147,
9977             0150,0151,0304,0305,0306,0307,0310,0311,
9978             0312,0152,0153,0154,0155,0156,0157,0160,
9979             0161,0162,0313,0314,0315,0316,0317,0320,
9980             0321,0176,0163,0164,0165,0166,0167,0170,
9981             0171,0172,0322,0323,0324,0325,0326,0327,
9982             0330,0331,0332,0333,0334,0335,0336,0337,
9983             0340,0341,0342,0343,0344,0345,0346,0347,
9984             0173,0101,0102,0103,0104,0105,0106,0107,
9985             0110,0111,0350,0351,0352,0353,0354,0355,
9986             0175,0112,0113,0114,0115,0116,0117,0120,
9987             0121,0122,0356,0357,0360,0361,0362,0363,
9988             0134,0237,0123,0124,0125,0126,0127,0130,
9989             0131,0132,0364,0365,0366,0367,0370,0371,
9990             0060,0061,0062,0063,0064,0065,0066,0067,
9991             0070,0071,0372,0373,0374,0375,0376,0377,
9992             };
9993 
9994         for (i=same=0; i<len; i += 16) {
9995             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
9996                 ++same;
9997                 continue;
9998                 }
9999             if (same > 0) {
10000                 sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
10001                 same = 0;
10002                 }
10003             group = (((len - i) > 16) ? 16 : (len - i));
10004             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10005             eidx = strlen(ebcdicbuf);
10006             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10007             ridx = strlen(rad50buf);
10008             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10009             soff = strlen(strbuf);
10010             for (sidx=oidx=0; sidx<group; ++sidx) {
10011                 outbuf[oidx++] = ' ';
10012                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10013                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10014                 if (sim_isprint (data[i+sidx]))
10015                     strbuf[soff+sidx] = data[i+sidx];
10016                 else
10017                     strbuf[soff+sidx] = '.';
10018                 if (ridx && ((sidx&1) == 0)) {
10019                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10020 
10021                     if (word >= 64000) {
10022                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10023                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10024                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10025                         }
10026                     else {
10027                         rad50buf[ridx++] = rad50[word/1600];
10028                         rad50buf[ridx++] = rad50[(word/40)%40];
10029                         rad50buf[ridx++] = rad50[word%40];
10030                         }
10031                     }
10032                 if (eidx) {
10033                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10034                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10035                     else
10036                         ebcdicbuf[eidx++] = '.';
10037                     }
10038                 }
10039             outbuf[oidx] = '\0';
10040             strbuf[soff+sidx] = '\0';
10041             ebcdicbuf[eidx] = '\0';
10042             rad50buf[ridx] = '\0';
10043             sim_debug (reason, dptr, "%04X%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
10044             }
10045         if (same > 0) {
10046             sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), (unsigned int)(len-1));
10047             }
10048         }
10049     }
10050 }
10051 
10052 int Fprintf (FILE *f, const char* fmt, ...)
10053 {
10054 int ret = 0;
10055 va_list args;
10056 
10057 va_start (args, fmt);
10058     ret = vfprintf (f, fmt, args);
10059 va_end (args);
10060 return ret;
10061 }
10062 
10063 /* Hierarchical help presentation
10064  *
10065  * Device help can be presented hierarchically by calling
10066  *
10067  * t_stat scp_help (FILE *st, DEVICE *dptr,
10068  *                  UNIT *uptr, int flag, const char *help, char *cptr)
10069  *
10070  * or one of its three cousins from the device HELP routine.
10071  *
10072  * *help is the pointer to the structured help text to be displayed.
10073  *
10074  * The format and usage, and some helper macros can be found in scp_help.h
10075  * If you don't use the macros, it is not necessary to #include "scp_help.h".
10076  *
10077  * Actually, if you don't specify a DEVICE pointer and don't include
10078  * other device references, it can be used for non-device help.
10079  */
10080 
10081 #define blankch(x) ((x) == ' ' || (x) == '\t')
10082 
10083 typedef struct topic {
10084     uint32         level;
10085     char          *title;
10086     char          *label;
10087     struct topic  *parent;
10088     struct topic **children;
10089     uint32         kids;
10090     char          *text;
10091     size_t         len;
10092     uint32         flags;
10093     uint32         kidwid;
10094 #define HLP_MAGIC_TOPIC  1
10095     } TOPIC;
10096 
10097 static volatile struct {
10098     const char *error;
10099     const char *prox;
10100     size_t block;
10101     size_t line;
10102     } help_where = { "", NULL, 0, 0 };
10103 jmp_buf help_env;
10104 #define FAIL(why,text,here) { help_where.error = #text; help_where.prox = here; longjmp (help_env, (why)); }
10105 
10106 /* Add to topic text.
10107  * Expands text buffer as necessary.
10108  */
10109 
10110 static void appendText (TOPIC *topic, const char *text, size_t len)
10111 {
10112 char *newt;
10113 
10114 if (!len)
10115     return;
10116 
10117 newt = (char *)realloc (topic->text, topic->len + len +1);
10118 if (!newt) {
10119     FAIL (SCPE_MEM, No memory, NULL);
10120     }
10121 topic->text = newt;
10122 memcpy (newt + topic->len, text, len);
10123 topic->len +=len;
10124 newt[topic->len] = '\0';
10125 return;
10126 }
10127 
10128 /* Release memory held by a topic and its children.
10129  */
10130 static void cleanHelp (TOPIC *topic)
10131 {
10132 TOPIC *child;
10133 size_t i;
10134 
10135 free (topic->title);
10136 free (topic->text);
10137 free (topic->label);
10138 for (i = 0; i < topic->kids; i++) {
10139     child = topic->children[i];
10140     cleanHelp (child);
10141     free (child);
10142     }
10143 free (topic->children);
10144 return;
10145 }
10146 
10147 /* Build a help tree from a string.
10148  * Handles substitutions, formatting.
10149  */
10150 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
10151                          UNIT *uptr, const char *htext, va_list ap)
10152 {
10153 char *end;
10154 size_t n, ilvl;
10155 #define VSMAX 100
10156 char *vstrings[VSMAX];
10157 size_t vsnum = 0;
10158 char * astrings[VSMAX+1];
10159 size_t asnum = 0;
10160 char *const *hblock;
10161 const char *ep;
10162 t_bool excluded = FALSE;
10163 
10164 /* variable arguments consumed table.
10165  * The scheme used allows arguments to be accessed in random
10166  * order, but for portability, all arguments must be char *.
10167  * If you try to violate this, there ARE machines that WILL break.
10168  */
10169 
10170 memset (vstrings, 0, sizeof (vstrings));
10171 memset (astrings, 0, sizeof (astrings));
10172 astrings[asnum++] = (char *) htext;
10173 
10174 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10175     help_where.block = hblock - astrings;
10176     help_where.line = 0;
10177     while (*htext) {
10178         const char *start;
10179 
10180         help_where.line++;
10181         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10182             if (excluded) {                     /* Excluded topic text */
10183                 while (*htext && *htext != '\n')
10184                     htext++;
10185                 if (*htext)
10186                     ++htext;
10187                 continue;
10188                 }
10189             ilvl = 1;
10190             appendText (topic, "    ", 4);      /* Basic indentation */
10191             if (*htext == '+') {                /* More for each + */
10192                 while (*htext == '+') {
10193                     ilvl++;
10194                     appendText (topic, "    ", 4);
10195                     htext++;
10196                     }
10197                 }
10198             while (*htext && *htext != '\n' && sim_isspace (*htext))
10199                 htext++;
10200             if (!*htext)                        /* Empty after removing leading spaces */
10201                 break;
10202             start = htext;
10203             while (*htext) {                    /* Process line for substitutions */
10204                 if (*htext == '%') {
10205                     appendText (topic, start, htext - start); /* Flush up to escape */
10206                     switch (*++htext) {         /* Evaluate escape */
10207                         case 'U':
10208                             if (dptr) {
10209                                 char buf[129];
10210                                 n = uptr? uptr - dptr->units: 0;
10211                                 sprintf (buf, "%s%u", dptr->name, (int)n);
10212                                 appendText (topic, buf, strlen (buf));
10213                                 }
10214                             break;
10215                         case 'D':
10216                                 appendText (topic, dptr->name, strlen (dptr->name));
10217                                 break;
10218                         case 'S':
10219                             appendText (topic, sim_name, strlen (sim_name));
10220                             break;
10221                         case '%':
10222                             appendText (topic, "%", 1);
10223                             break;
10224                         case '+':
10225                             appendText (topic, "+", 1);
10226                             break;
10227                         default:                    /* Check for vararg # */
10228                             if (sim_isdigit (*htext)) {
10229                                 n = 0;
10230                                 while (sim_isdigit (*htext))
10231                                     n += (n * 10) + (*htext++ - '0');
10232                                 if (( *htext != 'H' && *htext != 's') ||
10233                                     n == 0 || n >= VSMAX)
10234                                     FAIL (SCPE_ARG, Invalid escape, htext);
10235                                 while (n > vsnum)   /* Get arg pointer if not cached */
10236                                     vstrings[vsnum++] = va_arg (ap, char *);
10237                                 start = vstrings[n-1]; /* Insert selected string */
10238                                 if (*htext == 'H') {   /* Append as more input */
10239                                     if (asnum >= VSMAX) {
10240                                         FAIL (SCPE_ARG, Too many blocks, htext);
10241                                         }
10242                                     astrings[asnum++] = (char *)start;
10243                                     break;
10244                                     }
10245                                 ep = start;
10246                                 while (*ep) {
10247                                     if (*ep == '\n') {
10248                                         ep++;       /* Segment to \n */
10249                                         appendText (topic, start, ep - start);
10250                                         if (*ep) {  /* More past \n, indent */
10251                                             size_t i;
10252                                             for (i = 0; i < ilvl; i++)
10253                                                 appendText (topic, "    ", 4);
10254                                             }
10255                                         start = ep;
10256                                         }
10257                                     else
10258                                         ep++;
10259                                     }
10260                                 appendText (topic, start, ep-start);
10261                                 break;
10262                                 }
10263                             FAIL (SCPE_ARG, Invalid escape, htext);
10264                         } /* switch (escape) */
10265                     start = ++htext;
10266                     continue;                   /* Current line */
10267                     } /* if (escape) */
10268                 if (*htext == '\n') {           /* End of line, append last segment */
10269                     htext++;
10270                     appendText (topic, start, htext - start);
10271                     break;                      /* To next line */
10272                     }
10273                 htext++;                        /* Regular character */
10274                 }
10275             continue;
10276             } /* topic text line */
10277         if (sim_isdigit (*htext)) {             /* Topic heading */
10278             TOPIC **children;
10279             TOPIC *newt;
10280             char nbuf[100];
10281 
10282             n = 0;
10283             start = htext;
10284             while (sim_isdigit (*htext))
10285                 n += (n * 10) + (*htext++ - '0');
10286             if ((htext == start) || !n) {
10287                 FAIL (SCPE_ARG, Invalid topic heading, htext);
10288                 }
10289             if (n <= topic->level) {            /* Find level for new topic */
10290                 while (n <= topic->level)
10291                     topic = topic->parent;
10292                 }
10293             else {
10294                 if (n > topic->level +1) {      /* Skipping down more than 1 */
10295                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10296                     }
10297                 }
10298             while (*htext && (*htext != '\n') && sim_isspace (*htext))
10299                 htext++;
10300             if (!*htext || (*htext == '\n')) {  /* Name missing */
10301                 FAIL (SCPE_ARG, Missing topic name, htext);
10302                 }
10303             start = htext;
10304             while (*htext && (*htext != '\n'))
10305                 htext++;
10306             if (start == htext) {               /* Name NULL */
10307                 FAIL (SCPE_ARG, Null topic name, htext);
10308                 }
10309             excluded = FALSE;
10310             if (*start == '?') {                /* Conditional topic? */
10311                 size_t n = 0;
10312                 start++;
10313                 while (sim_isdigit (*start))    /* Get param # */
10314                     n += (n * 10) + (*start++ - '0');
10315                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX)
10316                     FAIL (SCPE_ARG, Invalid parameter number, start);
10317                 while (n > vsnum)               /* Get arg pointer if not cached */
10318                     vstrings[vsnum++] = va_arg (ap, char *);
10319                 end = vstrings[n-1];            /* Check for True */
10320                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10321                     excluded = TRUE;            /* False, skip topic this time */
10322                     if (*htext)
10323                         htext++;
10324                     continue;
10325                     }
10326                 }
10327             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10328             if (!newt) {
10329                 FAIL (SCPE_MEM, No memory, NULL);
10330                 }
10331             newt->title = (char *) malloc ((htext - start)+1);
10332             if (!newt->title) {
10333                 free (newt);
10334                 FAIL (SCPE_MEM, No memory, NULL);
10335                 }
10336             memcpy (newt->title, start, htext - start);
10337             newt->title[htext - start] = '\0';
10338             if (*htext)
10339                 htext++;
10340 
10341             if (newt->title[0] == '$')
10342                 newt->flags |= HLP_MAGIC_TOPIC;
10343 
10344             children = (TOPIC **) realloc (topic->children,
10345                                            (topic->kids +1) * sizeof (TOPIC *));
10346             if (!children) {
10347                 free (newt->title);
10348                 free (newt);
10349                 FAIL (SCPE_MEM, No memory, NULL);
10350                 }
10351             topic->children = children;
10352             topic->children[topic->kids++] = newt;
10353             newt->level = n;
10354             newt->parent = topic;
10355             n = strlen (newt->title);
10356             if (n > topic->kidwid)
10357                 topic->kidwid = n;
10358             sprintf (nbuf, ".%u", topic->kids);
10359             n = strlen (topic->label) + strlen (nbuf) + 1;
10360             newt->label = (char *) malloc (n);
10361             if (!newt->label) {
10362                 free (newt->title);
10363                 topic->children[topic->kids -1] = NULL;
10364                 free (newt);
10365                 FAIL (SCPE_MEM, No memory, NULL);
10366                 }
10367             sprintf (newt->label, "%s%s", topic->label, nbuf);
10368             topic = newt;
10369             continue;
10370             } /* digits introducing a topic */
10371         if (*htext == ';') {                    /* Comment */
10372             while (*htext && *htext != '\n')
10373                 htext++;
10374             continue;
10375             }
10376         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
10377         } /* htext not at end */
10378     memset (vstrings, 0, VSMAX * sizeof (char *));
10379     vsnum = 0;
10380     } /* all strings */
10381 
10382 return topic;
10383 }
10384 
10385 /* Create prompt string - top thru current topic
10386  * Add prompt at end.
10387  */
10388 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
10389 {
10390 char *prefix;
10391 char *newp, *newt;
10392 
10393 if (topic->level == 0) {
10394     prefix = (char *) calloc (2,1);
10395     if (!prefix) {
10396         FAIL (SCPE_MEM, No memory, NULL);
10397         }
10398     prefix[0] = '\n';
10399     }
10400 else
10401     prefix = helpPrompt (topic->parent, "", oneword);
10402 
10403 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
10404                         strlen (pstring) +1);
10405 if (!newp) {
10406     free (prefix);
10407     FAIL (SCPE_MEM, No memory, NULL);
10408     }
10409 strcpy (newp, prefix);
10410 if (topic->children) {
10411     if (topic->level != 0)
10412         strcat (newp, " ");
10413     newt = (topic->flags & HLP_MAGIC_TOPIC)?
10414             topic->title+1: topic->title;
10415     if (oneword) {
10416         char *np = newp + strlen (newp);
10417         while (*newt) {
10418             *np++ = blankch (*newt)? '_' : *newt;
10419             newt++;
10420             }
10421         *np = '\0';
10422         }
10423     else
10424         strcat (newp, newt);
10425     if (*pstring && *pstring != '?')
10426         strcat (newp, " ");
10427     }
10428 strcat (newp, pstring);
10429 free (prefix);
10430 return newp;
10431 }
10432 
10433 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
10434 {
10435 char tbuf[CBUFSIZE];
10436 size_t i, skiplines;
10437 #ifdef _WIN32
10438 FILE *tmp;
10439 char *tmpnam;
10440 
10441 do {
10442     int fd;
10443     tmpnam = _tempnam (NULL, "simh");
10444     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
10445     if (fd != -1) {
10446         tmp = _fdopen (fd, "w+");
10447         break;
10448         }
10449     } while (1);
10450 #else
10451 FILE *tmp = tmpfile();
10452 #endif
10453 
10454 if (!tmp) {
10455     fprintf (st, "Unable to create temporary file: %s\n", strerror (errno));
10456     return;
10457     }
10458 
10459 if (topic->title)
10460     fprintf (st, "%s\n", topic->title+1);
10461 
10462 skiplines = 0;
10463 if (!strcmp (topic->title+1, "Registers")) {
10464     fprint_reg_help (tmp, dptr) ;
10465     skiplines = 1;
10466     }
10467 else
10468     if (!strcmp (topic->title+1, "Set commands")) {
10469         fprint_set_help (tmp, dptr);
10470         skiplines = 3;
10471         }
10472     else
10473         if (!strcmp (topic->title+1, "Show commands")) {
10474             fprint_show_help (tmp, dptr);
10475             skiplines = 3;
10476             }
10477 rewind (tmp);
10478 
10479 /* Discard leading blank lines/redundant titles */
10480 
10481 for (i =0; i < skiplines; i++)
10482     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
10483 
10484 while (fgets (tbuf, sizeof (tbuf), tmp)) {
10485     if (tbuf[0] != '\n')
10486         fputs ("    ", st);
10487     fputs (tbuf, st);
10488     }
10489 fclose (tmp);
10490 #ifdef _WIN32
10491 remove (tmpnam);
10492 free (tmpnam);
10493 #endif
10494 return;
10495 }
10496 /* Flatten and display help for those who say they prefer it. */
10497 
10498 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
10499                                UNIT *uptr, int32 flag,
10500                                TOPIC *topic, va_list ap )
10501 {
10502 size_t i;
10503 
10504 if (topic->flags & HLP_MAGIC_TOPIC) {
10505     fprintf (st, "\n%s ", topic->label);
10506     displayMagicTopic (st, dptr, topic);
10507     }
10508 else
10509     fprintf (st, "\n%s %s\n", topic->label, topic->title);
10510 
10511 /* Topic text (for magic topics, follows for explanations)
10512  * It's possible/reasonable for a magic topic to have no text.
10513  */
10514 
10515 if (topic->text)
10516     fputs (topic->text, st);
10517 
10518 for (i = 0; i < topic->kids; i++)
10519     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
10520 
10521 return SCPE_OK;
10522 }
10523 
10524 #define HLP_MATCH_AMBIGUOUS (~0u)
10525 #define HLP_MATCH_WILDCARD  (~1U)
10526 #define HLP_MATCH_NONE      0
10527 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
10528 {
10529 size_t i, match;
10530 char cbuf[CBUFSIZE], *cptr;
10531 
10532 if (!strcmp (token, "*"))
10533     return HLP_MATCH_WILDCARD;
10534 
10535 match = 0;
10536 for (i = 0; i < topic->kids; i++) {
10537     strcpy (cbuf,topic->children[i]->title +
10538             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10539     cptr = cbuf;
10540     while (*cptr) {
10541         if (blankch (*cptr)) {
10542             *cptr++ = '_';
10543             }
10544         else {
10545             *cptr = (char)toupper (*cptr);
10546             cptr++;
10547             }
10548         }
10549     if (!strcmp (cbuf, token))      /* Exact Match */
10550         return i+1;
10551     if (!strncmp (cbuf, token, strlen (token))) {
10552         if (match)
10553             return HLP_MATCH_AMBIGUOUS;
10554         match = i+1;
10555         }
10556     }
10557 return match;
10558 }
10559 
10560 /* Main help routine */
10561 
10562 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
10563                   UNIT *uptr, int32 flag,
10564                   const char *help, const char *cptr, va_list ap)
10565 {
10566 
10567 TOPIC top;
10568 TOPIC *topic = &top;
10569 int failed;
10570 size_t match;
10571 size_t i;
10572 const char *p;
10573 t_bool flat_help = FALSE;
10574 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
10575 
10576 static const char attach_help[] = { " ATTACH" };
10577 static const char brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help" };
10578 static const char onecmd_help[] = { "%s help." };
10579 static const char help_help[] = {
10580 
10581     /****|***********************80 column width guide********************************/
10582     "    This help command provides hierarchical help.  To see more information,\n"
10583     "    type an offered subtopic name.  To move back a level, just type <CR>.\n"
10584     "    To review the current topic/subtopic, type \"?\".\n"
10585     "    To view all subtopics, type \"*\".\n"
10586     "    To exit help at any time, type EXIT.\n"
10587     };
10588 
10589 memset (&top, 0, sizeof(top));
10590 top.parent = &top;
10591 if ((failed = setjmp (help_env)) != 0) {
10592     fprintf (stderr, "\nHelp was unable to process the help for this device.\n"
10593                      "Error in block %u line %u: %s\n"
10594                      "%s%*.*s%s"
10595                      " Please contact the device maintainer.\n",
10596              (int)help_where.block, (int)help_where.line, help_where.error,
10597              help_where.prox ? "Near '" : "",
10598              help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
10599              help_where.prox ? help_where.prox : "",
10600                  help_where.prox ? "'" : "");
10601     cleanHelp (&top);
10602     return failed;
10603     }
10604 
10605 /* Compile string into navigation tree */
10606 
10607 /* Root */
10608 
10609 if (dptr) {
10610     p = dptr->name;
10611     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
10612     }
10613 else
10614     p = sim_name;
10615 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
10616 for (i = 0; p[i]; i++ )
10617     top.title[i] = (char)toupper (p[i]);
10618 top.title[i] = '\0';
10619 if (flag & SCP_HELP_ATTACH)
10620     strcpy (top.title+i, attach_help);
10621 
10622 top.label = (char *) malloc (sizeof ("1"));
10623 strcpy (top.label, "1");
10624 
10625 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
10626 
10627 if (flat_help) {
10628     flag |= SCP_HELP_FLAT;
10629     if (sim_ttisatty())
10630         fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
10631     else
10632         fprintf (st, "%s help.\n", top.title);
10633     }
10634 else
10635     fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
10636 
10637 /* Add text and subtopics */
10638 
10639 (void) buildHelp (&top, dptr, uptr, help, ap);
10640 
10641 /* Go to initial topic if provided */
10642 
10643 while (cptr && *cptr) {
10644     cptr = get_glyph (cptr, gbuf, 0);
10645     if (!gbuf[0])
10646         break;
10647     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10648         fprintf (st, "\n");
10649         fputs (help_help, st);
10650         break;
10651         }
10652     match =  matchHelpTopicName (topic, gbuf);
10653     if (match == HLP_MATCH_WILDCARD) {
10654         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10655         cleanHelp (&top);
10656         return SCPE_OK;
10657         }
10658     if (match == HLP_MATCH_AMBIGUOUS) {
10659         fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
10660         break;
10661         }
10662     if (match == HLP_MATCH_NONE) {
10663         fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
10664         break;
10665         }
10666     topic = topic->children[match-1];
10667     }
10668 cptr = NULL;
10669 
10670 if (flat_help) {
10671     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10672     cleanHelp (&top);
10673     return SCPE_OK;
10674     }
10675 
10676 /* Interactive loop displaying help */
10677 
10678 while (TRUE) {
10679     char *pstring;
10680     const char *prompt[2] = {"? ", "Subtopic? "};
10681 
10682     /* Some magic topic names for help from data structures */
10683 
10684     if (topic->flags & HLP_MAGIC_TOPIC) {
10685         fputc ('\n', st);
10686         displayMagicTopic (st, dptr, topic);
10687         }
10688     else
10689         fprintf (st, "\n%s\n", topic->title);
10690 
10691     /* Topic text (for magic topics, follows for explanations)
10692      * It's possible/reasonable for a magic topic to have no text.
10693      */
10694 
10695     if (topic->text)
10696         fputs (topic->text, st);
10697 
10698     if (topic->kids) {
10699         size_t w = 0;
10700         char *p;
10701         char tbuf[CBUFSIZE];
10702 
10703         fprintf (st, "\n    Additional information available:\n\n");
10704         for (i = 0; i < topic->kids; i++) {
10705             strcpy (tbuf, topic->children[i]->title +
10706                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10707             for (p = tbuf; *p; p++) {
10708                 if (blankch (*p))
10709                     *p = '_';
10710                 }
10711             w += 4 + topic->kidwid;
10712             if (w > 80) {
10713                 w = 4 + topic->kidwid;
10714                 fputc ('\n', st);
10715                 }
10716             fprintf (st, "    %-*s", topic->kidwid, tbuf);
10717             }
10718         fprintf (st, "\n\n");
10719         if (flag & SCP_HELP_ONECMD) {
10720             pstring = helpPrompt (topic, "", TRUE);
10721             fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
10722             free (pstring);
10723             break;
10724             }
10725         }
10726 
10727     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
10728         break;
10729 
10730   reprompt:
10731     if (!cptr || !*cptr) {
10732         if (topic->kids == 0)
10733             topic = topic->parent;
10734         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
10735 
10736         cptr = read_line_p (pstring, cbuf, sizeof (cbuf), stdin);
10737         free (pstring);
10738         if ((cptr != NULL) &&                   /* Got something? */
10739             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
10740              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
10741             cptr = NULL;                        /* These are EOF synonyms */
10742         }
10743 
10744     if (!cptr)                              /* EOF, exit help */
10745         break;
10746 
10747     cptr = get_glyph (cptr, gbuf, 0);
10748     if (!strcmp (gbuf, "*")) {              /* Wildcard */
10749         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10750         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
10751         }
10752     if (!gbuf[0]) {                         /* Blank, up a level */
10753         if (topic->level == 0)
10754             break;
10755         topic = topic->parent;
10756         continue;
10757         }
10758     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
10759         continue;
10760     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10761         fputs (help_help, st);
10762         goto reprompt;
10763         }
10764     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
10765         break;
10766 
10767     /* String - look for that topic */
10768 
10769     if (!topic->kids) {
10770         fprintf (st, "No additional help at this level.\n");
10771         cptr = NULL;
10772         goto reprompt;
10773         }
10774     match = matchHelpTopicName (topic, gbuf);
10775     if (match == HLP_MATCH_AMBIGUOUS) {
10776         fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
10777         cptr = NULL;
10778         goto reprompt;
10779         }
10780 
10781     if (match == HLP_MATCH_NONE) {
10782         fprintf (st, "Help for %s is not available\n", gbuf);
10783         cptr = NULL;
10784         goto reprompt;
10785         }
10786     /* Found, display subtopic */
10787 
10788     topic = topic->children[match-1];
10789     }
10790 
10791 /* Free structures and return */
10792 
10793 cleanHelp (&top);
10794 
10795 return SCPE_OK;
10796 }
10797 
10798 /* variable argument list shell - most commonly used */
10799 
10800 t_stat scp_help (FILE *st, DEVICE *dptr,
10801                  UNIT *uptr, int32 flag,
10802                  const char *help, const char *cptr, ...)
10803 {
10804 t_stat r;
10805 va_list ap;
10806 
10807 va_start (ap, cptr);
10808 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
10809 va_end (ap);
10810 
10811 return r;
10812 }
10813 
10814 #if defined(_MSC_VER)
10815 # pragma warning(pop)
10816 #endif
10817