root/src/simh/scp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setenv
  2. unsetenv
  3. trealloc
  4. processIsTranslated
  5. strremove
  6. strtrimspace
  7. allowCores
  8. CleanDUMA
  9. main
  10. process_stdin_commands
  11. set_prompt
  12. find_cmd
  13. exit_cmd
  14. _cmd_name_compare
  15. fprint_help
  16. fprint_header
  17. fprint_reg_help_ex
  18. fprint_reg_help
  19. fprint_attach_help_ex
  20. fprint_set_help_ex
  21. fprint_set_help
  22. fprint_show_help_ex
  23. fprint_show_help
  24. fprint_brk_help_ex
  25. help_dev_help
  26. help_cmd_output
  27. help_cmd
  28. spawn_cmd
  29. echo_cmd
  30. do_cmd
  31. do_position
  32. do_cmd_label
  33. sim_sub_args
  34. sim_cmp_string
  35. assert_cmd
  36. send_cmd
  37. sim_show_send
  38. expect_cmd
  39. sim_show_expect
  40. goto_cmd
  41. return_cmd
  42. shift_cmd
  43. call_cmd
  44. on_cmd
  45. noop_cmd
  46. set_on
  47. set_verify
  48. set_message
  49. set_localopc
  50. set_quiet
  51. sim_set_environment
  52. set_cmd
  53. find_ctab
  54. find_c1tab
  55. set_dev_radix
  56. set_dev_enbdis
  57. set_unit_enbdis
  58. set_dev_debug
  59. show_cmd
  60. show_cmd_fi
  61. find_shtab
  62. show_device
  63. fprint_sep
  64. show_unit
  65. sprint_capac
  66. fprint_capac
  67. show_default_base_system_script
  68. printp
  69. show_prom
  70. show_buildinfo
  71. show_version
  72. show_config
  73. show_log_names
  74. show_dev_logicals
  75. show_queue
  76. show_time
  77. show_break
  78. show_dev_radix
  79. show_dev_debug
  80. show_on
  81. show_mod_names
  82. show_dev_modifiers
  83. show_all_mods
  84. show_one_mod
  85. show_show_commands
  86. show_dev_show_commands
  87. brk_cmd
  88. ssh_break
  89. ssh_break_one
  90. reset_cmd
  91. reset_all
  92. reset_all_p
  93. attach_cmd
  94. scp_attach_unit
  95. attach_unit
  96. attach_err
  97. detach_cmd
  98. detach_all
  99. scp_detach_unit
  100. detach_unit
  101. sim_dname
  102. sim_uname
  103. run_cmd
  104. run_cmd_message
  105. sim_run_boot_prep
  106. fprint_stopped_gen
  107. fprint_stopped
  108. step_svc
  109. expect_svc
  110. int_handler
  111. exdep_cmd
  112. exdep_reg_loop
  113. exdep_addr_loop
  114. ex_reg
  115. get_rval
  116. dep_reg
  117. put_rval
  118. ex_addr
  119. get_aval
  120. dep_addr
  121. eval_cmd
  122. read_line
  123. read_line_p
  124. get_glyph_gen
  125. get_glyph
  126. get_glyph_nc
  127. get_glyph_quoted
  128. get_glyph_cmd
  129. sim_trim_endspc
  130. sim_isspace
  131. sim_islower
  132. sim_isalpha
  133. sim_isprint
  134. sim_isdigit
  135. sim_isgraph
  136. sim_isalnum
  137. get_uint
  138. get_range
  139. sim_decode_quoted_string
  140. sim_encode_quoted_string
  141. fprint_buffer_string
  142. find_dev
  143. find_unit
  144. sim_register_internal_device
  145. find_dev_from_unit
  146. qdisable
  147. find_reg_glob
  148. find_reg
  149. get_switches
  150. get_sim_sw
  151. get_sim_opt
  152. put_switches
  153. get_rsearch
  154. get_asearch
  155. test_search
  156. strtotv
  157. sprint_val
  158. fprint_val
  159. sim_fmt_secs
  160. sim_process_event
  161. sim_activate
  162. _sim_activate
  163. sim_activate_abs
  164. sim_activate_after
  165. _sim_activate_after
  166. sim_cancel
  167. sim_is_active
  168. sim_activate_time
  169. sim_gtime
  170. sim_qcount
  171. sim_brk_init
  172. sim_brk_fnd
  173. sim_brk_fnd_ex
  174. sim_brk_new
  175. sim_brk_set
  176. sim_brk_clr
  177. sim_brk_clrall
  178. sim_brk_show
  179. sim_brk_showall
  180. sim_brk_test
  181. sim_brk_getact
  182. sim_brk_clract
  183. sim_brk_setact
  184. sim_brk_npc
  185. sim_brk_clrspc
  186. sim_brk_message
  187. sim_set_expect
  188. sim_set_noexpect
  189. sim_exp_fnd
  190. sim_exp_clr_tab
  191. sim_exp_clr
  192. sim_exp_clrall
  193. sim_exp_set
  194. sim_exp_show_tab
  195. sim_exp_show
  196. sim_exp_showall
  197. sim_exp_check
  198. sim_send_input
  199. sim_send_clear
  200. sim_show_send_input
  201. sim_send_poll_data
  202. sim_error_text
  203. sim_string_to_stat
  204. get_dbg_verb
  205. sim_debug_prefix
  206. fprint_fields
  207. sim_debug_bits_hdr
  208. sim_debug_bits
  209. sim_printf
  210. sim_messagef
  211. _sim_debug
  212. sim_data_trace
  213. Fprintf
  214. appendText
  215. cleanHelp
  216. buildHelp
  217. helpPrompt
  218. displayMagicTopic
  219. displayFlatHelp
  220. matchHelpTopicName
  221. scp_vhelp
  222. scp_help

   1 /*
   2  * scp.c: simulator control program
   3  *
   4  * vim: filetype=c:tabstop=4:ai:colorcolumn=84:expandtab
   5  * SPDX-License-Identifier: X11
   6  * scspell-id: 7cde852c-f62a-11ec-8444-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2022 Robert M. Supnik
  11  * Copyright (c) 2006-2022 The DPS8M Development Team
  12  * Copyright (c) 2021-2022 Jeffrey H. Johnson <trnsz@pobox.com>
  13  *
  14  * Permission is hereby granted, free of charge, to any person obtaining a
  15  * copy of this software and associated documentation files (the "Software"),
  16  * to deal in the Software without restriction, including without limitation
  17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  18  * and/or sell copies of the Software, and to permit persons to whom the
  19  * Software is furnished to do so, subject to the following conditions:
  20  *
  21  * The above copyright notice and this permission notice shall be included in
  22  * all copies or substantial portions of the Software.
  23  *
  24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  27  * ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  28  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  29  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30  * SOFTWARE.
  31  *
  32  * Except as contained in this notice, the name of Robert M. Supnik shall not
  33  * be used in advertising or otherwise to promote the sale, use or other
  34  * dealings in this Software without prior written authorization from
  35  * Robert M. Supnik.
  36  *
  37  * ---------------------------------------------------------------------------
  38  */
  39 
  40 //-V::701
  41 
  42 /* Macros and data structures */
  43 
  44 #include "sim_defs.h"
  45 #include "sim_disk.h"
  46 #include "sim_tape.h"
  47 #include "sim_sock.h"
  48 #include <signal.h>
  49 #include <ctype.h>
  50 #include <time.h>
  51 #include <math.h>
  52 #if defined(_WIN32)
  53 # ifndef WIN32_LEAN_AND_MEAN
  54 #  define WIN32_LEAN_AND_MEAN
  55 # endif
  56 # if defined(_MSC_VER)
  57 #  pragma warning(push, 3)
  58 # endif
  59 # include <direct.h>
  60 # include <io.h>
  61 # include <fcntl.h>
  62 #else
  63 # include <unistd.h>
  64 # define HAVE_UNISTD 1
  65 #endif
  66 #include <sys/stat.h>
  67 #include <sys/types.h>
  68 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  69 # include <sys/resource.h>
  70 #endif /* if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
  71 #include <setjmp.h>
  72 #include <limits.h>
  73 #include <locale.h>
  74 
  75 #include "linehistory.h"
  76 
  77 #if defined(__APPLE__)
  78 # include <sys/sysctl.h>
  79 #endif
  80 
  81 #if ( defined(__linux__) || defined(__linux) \
  82   ||  defined(_linux)    || defined(linux) )
  83 # include <sys/sysinfo.h>
  84 # define LINUX_OS
  85 #endif
  86 
  87 #include <uv.h>
  88 
  89 #ifndef HAVE_UNISTD
  90 # undef USE_BACKTRACE
  91 #endif
  92 
  93 #ifdef USE_BACKTRACE
  94 # include <string.h>
  95 # include <signal.h>
  96 #endif
  97 
  98 #ifdef __HAIKU__
  99 # include <OS.h>
 100 #endif /* ifdef __HAIKU__ */
 101 
 102 #define DBG_CTR 0
 103 
 104 #include "../dps8/dps8.h"
 105 #include "../dps8/dps8_cpu.h"
 106 #include "../dps8/ver.h"
 107 #include "../dps8/sysdefs.h"
 108 
 109 #include "../dps8/dps8_iom.h"
 110 #include "../dps8/dps8_fnp2.h"
 111 
 112 #include "../decNumber/decContext.h"
 113 #include "../decNumber/decNumberLocal.h"
 114 
 115 #include "../dps8/dps8_math128.h"
 116 
 117 #include "dispatch.h"
 118 
 119 #ifndef MAX
 120 # define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
 121 #endif
 122 
 123 #ifdef TESTING
 124 # undef FREE
 125 # define FREE(p) free(p)
 126 #endif /* ifdef TESTING */
 127 
 128 /* search logical and boolean ops */
 129 
 130 #define SCH_OR          0                               /* search logicals */
 131 #define SCH_AND         1
 132 #define SCH_XOR         2
 133 #define SCH_E           0                               /* search booleans */
 134 #define SCH_N           1
 135 #define SCH_G           2
 136 #define SCH_L           3
 137 #define SCH_EE          4
 138 #define SCH_NE          5
 139 #define SCH_GE          6
 140 #define SCH_LE          7
 141 
 142 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
 143 #define SRBSIZ          1024                            /* save/restore buffer */
 144 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
 145 #define SIM_BRK_ALLTYP  0xFFFFFFFB
 146 
 147 #define UPDATE_SIM_TIME                                         \
 148     if (1) {                                                    \
 149         int32 _x;                                               \
 150         if (sim_clock_queue == QUEUE_LIST_END)                  \
 151             _x = noqueue_time;                                  \
 152         else                                                    \
 153             _x = sim_clock_queue->time;                         \
 154         sim_time = sim_time + (_x - sim_interval);              \
 155         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
 156         if (sim_clock_queue == QUEUE_LIST_END)                  \
 157             noqueue_time = sim_interval;                        \
 158         else                                                    \
 159             sim_clock_queue->time = sim_interval;               \
 160         }                                                       \
 161     else                                                        \
 162         (void)0
 163 
 164 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
 165 
 166 #define SZ_R(rp) \
 167     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
 168 
 169 #define SZ_LOAD(sz,v,mb,j)                                                 \
 170     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j));        \
 171     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
 172     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
 173     else v = *(((t_uint64 *) mb) + ((uint32) j));
 174 
 175 #define SZ_STORE(sz,v,mb,j)                                                         \
 176     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v;                    \
 177     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
 178     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
 179     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
 180 
 181 #define GET_SWITCHES(cp) \
 182     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
 183 
 184 #define GET_RADIX(val,dft)                          \
 185     if (sim_switches & SWMASK ('O')) val = 8;       \
 186     else if (sim_switches & SWMASK ('D')) val = 10; \
 187     else if (sim_switches & SWMASK ('H')) val = 16; \
 188     else val = dft;
 189 
 190 /*
 191  * The per-simulator init routine is a weak global that defaults to NULL
 192  * The other per-simulator pointers can be overridden by the init routine
 193  */
 194 
 195 t_bool sim_asynch_enabled = FALSE;
 196 t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
 197 t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
 198 extern void (*sim_vm_init) (void);
 199 extern void (*sim_vm_exit) (void);
 200 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
 201 void (*sim_vm_post) (t_bool from_scp) = NULL;
 202 CTAB *sim_vm_cmd = NULL;
 203 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
 204 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
 205 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
 206 t_value (*sim_vm_pc_value) (void) = NULL;
 207 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
 208 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
 209 
 210 /* Prototypes */
 211 
 212 /* Set and show command processors */
 213 
 214 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 215 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 216 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 217 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 218 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
 219 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
 220 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 221 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 222 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 223 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 224 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 225 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 226 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 227 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 228 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 229 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 230 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 231 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 232 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
 233 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 234 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 235 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 236 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 237 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 238 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 239 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 240 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
 241 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
 242 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
 243 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
 244 t_stat sim_save (FILE *sfile);
 245 t_stat sim_rest (FILE *rfile);
 246 
 247 /* Breakpoint package */
 248 
 249 t_stat sim_brk_init (void);
 250 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
 251 t_stat sim_brk_clr (t_addr loc, int32 sw);
 252 t_stat sim_brk_clrall (int32 sw);
 253 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
 254 t_stat sim_brk_showall (FILE *st, int32 sw);
 255 CONST char *sim_brk_getact (char *buf, int32 size);
 256 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
 257 char *sim_brk_clract (void);
 258 
 259 FILE *stdnul;
 260 
 261 /* Command support routines */
 262 
 263 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 264 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 265 int32 test_search (t_value *val, SCHTAB *schptr);
 266 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
 267 int32 get_switches (const char *cptr);
 268 CONST char *get_sim_sw (CONST char *cptr);
 269 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
 270 t_value get_rval (REG *rptr, uint32 idx);
 271 void put_rval (REG *rptr, uint32 idx, t_value val);
 272 void fprint_help (FILE *st);
 273 void fprint_stopped (FILE *st, t_stat r);
 274 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
 275 void fprint_sep (FILE *st, int32 *tokens);
 276 char *read_line (char *ptr, int32 size, FILE *stream);
 277 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
 278 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
 279 char *sim_trim_endspc (char *cptr);
 280 
 281 /* Forward references */
 282 
 283 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
 284 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
 285 t_bool qdisable (DEVICE *dptr);
 286 t_stat attach_err (UNIT *uptr, t_stat stat);
 287 t_stat detach_all (int32 start_device, t_bool shutdown);
 288 t_stat assign_device (DEVICE *dptr, const char *cptr);
 289 t_stat deassign_device (DEVICE *dptr);
 290 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
 291 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
 292     REG *lowr, REG *highr, uint32 lows, uint32 highs);
 293 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
 294 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
 295 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
 296     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
 297 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
 298 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
 299     UNIT *uptr, int32 dfltinc);
 300 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
 301 t_stat step_svc (UNIT *ptr);
 302 t_stat expect_svc (UNIT *ptr);
 303 t_stat set_on (int32 flag, CONST char *cptr);
 304 t_stat set_verify (int32 flag, CONST char *cptr);
 305 t_stat set_message (int32 flag, CONST char *cptr);
 306 t_stat set_quiet (int32 flag, CONST char *cptr);
 307 t_stat set_localopc (int32 flag, CONST char *cptr);
 308 t_stat set_asynch (int32 flag, CONST char *cptr);
 309 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 310 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
 311 void int_handler (int signal);
 312 t_stat set_prompt (int32 flag, CONST char *cptr);
 313 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
 314 t_stat sim_set_environment (int32 flag, CONST char *cptr);
 315 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
 316 
 317 /* Global data */
 318 
 319 DEVICE *sim_dflt_dev             = NULL;
 320 UNIT *sim_clock_queue            = QUEUE_LIST_END;
 321 int32 sim_interval               = 0;
 322 int32 sim_switches               = 0;
 323 FILE *sim_ofile                  = NULL;
 324 SCHTAB *sim_schrptr              = FALSE;
 325 SCHTAB *sim_schaptr              = FALSE;
 326 DEVICE *sim_dfdev                = NULL;
 327 UNIT *sim_dfunit                 = NULL;
 328 DEVICE **sim_internal_devices    = NULL;
 329 uint32 sim_internal_device_count = 0;
 330 int32 sim_opt_out                = 0;
 331 int32 sim_is_running             = 0;
 332 t_bool sim_processing_event      = FALSE;
 333 uint32 sim_brk_summ              = 0;
 334 uint32 sim_brk_types             = 0;
 335 BRKTYPTAB *sim_brk_type_desc     = NULL;         /* type descriptions */
 336 uint32 sim_brk_dflt              = 0;
 337 uint32 sim_brk_match_type;
 338 t_addr sim_brk_match_addr;
 339 char *sim_brk_act[MAX_DO_NEST_LVL];
 340 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
 341 BRKTAB **sim_brk_tab             = NULL;
 342 int32 sim_brk_ent                = 0;
 343 int32 sim_brk_lnt                = 0;
 344 int32 sim_brk_ins                = 0;
 345 int32 sim_iglock                 = 0;
 346 int32 sim_nolock                 = 0;
 347 int32 sim_quiet                  = 0;
 348 int32 sim_localopc               = 1;
 349 int32 sim_randompst              = 0;
 350 int32 sim_randstate              = 0;
 351 int32 sim_step                   = 0;
 352 int nodist                       = 0;
 353 #ifdef PERF_STRIP
 354 int32 sim_nostate                = 1;
 355 #else
 356 int32 sim_nostate                = 0;
 357 #endif /* ifndef PERF_STRIP */
 358 static double sim_time;
 359 static uint32 sim_rtime;
 360 static int32 noqueue_time;
 361 volatile int32 stop_cpu          = 0;
 362 t_value *sim_eval                = NULL;
 363 static t_value sim_last_val;
 364 static t_addr sim_last_addr;
 365 FILE *sim_log                    = NULL;         /* log file */
 366 FILEREF *sim_log_ref             = NULL;         /* log file file reference */
 367 FILE *sim_deb                    = NULL;         /* debug file */
 368 FILEREF *sim_deb_ref             = NULL;         /* debug file file reference */
 369 int32 sim_deb_switches           = 0;            /* debug switches */
 370 struct timespec sim_deb_basetime;                /* debug timestamp relative base time */
 371 char *sim_prompt                 = NULL;         /* prompt string */
 372 static FILE *sim_gotofile;                       /* the currently open do file */
 373 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];   /* the current line number in the currently open do file */
 374 static int32 sim_do_echo         = 0;            /* the echo status of the currently open do file */
 375 static int32 sim_show_message    = 1;            /* the message display status of the currently open do file */
 376 static int32 sim_on_inherit      = 0;            /* the inherit status of on state and conditions when executing do files */
 377 static int32 sim_do_depth        = 0;
 378 
 379 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
 380 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
 381 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
 382 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
 383 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
 384 
 385 t_stat sim_last_cmd_stat;                        /* Command Status */
 386 
 387 static SCHTAB sim_stabr;                         /* Register search specifier */
 388 static SCHTAB sim_staba;                         /* Memory search specifier */
 389 
 390 static UNIT sim_step_unit   = { UDATA (&step_svc, 0, 0)  };
 391 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
 392 
 393 /* Tables and strings */
 394 
 395 const char save_vercur[] = "V4.1";
 396 const char save_ver40[] = "V4.0";
 397 const char save_ver35[] = "V3.5";
 398 const char save_ver32[] = "V3.2";
 399 const char save_ver30[] = "V3.0";
 400 const struct scp_error {
 401     const char *code;
 402     const char *message;
 403     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
 404         {{"NXM",     "Address space exceeded"},
 405          {"UNATT",   "Unit not attached"},
 406          {"IOERR",   "I/O error"},
 407          {"CSUM",    "Checksum error"},
 408          {"FMT",     "Format error"},
 409          {"NOATT",   "Unit not attachable"},
 410          {"OPENERR", "File open error"},
 411          {"MEM",     "Memory exhausted"},
 412          {"ARG",     "Invalid argument"},
 413          {"STEP",    "Step expired"},
 414          {"UNK",     "Unknown command"},
 415          {"RO",      "Read only argument"},
 416          {"INCOMP",  "Command not completed"},
 417          {"STOP",    "Simulation stopped"},
 418          {"EXIT",    "Goodbye"},
 419          {"TTIERR",  "Console input I/O error"},
 420          {"TTOERR",  "Console output I/O error"},
 421          {"EOF",     "End of file"},
 422          {"REL",     "Relocation error"},
 423          {"NOPARAM", "No settable parameters"},
 424          {"ALATT",   "Unit already attached"},
 425          {"TIMER",   "Hardware timer error"},
 426          {"SIGERR",  "Signal handler setup error"},
 427          {"TTYERR",  "Console terminal setup error"},
 428          {"SUB",     "Subscript out of range"},
 429          {"NOFNC",   "Command not allowed"},
 430          {"UDIS",    "Unit disabled"},
 431          {"NORO",    "Read only operation not allowed"},
 432          {"INVSW",   "Invalid switch"},
 433          {"MISVAL",  "Missing value"},
 434          {"2FARG",   "Too few arguments"},
 435          {"2MARG",   "Too many arguments"},
 436          {"NXDEV",   "Non-existent device"},
 437          {"NXUN",    "Non-existent unit"},
 438          {"NXREG",   "Non-existent register"},
 439          {"NXPAR",   "Non-existent parameter"},
 440          {"NEST",    "Nested DO command limit exceeded"},
 441          {"IERR",    "Internal error"},
 442          {"MTRLNT",  "Invalid magtape record length"},
 443          {"LOST",    "Console Telnet connection lost"},
 444          {"TTMO",    "Console Telnet connection timed out"},
 445          {"STALL",   "Console Telnet output stall"},
 446          {"AFAIL",   "Assertion failed"},
 447          {"INVREM",  "Invalid remote console command"},
 448          {"NOTATT",  "Not attached"},
 449          {"EXPECT",  "Expect matched"},
 450          {"REMOTE",  "Remote console command"},
 451     };
 452 
 453 const size_t size_map[] = { sizeof (int8),
 454     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
 455     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
 456 };
 457 
 458 const t_value width_mask[] = { 0,
 459     0x1, 0x3, 0x7, 0xF,
 460     0x1F, 0x3F, 0x7F, 0xFF,
 461     0x1FF, 0x3FF, 0x7FF, 0xFFF,
 462     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
 463     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
 464     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
 465     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
 466     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
 467     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
 468     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
 469     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
 470     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
 471     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
 472     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
 473     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
 474     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
 475     };
 476 
 477 static const char simh_help[] =
 478        /***************** 80 character line width template *************************/
 479       "1Commands\n"
 480 #define HLP_RESET       "*Commands Resetting Devices"
 481        /***************** 80 character line width template *************************/
 482       "2Resetting Devices\n"
 483       " The `RESET` command (*abbreviated* `RE`) resets a device or the entire\n"
 484       " simulator to a predefined condition.  If the switch \"`-p`\" is specified,\n"
 485       " the device is reset to its initial power-on state:\n\n"
 486       "++RESET                  resets all devices\n"
 487       "++RESET -p               power-cycle all devices\n"
 488       "++RESET ALL              resets all devices\n"
 489       "++RESET <device>         resets the specified <device>\n\n"
 490       " * Typically, `RESET` *aborts* in-progress I/O operations, *clears* any\n"
 491       " interrupt requests, and returns the device to a quiescent state.\n\n"
 492       " * It does **NOT** clear the main memory or affect associated I/O\n"
 493       " connections.\n"
 494 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
 495 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
 496 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
 497 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
 498        /***************** 80 character line width template *************************/
 499       "2Examining and Changing State\n"
 500       " There are four commands to examine and change state:\n\n"
 501       " * `EXAMINE` (*abbreviated* `E`) examines state\n"
 502       " * `DEPOSIT` (*abbreviated* `D`) changes state\n"
 503       " * `IEXAMINE` (\"interactive examine\", *abbreviated* `IE`) examines state\n"
 504       "    and allows the user to interactively change it\n"
 505       " * `IDEPOSIT` (interactive deposit, *abbreviated* `ID`) allows the user to\n"
 506       "    interactively change state\n\n"
 507       " All four commands take the form:\n\n"
 508       "++command {modifiers} <object list>\n\n"
 509       " The `DEPOSIT` command requires the deposit value at the end of the command.\n\n"
 510       " There are four kinds of modifiers: **switches**, **device/unit name**,\n"
 511       " **search specifier**, and for `EXAMINE`, **output file**.\n\n"
 512       " * **Switches** have been described previously.\n"
 513       " * A **device/unit name** identifies the device and unit whose address\n"
 514       " space is to be examined or modified. If no device is specified, the CPU\n"
 515       " main memory is selected. If a device but no unit is specified, unit `0`\n"
 516       " of the specified device is selected automatically.\n"
 517       " * The **search specifier** provides criteria for testing addresses or\n"
 518       " registers to see if they should be processed.  The search specifier\n"
 519       " consists of a \"<`logical operator`>\", a \"<`relational operator`>\", or\n"
 520       " both, optionally separated by spaces:\n\n"
 521       "++{ < logical op >  < value > }  < relational op >  < value >\n\n"
 522        /***************** 80 character line width template *************************/
 523       " * * The \"<`logical operator`>\" may be \"`&`\" (*and*), \"`|`\" (*or*),\n"
 524       " or \"`^`\" (*exclusive or*), and the \"<`relational operator`>\" may\n"
 525       " be \"`=`\" or \"`==`\" (*equal*), \"`!`\" or \"`!=`\" (*not\n"
 526       " equal*), \">=\" (*greater than or equal*), \">\" (*greater\n"
 527       " than*), \"<=\" (*less than or equal*), or \"<\" (*less than*).\n"
 528       " * * If any \"<`logical operator`>\" is specified without\n"
 529       " a \"<`relational operator`>\", it is ignored.\n"
 530       " * * If any \"<`relational operator`>\" is specified without\n"
 531       " a \"<`logical operator`>\", no logical operation is performed.\n"
 532       " * * All comparisons are unsigned.\n\n"
 533       " * The **output file** modifier redirects the command output to a file\n"
 534       " instead of the console.  The **output file** modifier is specified with\n"
 535       " the \"`@`\" (*commercial-at*) character, followed by a valid file name.\n\n"
 536       " **NOTE**: Modifiers may be specified in any order.  If multiple\n"
 537       " modifiers of the same type are specified, later modifiers override earlier\n"
 538       " modifiers. If the **device/unit name** comes *after* the search specifier,\n"
 539       " the search values will interpreted in the *radix of the CPU*, rather than\n"
 540       " of the device/unit.\n\n"
 541       " The \"<`object list`>\" argument consists of one or more of the following,\n"
 542       " separated by commas:\n\n"
 543        /***************** 80 character line width template *************************/
 544       "++register                the specified register\n"
 545       "++register[sub1-sub2]     the specified register array locations,\n"
 546       "++++++++                  starting at location sub1 up to and\n"
 547       "++++++++                  including location sub2\n"
 548       "++register[sub1/length]   the specified register array locations,\n"
 549       "++++++++                  starting at location sub1 up to but\n"
 550       "++++++++                  not including sub1+length\n"
 551       "++register[ALL]           all locations in the specified register\n"
 552       "++++++++                  array\n"
 553       "++register1-register2     all the registers starting at register1\n"
 554       "++++++++                  up to and including register2\n"
 555       "++address                 the specified location\n"
 556       "++address1-address2       all locations starting at address1 up to\n"
 557       "++++++++                  and including address2\n"
 558       "++address/length          all location starting at address up to\n"
 559       "++++++++                  but not including address+length\n"
 560       "++STATE                   all registers in the device\n"
 561       "++ALL                     all locations in the unit\n"
 562       "++$                       the last value displayed by an EXAMINE\n"
 563       "++++++++                  command interpreted as an address\n"
 564       "3Switches\n"
 565       "4Formatting Control\n"
 566       " Switches can be used to control the format of the displayed information:\n\n"
 567        /***************** 80 character line width template *************************/
 568       "5-a\n"
 569       " display as ASCII\n"
 570       "5-c\n"
 571       " display as character string\n"
 572       "5-m\n"
 573       " display as instruction mnemonics\n"
 574       "5-o\n"
 575       " display as octal\n"
 576       "5-d\n"
 577       " display as decimal\n"
 578       "5-h\n"
 579       " display as hexadecimal\n\n"
 580       "3Examples\n"
 581       "++ex 1000-1100                examine 1000 to 1100\n"
 582       "++de PC 1040                  set PC to 1040\n"
 583       "++ie 40-50                    interactively examine 40:50\n"
 584       "++ie >1000 40-50              interactively examine the subset\n"
 585       "+++++++++                     of locations 40:50 that are >1000\n"
 586       "++ex rx0 50060                examine 50060, RX unit 0\n"
 587       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\n"
 588       "++de all 0                    set main memory to 0\n"
 589       "++de &77>0 0                  set all addresses whose low order\n"
 590       "+++++++++                     bits are non-zero to 0\n"
 591       "++ex -m @memdump.txt 0-7777   dump memory to file\n\n"
 592       " * **NOTE**: To terminate an interactive command, simply type any bad value\n"
 593       "           (*e.g.* `XYZ`) when input is requested.\n"
 594 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
 595        /***************** 80 character line width template *************************/
 596       "2Evaluating Instructions\n"
 597       " The `EVAL` command evaluates a symbolic expression and returns the\n"
 598       " equivalent numeric value.\n\n"
 599        /***************** 80 character line width template *************************/
 600       "2Running A Simulated Program\n"
 601 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
 602       "3RUN\n"
 603       " The `RUN` command (*abbreviated* `RU`) resets all devices, deposits its\n"
 604       " argument, if given, in the PC (program counter), and starts execution.\n"
 605       " If no argument is given execution starts at the current PC.\n"
 606 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
 607       "3GO\n"
 608       " The `GO` command does *not* reset devices, deposits its argument (if\n"
 609       " given) in the PC, and starts execution.  If no argument is given,\n"
 610       " execution starts at the current PC (program counter).\n"
 611 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program Continuing_Execution"
 612       "3Continuing Execution\n"
 613       " The `CONTINUE` command (*abbreviated* `CONT` or `CO`) resumes execution\n"
 614       " (if execution was stopped, possibly due to hitting a breakpoint) at the\n"
 615       " current program counter without resetting any devices.\n"
 616 #define HLP_STEP        "*Commands Running_A_Simulated_Program Step_Execution"
 617       "3Step Execution\n"
 618       " The `STEP` command (*abbreviated* `S`) resumes execution at the current\n"
 619       " PC for the number of instructions given by its argument.  If no argument\n"
 620       " is supplied, one instruction is executed.\n"
 621       "4Switches\n"
 622       "5`-T`\n"
 623       " If the `STEP` command is invoked with the \"`-T`\" switch, the step\n"
 624       " command will cause execution to run for *microseconds* rather than\n"
 625       " instructions.\n"
 626 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
 627       "3NEXT\n"
 628       " The `NEXT` command (*abbreviated* `N`) resumes execution at the current PC\n"
 629       " for one instruction, attempting to execute *through* subroutine calls.\n"
 630       " If the next instruction to be executed is *not* a subroutine call, then\n"
 631       " one instruction is executed.\n"
 632 #define HLP_BOOT        "*Commands Running_A_Simulated_Program Booting_the_system"
 633       "3Booting the system\n"
 634       " The `BOOT` command (*abbreviated* `BO`) resets all devices and bootstraps\n"
 635       " the device and unit given by its argument. If no unit is supplied,\n"
 636       " unit `0` is bootstrapped.  The specified unit must be `ATTACH`'ed.\n\n"
 637       " When booting Multics, the boot device should always be `iom0`.\n"
 638       " Assuming a tape is attached to the `tape0` device, it will be bootstrapped\n"
 639       " into memory and the system will transfer control to the boot record.\n\n"
 640       " **Example**\n\n"
 641       "++; Boot Multics using iom0\n"
 642       "++boot iom0\n\n"
 643        /***************** 80 character line width template *************************/
 644       "2Stopping The Simulator\n"
 645       " The simulator runs until the simulated hardware encounters an error, or\n"
 646       " until the user forces a stop condition.\n"
 647       "3Simulator Detected Stop Conditions\n"
 648       " These simulator-detected conditions stop simulation:\n\n"
 649       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\n\n"
 650       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\n"
 651       "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
 652       "+++usually stops.\n\n"
 653       "++-  Processor condition.  Certain processor conditions can stop\n"
 654       "+++the simulation.\n"
 655       "3User Specified Stop Conditions\n"
 656       " Typing the interrupt character stops simulation.  The interrupt character\n"
 657       " is defined by the `WRU` (*Where aRe yoU*) console option, and is initially\n"
 658       " set to `005` (`^E`).\n\n"
 659        /***************** 80 character line width template *************************/
 660 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 661 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 662       "4Breakpoints\n"
 663       " The simulator offers breakpoint capability for debugging. Users may define\n"
 664       " breakpoints of different types, identified by letter (for example, `E`\n"
 665       " for *execution*, `R` for *read*, `W` for *write*, etc).\n\n"
 666       " Associated with each breakpoint is a count and, optionally, one or more\n"
 667       " actions.  Each time a breakpoint occurs, the associated count\n"
 668       " is *decremented*.  If the count is less than or equal to `0`, the breakpoint\n"
 669       " occurs; otherwise, it is deferred.  When the breakpoint occurs, any\n"
 670       " optional actions are automatically executed.\n\n"
 671       " A breakpoint is set by the `BREAK` (or `SET BREAK`) command:\n\n"
 672       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
 673       " If no type is specified, the default breakpoint type (`E`, *execution*) is\n"
 674       " used.  If no address range is specified, the current PC is used.  As\n"
 675       " with `EXAMINE` and `DEPOSIT`, an address range may be a single address, a\n"
 676       " range of addresses low-high, or a relative range of address/length.\n"
 677        /***************** 80 character line width template *************************/
 678       "5Displaying Breakpoints\n"
 679       " Currently set breakpoints can be displayed with the `SHOW BREAK` command:\n\n"
 680       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
 681       " Locations with breakpoints of the specified type are displayed.\n\n"
 682       " The \"`-C`\" switch displays the selected breakpoint(s) formatted as\n"
 683       " commands which may be subsequently used to establish the same\n"
 684       " breakpoint(s).\n\n"
 685       "5Removing Breakpoints\n"
 686       " Breakpoints can be cleared by the `NOBREAK` or the `SET NOBREAK` commands.\n"
 687       "5Examples\n"
 688       " The following examples illustrate breakpoint usage:\n\n"
 689       "++BREAK                      set E break at current PC\n"
 690       "++BREAK -e 200               set E break at 200\n"
 691       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\n"
 692       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\n"
 693       "+++++++++D MQ 0\n"
 694       "++BREAK 100;                 delete action on break at 100\n\n"
 695        /***************** 80 character line width template *************************/
 696       "2Connecting and Disconnecting Devices\n"
 697       " Units are simulated as files on the host file system.  Before using any\n"
 698       " simulated unit, the user must specify the file to be accessed by that unit.\n"
 699 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices Attaching_devices"
 700       "3Attaching devices\n"
 701       " The `ATTACH` (*abbreviation* `AT`) command associates a unit and a file:\n\n"
 702       "++ATTACH <unit> <filename>\n\n"
 703       " Some devices have more detailed or specific help available with:\n\n"
 704       "++HELP <device> ATTACH\n\n"
 705       "4Switches\n"
 706       "5-n\n"
 707       " If the \"`-n`\" switch is specified when `ATTACH` is executed, a new\n"
 708       " file will be created when the filename specified does not exist, or an\n"
 709       " existing file will have it's size truncated to zero, and an appropriate\n"
 710       " message is printed.\n"
 711       "5-e\n"
 712       " If the file does not exist, and the \"`-e`\" switch *was not* specified,\n"
 713       " a new file is created, and an appropriate message is printed.  If\n"
 714       " the \"`-e`\" switch *was* specified, a new file is *not* created, and an\n"
 715       " error message is printed.\n"
 716       "5-r\n"
 717       " If the \"`-r`\" switch is specified, or the file is write protected by\n"
 718       " host operating system, `ATTACH` tries to open the file in read only mode.\n"
 719       " If the file does not exist, or the unit does not support read only\n"
 720       " operation, an error occurs.  Input-only devices, such as card readers, or\n"
 721       " storage devices with write locking switches, such as disks or tapes,\n"
 722       " support read only operation - other devices do not.  If a file is\n"
 723       " attached read only, its contents can be examined but not modified.\n"
 724       "5-q\n"
 725       " If the \"`-q`\" switch is specified when creating a new file (\"`-n`\")\n"
 726       " or opening one read only (\"`-r`\"), the message announcing this fact\n"
 727       " is suppressed.\n"
 728       "5-f\n"
 729       " For simulated magnetic tapes, the `ATTACH` command can specify the format\n"
 730       " of the attached tape image file:\n\n"
 731       "++ATTACH -f <tape_unit> <format> <filename>\n\n"
 732       " * The currently supported magnetic tape image file formats are:\n\n"
 733       " |                  |                                                      |\n"
 734       " | ----------------:|:---------------------------------------------------- |\n"
 735       " | \"**`SIMH`**\"   | The **SIMH** / **DPS8M** native portable tape format |\n"
 736       " | \"**`E11`**\"    | The *D Bit* **Ersatz-11** simulator format           |\n"
 737       " | \"**`TPC`**\"    | The **TPC** format (*used by _SIMH_ prior to V2.3*)  |\n"
 738       " | \"**`P7B`**\"    | The **Paul Pierce** `7`-track tape archive format    |\n\n"
 739        /***************** 80 character line width template *************************/
 740       " * The default tape format can also be specified with the `SET` command\n"
 741       " prior to using the `ATTACH` command:\n\n"
 742       "++SET <tape_unit> FORMAT=<format>\n"
 743       "++ATTACH <tape_unit> <filename>\n\n"
 744       " * The format of a currently attached tape image can be displayed with\n"
 745       "   the `SHOW FORMAT` command:\n\n"
 746       "++SHOW <unit> FORMAT\n\n"
 747       " **Examples**\n\n"
 748       " The following example illustrates common `ATTACH` usage:\n"
 749       "++; Associate the tape image file \"12.7MULTICS.tap\" with the tape0 unit\n"
 750       "++; in read-only mode, where tape0 corresponds to the first tape device.\n"
 751       "++ATTACH -r tape0 12.7MULTICS.tap\n\n"
 752       "++; Associate the disk image file \"root.dsk\" with the disk0 unit.\n"
 753       "++; The disk0 unit corresponds to the first disk device.\n"
 754       "++ATTACH disk0 root.dsk\n\n"
 755        /***************** 80 character line width template *************************/
 756 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices Detaching_devices"
 757       "3Detaching devices\n"
 758       " The `DETACH` (*abbreviation* `DET`) command breaks the association between\n"
 759       " a unit and its backing file or device:\n\n"
 760       "++DETACH ALL             Detach all units\n"
 761       "++DETACH <unit>          Detach specified unit\n\n"
 762       " * **NOTE:** The `EXIT` command performs an automatic `DETACH ALL`.\n"
 763 #define HLP_SET         "*Commands SET"
 764       "2SET\n"
 765        /***************** 80 character line width template *************************/
 766 #define HLP_SET_LOG    "*Commands SET Logging"
 767       "3Logging\n"
 768       " Interactions with the simulator session can be recorded to a log file.\n\n"
 769       "+SET LOG log_file            Specify the log destination\n"
 770       "++++++++                     (STDOUT, DEBUG, or filename)\n"
 771       "+SET NOLOG                   Disables any currently active logging\n"
 772       "4Switches\n"
 773       "5`-N`\n"
 774       " By default, log output is written at the *end* of the specified log file.\n"
 775       " A new log file can created if the \"`-N`\" switch is used on the command\n"
 776       " line.\n\n"
 777       "5`-B`\n"
 778       " By default, log output is written in *text* mode.  The log file can be\n"
 779       " opened for *binary* mode writing if the \"`-B`\" switch is used on the\n"
 780       " command line.\n"
 781 #define HLP_SET_DEBUG  "*Commands SET Debug_Messages"
 782        /***************** 80 character line width template *************************/
 783       "3Debug Messages\n"
 784       "+SET DEBUG debug_file        Specify the debug destination\n"
 785       "++++++++                     (STDOUT, STDERR, LOG, or filename)\n"
 786       "+SET NODEBUG                 Disables any currently active debug output\n"
 787       "4Switches\n"
 788       " Debug message output contains a timestamp which indicates the number of\n"
 789       " simulated instructions which have been executed prior to the debug event.\n\n"
 790       " Debug message output can be enhanced to contain additional, potentially\n"
 791       " useful information.\n\n\n"
 792       " **NOTE**: If neither \"`-T`\" or \"`-A`\" is specified, \"`-T`\" is implied.\n"
 793       "5-T\n"
 794       " The \"`-T`\" switch causes debug output to contain a time of day displayed\n"
 795       " as `hh:mm:ss.msec`.\n"
 796       "5-A\n"
 797       " The \"`-A`\" switch causes debug output to contain a time of day displayed\n"
 798       " as `seconds.msec`.\n"
 799       "5-R\n"
 800       " The \"`-R`\" switch causes timing to be relative to the start of debugging.\n"
 801       "5-P\n"
 802       " The \"`-P`\" switch adds the output of the PC (program counter) to each\n"
 803       " debug message.\n"
 804       "5-N\n"
 805       " The \"`-N`\" switch causes a new (empty) file to be written to.\n"
 806       " (The default is to append to an existing debug log file).\n"
 807       "5-D\n"
 808       " The \"`-D`\" switch causes data blob output to also display the data\n"
 809       " as **`RADIX-50`** characters.\n"
 810       "5-E\n"
 811       " The \"`-E`\" switch causes data blob output to also display the data\n"
 812       " as \"**EBCDIC**\" characters.\n"
 813        /***************** 80 character line width template *************************/
 814 #define HLP_SET_ENVIRON "*Commands SET Environment_Variables"
 815       "3Environment Variables\n"
 816       "+SET ENVIRONMENT NAME=val    Set environment variable\n"
 817       "+SET ENVIRONMENT NAME        Clear environment variable\n"
 818 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
 819       "3Command Status Trap Dispatching\n"
 820       "+SET ON                      Enables error checking command execution\n"
 821       "+SET NOON                    Disables error checking command execution\n"
 822       "+SET ON INHERIT              Enables inheritance of ON state and actions\n"
 823       "+SET ON NOINHERIT            Disables inheritance of ON state and actions\n"
 824 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
 825       "3Command Execution Display\n"
 826       "+SET VERIFY                  Enables display of processed script commands\n"
 827       "+SET VERBOSE                 Enables display of processed script commands\n"
 828       "+SET NOVERIFY                Disables display of processed script commands\n"
 829       "+SET NOVERBOSE               Disables display of processed script commands\n"
 830 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
 831       "3Command Error Status Display\n"
 832       "+SET MESSAGE                 Re-enables display of script error messages\n"
 833       "+SET NOMESSAGE               Disables display of script error messages\n"
 834 #define HLP_SET_LOCALOPC "*Commands SET Local_Operator_Console"
 835       "3Local Operator Console\n"
 836       "+SET LOCALOPC                Enables local operator console\n"
 837       "+SET NOLOCALOPC              Disables local operator console\n"
 838 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
 839       "3Command Output Display\n"
 840       "+SET QUIET                   Disables suppression of some messages\n"
 841       "+SET NOQUIET                 Re-enables suppression of some messages\n"
 842 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
 843       "3Command Prompt\n"
 844       "+SET PROMPT \"string\"         Sets an alternate simulator prompt string\n"
 845       "3Device and Unit Settings\n"
 846       "+SET <dev> OCT|DEC|HEX       Set device display radix\n"
 847       "+SET <dev> ENABLED           Enable device\n"
 848       "+SET <dev> DISABLED          Disable device\n"
 849       "+SET <dev> DEBUG{=arg}       Set device debug flags\n"
 850       "+SET <dev> NODEBUG={arg}     Clear device debug flags\n"
 851       "+SET <dev> arg{,arg...}      Set device parameters\n"
 852       "+SET <unit> ENABLED          Enable unit\n"
 853       "+SET <unit> DISABLED         Disable unit\n"
 854       "+SET <unit> arg{,arg...}     Set unit parameters\n"
 855       "+HELP <dev> SET              Displays any device specific SET commands\n"
 856       " \n\n"
 857       " See the Omnibus documentation for a complete SET command reference.\n"
 858        /***************** 80 character line width template *************************/
 859 #define HLP_SHOW        "*Commands SHOW"
 860       "2SHOW\n"
 861       "+SH{OW} B{UILDINFO}               Show build-time compilation information\n"
 862       "+SH{OW} CL{OCKS}                  Show wall clock and timer information\n"
 863       "+SH{OW} C{ONFIGURATION}           Show simulator configuration\n"
 864       "+SH{OW} D{EFAULT_BASE_SYSTEM}     Show default base system script\n"
 865       "+SH{OW} DEV{ICES}                 Show devices\n"
 866       "+SH{OW} M{ODIFIERS}               Show SET commands for all devices\n"
 867       "+SH{OW} O{N}                      Show ON condition actions\n"
 868       "+SH{OW} P{ROM}                    Show CPU ID PROM initialization data\n"
 869       "+SH{OW} Q{UEUE}                   Show event queue\n"
 870       "+SH{OW} S{HOW}                    Show SHOW commands for all devices\n"
 871       "+SH{OW} T{IME}                    Show simulated timer\n"
 872       "+SH{OW} VE{RSION}                 Show simulator version\n"
 873       "+H{ELP} <dev> SHOW                Show device-specific SHOW commands\n"
 874       "+SH{OW} <dev> {arg,...}           Show device parameters\n"
 875       "+SH{OW} <dev> DEBUG               Show device debug flags\n"
 876       "+SH{OW} <dev> MODIFIERS           Show device modifiers\n"
 877       "+SH{OW} <dev> RADIX               Show device display radix\n"
 878       "+SH{OW} <dev> SHOW                Show device SHOW commands\n"
 879       "+SH{OW} <unit> {arg,...}          Show unit parameters\n\n"
 880       " See the Omnibus documentation for a complete SHOW command reference.\n\n"
 881 #define HLP_SHOW_CONFIG         "*Commands SHOW"
 882 #define HLP_SHOW_DEVICES        "*Commands SHOW"
 883 #define HLP_SHOW_FEATURES       "*Commands SHOW"
 884 #define HLP_SHOW_QUEUE          "*Commands SHOW"
 885 #define HLP_SHOW_TIME           "*Commands SHOW"
 886 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
 887 #define HLP_SHOW_NAMES          "*Commands SHOW"
 888 #define HLP_SHOW_SHOW           "*Commands SHOW"
 889 #define HLP_SHOW_VERSION        "*Commands SHOW"
 890 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
 891 #define HLP_SHOW_PROM           "*Commands SHOW"
 892 #define HLP_SHOW_DBS            "*Commands SHOW"
 893 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
 894 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
 895 #define HLP_SHOW_REMOTE         "*Commands SHOW"
 896 #define HLP_SHOW_BREAK          "*Commands SHOW"
 897 #define HLP_SHOW_LOG            "*Commands SHOW"
 898 #define HLP_SHOW_DEBUG          "*Commands SHOW"
 899 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
 900 #define HLP_SHOW_ON             "*Commands SHOW"
 901 #define HLP_SHOW_SEND           "*Commands SHOW"
 902 #define HLP_SHOW_EXPECT         "*Commands SHOW"
 903 #define HLP_HELP                "*Commands HELP"
 904        /***************** 80 character line width template *************************/
 905       "2HELP\n"
 906       "+H{ELP}                      Show this message\n"
 907       "+H{ELP} <command>            Show help for command\n"
 908       "+H{ELP} <dev>                Show help for device\n"
 909       "+H{ELP} <dev> REGISTERS      Show help for device register variables\n"
 910       "+H{ELP} <dev> ATTACH         Show help for device specific ATTACH command\n"
 911       "+H{ELP} <dev> SET            Show help for device specific SET commands\n"
 912       "+H{ELP} <dev> SHOW           Show help for device specific SHOW commands\n"
 913       "+H{ELP} <dev> <command>      Show help for device specific <command> command\n"
 914        /***************** 80 character line width template *************************/
 915       "2Altering The Simulated Configuration\n"
 916       " The \"SET <device> DISABLED\" command removes a device from the configuration.\n"
 917       " A `DISABLED` device is invisible to running programs.  The device can still\n"
 918       " be `RESET`, but it cannot be `ATTACH`ed, `DETACH`ed, or `BOOT`ed.\n\n"
 919       " The \"SET <device> ENABLED\" command restores a disabled device to a\n"
 920       " configuration.\n\n"
 921       " Most multi-unit devices allow units to be enabled or disabled:\n\n"
 922       "++SET <unit> ENABLED\n"
 923       "++SET <unit> DISABLED\n\n"
 924       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
 925        /***************** 80 character line width template *************************/
 926 #define HLP_DO          "*Commands Executing_Command_Files Processing_Command_Files"
 927       "2Executing Command Files\n"
 928       "3Processing Command Files\n"
 929       " The simulator can invoke another script file with the \"`DO`\" command:\n\n"
 930       "++DO <filename> {arguments...}       execute commands in specified file\n\n"
 931       " The \"`DO`\" command allows command files to contain substitutable\n"
 932       " arguments. The string \"`%%n`\", where \"`n`\" is a number\n"
 933       " between \"`1`\" and \"`9`\", is replaced with argument \"`n`\" from\n"
 934       " the \"`DO`\" command line. (*i.e.* \"`%%0`\", \"`%%1`\", \"`%%2`\", etc.).\n"
 935       " The string \"`%%0`\" is replaced with \"<`filename`>\".\n The\n"
 936       " sequences \"`\\%%`\" and \"`\\\\`\" are replaced with the literal\n"
 937       " characters \"`%%`\" and \"`\\`\", respectively. Arguments with spaces must\n"
 938       " be enclosed in matching single or double quotation marks.\n\n"
 939       " * **NOTE**: Nested \"`DO`\" commands are supported, up to ten invocations\n"
 940       " deep.\n\n"
 941       "4Switches\n"
 942       "5`-v`\n\n"
 943       " If the switch \"`-v`\" is specified, commands in the command file are\n"
 944       " echoed *before* they are executed.\n\n"
 945       "5`-e`\n\n"
 946       " If the switch \"`-e`\" is specified, command processing (including nested\n"
 947       " command invocations) will be aborted if any command error is encountered.\n"
 948       " (A simulation stop **never** aborts processing; use `ASSERT` to catch\n"
 949       " unexpected stops.) Without this switch, all errors except `ASSERT` failures\n"
 950       " will be ignored, and command processing will continue.\n\n"
 951       "5`-o`\n\n"
 952       " If the switch \"`-o`\" is specified, the `ON` conditions and actions from\n"
 953       " the calling command file will be inherited by the command file being\n"
 954       " invoked.\n"
 955       "5`-q`\n\n"
 956       " If the switch \"`-q`\" is specified, *quiet mode* will be explicitly\n"
 957       " enabled for the called command file, otherwise the *quiet mode* setting\n"
 958       " is inherited from the calling context.\n"
 959        /***************** 80 character line width template *************************/
 960 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
 961       "3GOTO\n"
 962       " Commands in a command file execute in sequence until either an error\n"
 963       " trap occurs (when a command completes with an error status), or when an\n"
 964       " explict request is made to start command execution elsewhere with\n"
 965       " the `GOTO` command:\n\n"
 966       "++GOTO <label>\n\n"
 967       " * Labels are lines in a command file which the first non-whitespace\n"
 968       " character is a \"`:`\".\n"
 969       " * The target of a `GOTO` is the first matching label in the current `DO`\n"
 970       " command file which is encountered.\n\n"
 971       " **Example**\n\n"
 972       " The following example illustrates usage of the `GOTO` command (by\n"
 973       " creating an infinite loop):\n\n"
 974       "++:Label\n"
 975       "++:: This is a loop.\n"
 976       "++GOTO Label\n\n"
 977 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
 978        /***************** 80 character line width template *************************/
 979       "3RETURN\n"
 980       " The `RETURN` command causes the current procedure call to be restored to\n"
 981       " the calling context, possibly returning a specific return status.\n"
 982       " If no return status is specified, the return status from the last command\n"
 983       " executed will be returned.  The calling context may have `ON` traps defined\n"
 984       " which may redirect command flow in that context.\n\n"
 985       "++RETURN                 return from command file with last command status\n"
 986       "++RETURN {-Q} <status>   return from command file with specific status\n\n"
 987       " * The status return can be any numeric value or one of the standard SCPE_\n"
 988       " condition names.\n\n"
 989       " * The \"`-Q`\" switch on the `RETURN` command will cause the specified\n"
 990       " status to be returned, but normal error status message printing to be\n"
 991       " suppressed.\n\n"
 992       " **Condition Names**\n\n"
 993       " The available standard SCPE_ condition names and their meanings are:\n\n"
 994       " | Name    | Meaning                         | Name    | Meaning                             |\n"
 995       " | ------- | --------------------------------| ------- | ----------------------------------- |\n"
 996       " | NXM     | Address space exceeded          | UNATT   | Unit not attached                   |\n"
 997       " | IOERR   | I/O error                       | CSUM    | Checksum error                      |\n"
 998       " | FMT     | Format error                    | NOATT   | Unit not attachable                 |\n"
 999       " | OPENERR | File open error                 | MEM     | Memory exhausted                    |\n"
1000       " | ARG     | Invalid argument                | STEP    | Step expired                        |\n"
1001       " | UNK     | Unknown command                 | RO      | Read only argument                  |\n"
1002       " | INCOMP  | Command not completed           | STOP    | Simulation stopped                  |\n"
1003       " | EXIT    | Goodbye                         | TTIERR  | Console input I/O error             |\n"
1004       " | TTOERR  | Console output I/O error        | EOF     | End of file                         |\n"
1005       " | REL     | Relocation error                | NOPARAM | No settable parameters              |\n"
1006       " | ALATT   | Unit already attached           | TIMER   | Hardware timer error                |\n"
1007       " | SIGERR  | Signal handler setup error      | TTYERR  | Console terminal setup error        |\n"
1008       " | NOFNC   | Command not allowed             | UDIS    | Unit disabled                       |\n"
1009       " | NORO    | Read only operation not allowed | INVSW   | Invalid switch                      |\n"
1010       " | MISVAL  | Missing value                   | 2FARG   | Too few arguments                   |\n"
1011       " | 2MARG   | Too many arguments              | NXDEV   | Non-existent device                 |\n"
1012       " | NXUN    | Non-existent unit               | NXREG   | Non-existent register               |\n"
1013       " | NXPAR   | Non-existent parameter          | NEST    | Nested DO command limit exceeded    |\n"
1014       " | IERR    | Internal error                  | MTRLNT  | Invalid magtape record length       |\n"
1015       " | LOST    | Console Telnet connection lost  | TTMO    | Console Telnet connection timed out |\n"
1016       " | STALL   | Console Telnet output stall     | AFAIL   | Assertion failed                    |\n"
1017       " | INVREM  | Invalid remote console command  |         |                                     |\n"
1018       "\n\n"
1019 #define HLP_SHIFT       "*Commands Executing_Command_Files Shift_Parameters"
1020       "3Shift Parameters\n"
1021       " Shift the command files positional parameters\n"
1022 #define HLP_CALL        "*Commands Executing_Command_Files Call_a_subroutine"
1023       "3Call a subroutine\n"
1024       " Control can be transferred to a labeled subroutine using `CALL`.\n\n"
1025       " **Example**\n\n"
1026       "++CALL routine\n"
1027       "++BYE\n"
1028       "++\n"
1029       "++:routine\n"
1030       "++ECHO routine called\n"
1031       "++RETURN\n\n"
1032 #define HLP_ON          "*Commands Executing_Command_Files ON"
1033       "3ON\n"
1034       " The `ON` command performs actions after a condition, or clears a condition.\n"
1035       "++ON <condition> <action>  Perform action after condition\n"
1036       "++ON <condition>           Clears action of specified condition\n"
1037 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1038 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1039        /***************** 80 character line width template *************************/
1040       "3PROCEED or IGNORE\n"
1041       " The `PROCEED` (or `IGNORE`) command does nothing.  It is potentially\n"
1042       " useful as a placeholder for any `ON` action condition that should be\n"
1043       " explicitly ignored, allowing command file execution to continue without\n"
1044       " taking any specific action.\n"
1045 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1046        /***************** 80 character line width template *************************/
1047       "3Displaying Arbitrary Text\n"
1048       " The `ECHO` command is a useful way of annotating command files.  `ECHO`\n"
1049       " prints out its arguments to the console (and to any applicable log file):\n\n"
1050       "++ECHO <string>      Output string to console\n\n"
1051       " **NOTE**: If no arguments are specified, `ECHO` prints a blank line.\n"
1052       " This may be used to provide spacing for console messages or log file\n"
1053       " output.\n"
1054        /***************** 80 character line width template *************************/
1055 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Assertions"
1056       "3Testing Assertions\n"
1057       " The `ASSERT` command tests a simulator state condition and halts command\n"
1058       " file execution if the condition is false:\n\n"
1059       "++ASSERT <Simulator State Expressions>\n\n"
1060       " * If the indicated expression evaluates to false, the command completes\n"
1061       " with an `AFAIL` condition.  By default, when a command file encounters a\n"
1062       " command which returns the `AFAIL` condition, it will exit the running\n"
1063       " command file with the `AFAIL` status to the calling command file.  This\n"
1064       " behavior can be changed with the `ON` command as well as switches to the\n"
1065       " invoking `DO` command.\n\n"
1066       " **Examples**\n\n"
1067       " The command file below might be used to bootstrap a hypothetical system\n"
1068       " that halts after the initial load from disk. The `ASSERT` command can then\n"
1069       " be used to confirm that the load completed successfully by examining the\n"
1070       " CPU's \"`A`\" register for the expected value:\n\n"
1071       "++; Example INI file\n"
1072       "++BOOT\n"
1073       "++; A register contains error code; 0 = good boot\n"
1074       "++ASSERT A=0\n"
1075       "++RUN\n\n"
1076        /***************** 80 character line width template *************************/
1077       " * In the above example, if the \"`A`\" register is *not* `0`,\n"
1078       " the \"`ASSERT A=0`\" command will be displayed to the user, and the\n"
1079       " command file will be aborted with an \"`Assertion failed`\" message.\n"
1080       " Otherwise, the command file will continue to bring up the system.\n\n"
1081       " * See the **`IF`** command documentation for more information and details\n"
1082       " regarding simulator state expressions.\n\n"
1083 #define HLP_IF          "*Commands Executing_Command_Files Testing_Conditions"
1084       "3Testing Conditions\n"
1085       " The `IF` command tests a simulator state condition and executes additional\n"
1086       " commands if the condition is true:\n\n"
1087       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommand}...\n\n"
1088       " **Examples**\n\n"
1089       " The command file below might be used to bootstrap a hypothetical system\n"
1090       " that halts after the initial load from disk. The `IF` command can then\n"
1091       " be used to confirm that the load completed successfully by examining the\n"
1092       " CPU's \"`A`\" register for an expected value:\n\n"
1093       "++; Example INI file\n"
1094       "++BOOT\n"
1095       "++; A register contains error code; 0 = good boot\n"
1096       "++IF NOT A=0 echo Boot failed - Failure Code ; EX A; exit AFAIL\n"
1097       "++RUN\n\n"
1098        /***************** 80 character line width template *************************/
1099       " * In the above example, if the \"`A`\" register is *not* `0`, the\n"
1100       " message \"`Boot failed - Failure Code `\" will be displayed, the contents\n"
1101       " of the \"`A`\" register will be displayed, and the command file will be\n"
1102       " aborted with an \"`Assertion failed`\" message.  Otherwise, the command\n"
1103       " file will continue to bring up the system.\n"
1104       "4Conditional Expressions\n"
1105       " The `IF` and `ASSERT` commands evaluate the following two different forms\n"
1106       " of conditional expressions.\n\n"
1107       "5Simulator State Expressions\n"
1108       "  &nbsp;\n \n"
1109       " The values of simulator registers can be evaluated with:\n\n"
1110       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1111       " * If \"<`dev`>\" is not specified, `CPU` is assumed.  \"<`reg`>\" is a\n"
1112       " register belonging to the indicated device.\n"
1113       " * The \"<`addr`>\" is an address in the address space of the indicated\n"
1114       " device.\n"
1115       " * The \"<`conditional-op`>\" and optional \"<`logical-op`>\" are\n"
1116       " the same as those used for \"search specifiers\" by the `EXAMINE` and\n"
1117       " `DEPOSIT` commands.\n"
1118       " The \"<`value`>\" is expressed in the radix specified for \"<`reg`>\",\n"
1119       " not in the radix for the device when referencing a register; when an\n"
1120       " address is referenced the device radix is used as the default.\n\n"
1121       " * If \"<`logical-op`>\" and \"<`value`>\" are specified, the target\n"
1122       " register value is first altered as indicated.  The result is then compared\n"
1123       " to the \"<`value`>\" via the \"<`conditional-op`>\".\n"
1124       " * * If the result is *true*, the command(s) are executed before proceeding\n"
1125       " to the next line in the command file.\n"
1126       " * * If the result is *false*, the next command in the command file is\n"
1127       " processed.\n\n"
1128       "5String Comparison Expressions\n"
1129       "  &nbsp;\n \n"
1130       " String Values can be compared with:\n\n"
1131       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1132       " * The \"`-i`\" switch, if present, causes a comparison to be case\n"
1133       "   insensitive.\n"
1134       " * The \"<`string1`>\" and \"<`string2`>\" arguments are quoted string\n"
1135       "   values which may have environment variables substituted as desired.\n"
1136       " * The \"<`compare-op`>\" may be one of:\n\n"
1137       " |                |                        |\n"
1138       " | --------------:|:---------------------- |\n"
1139       " | \"**`==`**\"     |  equal                 |\n"
1140       " | \"**`EQU`**\"    |  equal                 |\n"
1141       " | \"**`!=`**\"     |  not equal             |\n"
1142       " | \"**`NEQ`**\"    |  not equal             |\n"
1143       " | \"**<**\"        |  less than             |\n"
1144       " | \"**`LSS`**\"    |  less than             |\n"
1145       " | \"**<=**\"       |  less than or equal    |\n"
1146       " | \"**`LEQ`**\"    |  less than or equal    |\n"
1147       " | \"**>**\"        |  greater than          |\n"
1148       " | \"**`GTR`**\"    |  greater than          |\n"
1149       " | \"**>=**\"       |  greater than or equal |\n"
1150       " | \"**`GEQ`**\"    |  greater than or equal |\n"
1151       " * **NOTE**: Comparisons are *generic*.  This means that if\n"
1152       " both \"<`string1`>\" and \"<`string2`>\" are comprised of all numeric\n"
1153       " digits, then the strings are converted to numbers and a numeric\n"
1154       " comparison is performed.  For example, the comparison\n"
1155       " '`\"+1\"` `EQU` `\"1\"`' evaluates to *true*.\n"
1156        /***************** 80 character line width template *************************/
1157 #define HLP_EXIT        "*Commands Exiting_the_Simulator"
1158       "2Exiting the Simulator\n"
1159       " The `EXIT` command (*synonyms* `QUIT` *and* `BYE`) exits the simulator,\n"
1160       " returning control to the host operating system.\n"
1161        /***************** 80 character line width template *************************/
1162 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1163       "2Executing System Commands\n"
1164       " * The simulator can execute host operating system commands with\n"
1165       " the \"`!`\" (*spawn*) command.\n\n"
1166       " |                         |                                             |\n"
1167       " |:----------------------- |:------------------------------------------- |\n"
1168       " | \"**`!`**\"             | Spawn the hosts default command interpreter |\n"
1169       " | \"**`!`** <`command`>\" | Execute the host operating system `command` |\n\n"
1170       " * **NOTE**: The *exit status* from the command which was executed is set\n"
1171       " as the *command completion status* for the \"`!`\" command.  This may\n"
1172       " influence any enabled `ON` condition traps.\n" ;
1173        /***************** 80 character line width template *************************/
1174 
1175 static CTAB cmd_table[] = {
1176     { "RESET",      &reset_cmd,     0,          HLP_RESET },
1177     { "EXAMINE",    &exdep_cmd,     EX_E,       HLP_EXAMINE },
1178     { "IEXAMINE",   &exdep_cmd,     EX_E+EX_I,  HLP_IEXAMINE },
1179     { "DEPOSIT",    &exdep_cmd,     EX_D,       HLP_DEPOSIT },
1180     { "IDEPOSIT",   &exdep_cmd,     EX_D+EX_I,  HLP_IDEPOSIT },
1181     { "EVALUATE",   &eval_cmd,      0,          HLP_EVALUATE },
1182     { "RUN",        &run_cmd,       RU_RUN,     HLP_RUN,        NULL, &run_cmd_message },
1183     { "GO",         &run_cmd,       RU_GO,      HLP_GO,         NULL, &run_cmd_message },
1184     { "STEP",       &run_cmd,       RU_STEP,    HLP_STEP,       NULL, &run_cmd_message },
1185     { "NEXT",       &run_cmd,       RU_NEXT,    HLP_NEXT,       NULL, &run_cmd_message },
1186     { "CONTINUE",   &run_cmd,       RU_CONT,    HLP_CONTINUE,   NULL, &run_cmd_message },
1187     { "BOOT",       &run_cmd,       RU_BOOT,    HLP_BOOT,       NULL, &run_cmd_message },
1188     { "BREAK",      &brk_cmd,       SSH_ST,     HLP_BREAK },
1189     { "NOBREAK",    &brk_cmd,       SSH_CL,     HLP_NOBREAK },
1190     { "ATTACH",     &attach_cmd,    0,          HLP_ATTACH },
1191     { "DETACH",     &detach_cmd,    0,          HLP_DETACH },
1192     { "EXIT",       &exit_cmd,      0,          HLP_EXIT },
1193     { "QUIT",       &exit_cmd,      0,          NULL },
1194     { "BYE",        &exit_cmd,      0,          NULL },
1195     { "SET",        &set_cmd,       0,          HLP_SET },
1196     { "SHOW",       &show_cmd,      0,          HLP_SHOW },
1197     { "DO",         &do_cmd,        1,          HLP_DO },
1198     { "GOTO",       &goto_cmd,      1,          HLP_GOTO },
1199     { "RETURN",     &return_cmd,    0,          HLP_RETURN },
1200     { "SHIFT",      &shift_cmd,     0,          HLP_SHIFT },
1201     { "CALL",       &call_cmd,      0,          HLP_CALL },
1202     { "ON",         &on_cmd,        0,          HLP_ON },
1203     { "IF",         &assert_cmd,    0,          HLP_IF },
1204     { "PROCEED",    &noop_cmd,      0,          HLP_PROCEED },
1205     { "IGNORE",     &noop_cmd,      0,          HLP_IGNORE },
1206     { "ECHO",       &echo_cmd,      0,          HLP_ECHO },
1207     { "ASSERT",     &assert_cmd,    1,          HLP_ASSERT },
1208     { "SEND",       &send_cmd,      0 }, /* deprecated */
1209     { "EXPECT",     &expect_cmd,    1 }, /* deprecated */
1210     { "NOEXPECT",   &expect_cmd,    0 }, /* deprecated */
1211     { "!",          &spawn_cmd,     0,          HLP_SPAWN },
1212     { "HELP",       &help_cmd,      0,          HLP_HELP },
1213     { NULL, NULL, 0 }
1214     };
1215 
1216 static CTAB set_glob_tab[] = {
1217     { "CONSOLE",     &sim_set_console,          0 }, /* deprecated */
1218     { "REMOTE",      &sim_set_remote_console,   0 }, /* deprecated */
1219     { "BREAK",       &brk_cmd,             SSH_ST }, /* deprecated */
1220     { "NOBREAK",     &brk_cmd,             SSH_CL }, /* deprecated */
1221     { "TELNET",      &sim_set_telnet,           0 }, /* deprecated */
1222     { "NOTELNET",    &sim_set_notelnet,         0 }, /* deprecated */
1223     { "LOG",         &sim_set_logon,            0, HLP_SET_LOG  },
1224     { "NOLOG",       &sim_set_logoff,           0, HLP_SET_LOG  },
1225     { "DEBUG",       &sim_set_debon,            0, HLP_SET_DEBUG  },
1226     { "NODEBUG",     &sim_set_deboff,           0, HLP_SET_DEBUG  },
1227     { "ENVIRONMENT", &sim_set_environment,      1, HLP_SET_ENVIRON },
1228     { "ON",          &set_on,                   1, HLP_SET_ON },
1229     { "NOON",        &set_on,                   0, HLP_SET_ON },
1230     { "VERIFY",      &set_verify,               1, HLP_SET_VERIFY },
1231     { "VERBOSE",     &set_verify,               1, HLP_SET_VERIFY },
1232     { "NOVERIFY",    &set_verify,               0, HLP_SET_VERIFY },
1233     { "NOVERBOSE",   &set_verify,               0, HLP_SET_VERIFY },
1234     { "MESSAGE",     &set_message,              1, HLP_SET_MESSAGE },
1235     { "NOMESSAGE",   &set_message,              0, HLP_SET_MESSAGE },
1236     { "QUIET",       &set_quiet,                1, HLP_SET_QUIET },
1237     { "NOQUIET",     &set_quiet,                0, HLP_SET_QUIET },
1238     { "LOCALOPC",    &set_localopc,             1, HLP_SET_LOCALOPC },
1239     { "NOLOCALOPC",  &set_localopc,             0, HLP_SET_LOCALOPC },
1240     { "PROMPT",      &set_prompt,               0, HLP_SET_PROMPT },
1241     { NULL,          NULL,                      0 }
1242     };
1243 
1244 static C1TAB set_dev_tab[] = {
1245     { "OCTAL",      &set_dev_radix,     8 },
1246     { "DECIMAL",    &set_dev_radix,     10 },
1247     { "HEX",        &set_dev_radix,     16 },
1248     { "ENABLED",    &set_dev_enbdis,    1 },
1249     { "DISABLED",   &set_dev_enbdis,    0 },
1250     { "DEBUG",      &set_dev_debug,     1 },
1251     { "NODEBUG",    &set_dev_debug,     0 },
1252     { NULL,         NULL,               0 }
1253     };
1254 
1255 static C1TAB set_unit_tab[] = {
1256     { "ENABLED",    &set_unit_enbdis,   1 },
1257     { "DISABLED",   &set_unit_enbdis,   0 },
1258     { NULL,         NULL,               0 }
1259     };
1260 
1261 static SHTAB show_glob_tab[] = {
1262     { "CONFIGURATION",  &show_config,               0, HLP_SHOW_CONFIG },
1263     { "DEFAULT_BASE_SYSTEM_SCRIPT", &show_default_base_system_script, 0, HLP_SHOW_DBS },
1264     { "DEVICES",        &show_config,               1, HLP_SHOW_DEVICES },
1265     { "FEATURES",       &show_config,               2, HLP_SHOW_FEATURES },
1266     { "QUEUE",          &show_queue,                0, HLP_SHOW_QUEUE },
1267     { "TIME",           &show_time,                 0, HLP_SHOW_TIME },
1268     { "MODIFIERS",      &show_mod_names,            0, HLP_SHOW_MODIFIERS },
1269     { "NAMES",          &show_log_names,            0, HLP_SHOW_NAMES },
1270     { "SHOW",           &show_show_commands,        0, HLP_SHOW_SHOW },
1271     { "VERSION",        &show_version,              1, HLP_SHOW_VERSION },
1272     { "BUILDINFO",      &show_buildinfo,            1, HLP_SHOW_BUILDINFO },
1273     { "PROM",           &show_prom,                 0, HLP_SHOW_PROM },
1274     { "CONSOLE",        &sim_show_console,          0, HLP_SHOW_CONSOLE },
1275     { "REMOTE",         &sim_show_remote_console,   0, HLP_SHOW_REMOTE },
1276     { "BREAK",          &show_break,                0, HLP_SHOW_BREAK },
1277     { "LOG",            &sim_show_log,              0, HLP_SHOW_LOG },
1278     { "TELNET",         &sim_show_telnet,           0 },    /* deprecated */
1279     { "DEBUG",          &sim_show_debug,            0, HLP_SHOW_DEBUG },
1280     { "CLOCKS",         &sim_show_timers,           0, HLP_SHOW_CLOCKS },
1281     { "SEND",           &sim_show_send,             0, HLP_SHOW_SEND },
1282     { "EXPECT",         &sim_show_expect,           0, HLP_SHOW_EXPECT },
1283     { "ON",             &show_on,                   0, HLP_SHOW_ON },
1284     { NULL,             NULL,                       0 }
1285     };
1286 
1287 static SHTAB show_dev_tab[] = {
1288     { "RADIX",      &show_dev_radix,            0 },
1289     { "DEBUG",      &show_dev_debug,            0 },
1290     { "MODIFIERS",  &show_dev_modifiers,        0 },
1291     { "NAMES",      &show_dev_logicals,         0 },
1292     { "SHOW",       &show_dev_show_commands,    0 },
1293     { NULL,         NULL,                       0 }
1294     };
1295 
1296 static SHTAB show_unit_tab[] = {
1297     { NULL, NULL, 0 }
1298     };
1299 
1300 #if defined(_WIN32)
1301 static
1302 int setenv(const char *envname, const char *envval, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
1303 {
1304 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1305 int r;
1306 
1307 sprintf(envstr, "%s=%s", envname, envval);
1308 r = _putenv(envstr);
1309 FREE(envstr);
1310 return r;
1311 }
1312 
1313 static
1314 int unsetenv(const char *envname)
     /* [previous][next][first][last][top][bottom][index][help] */
1315 {
1316 setenv(envname, "", 1);
1317 return 0;
1318 }
1319 #endif
1320 
1321 /* Testing realloc */
1322 
1323 #ifdef TESTING
1324 void *
1325 trealloc(void *ptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
1326 {
1327   void *r = realloc(ptr, size);
1328 
1329   if (r != ptr) return r;
1330   else if (r)
1331     {
1332       void *rm = malloc(size);
1333       if (!rm)
1334         {
1335           (void)fprintf(
1336             stderr,
1337             "\rFATAL: Out of memory?! Aborting at %s[%s:%d]\r\n",
1338             __func__, __FILE__, __LINE__);
1339 # if defined(USE_BACKTRACE)
1340 #  ifdef SIGUSR2
1341         (void)raise(SIGUSR2);
1342         /*NOTREACHED*/ /* unreachable */
1343 #  endif /* ifdef SIGUSR2 */
1344 # endif /* if defined(USE_BACKTRACE) */
1345         abort();
1346         /* NOTREACHED */ /* unreachable */
1347         }
1348       memcpy(rm, r, size);
1349       FREE(r);
1350       return rm;
1351     }
1352   else return r;
1353 }
1354 #endif /* ifdef TESTING */
1355 
1356 #ifdef TESTING
1357 # ifdef USE_TREALLOC
1358 #  undef realloc
1359 #  define realloc trealloc
1360 # endif /* ifdef USE_TREALLOC */
1361 #endif /* ifdef TESTING */
1362 
1363 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1364 
1365 /* Check if running on Rosetta 2 */
1366 
1367 #if defined(__APPLE__)
1368 int processIsTranslated(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1369 {
1370     int ret = 0;
1371     size_t size = sizeof(ret);
1372     if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1373         if (errno == ENOENT)
1374             return 0;
1375         return -1; }
1376     return ret;
1377 }
1378 #endif
1379 
1380 /* Substring removal hack */
1381 
1382 char *strremove(char *str, const char *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
1383 {
1384     char *p, *q, *r;
1385     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1386         size_t len = strlen(sub);
1387         while ((r = strstr(p = r + len, sub)) != NULL) {
1388             while (p < r)
1389                 *q++ = *p++;
1390         }
1391         while ((*q++ = *p++) != '\0')
1392             continue;
1393     }
1394     return str;
1395 }
1396 
1397 /* Trim whitespace */
1398 
1399 void strtrimspace (char *str_trimmed, const char *str_untrimmed)
     /* [previous][next][first][last][top][bottom][index][help] */
1400 {
1401     while (*str_untrimmed != '\0') {
1402       if(!isspace((unsigned char)*str_untrimmed)) {
1403         *str_trimmed = (char)*str_untrimmed;
1404         str_trimmed++;
1405       }
1406       str_untrimmed++;
1407     }
1408     *str_trimmed = '\0';
1409 }
1410 
1411 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1412 void allowCores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1413 {
1414   int ret;
1415   struct rlimit limit;
1416 # ifdef RLIMIT_CORE
1417   ret = getrlimit(RLIMIT_CORE, &limit);
1418   (void)ret;
1419 #  ifdef TESTING
1420   if (ret != 0)
1421     {
1422       sim_warn ("Failed to query core dump configuration.");
1423       return;
1424     }
1425 #  endif /* ifdef TESTING */
1426   limit.rlim_cur = limit.rlim_max;
1427   ret = setrlimit(RLIMIT_CORE, &limit);
1428 #  ifdef TESTING
1429   if (ret != 0)
1430     {
1431       sim_warn ("Failed to enable unlimited core dumps.");
1432       return;
1433     }
1434 #  endif /* ifdef TESTING */
1435 # else
1436 #  ifdef TESTING
1437   sim_warn ("Unable to query core dump configuration.");
1438 #  endif /* ifdef TESTING */
1439 # endif /* ifdef RLIMIT_CORE */
1440   return;
1441 }
1442 #endif /* if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1443 
1444 #ifdef USE_DUMA
1445 void CleanDUMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1446 {
1447   (void)fflush(stdout);
1448   DUMA_CHECKALL();
1449   (void)fflush(stderr);
1450 }
1451 # undef USE_DUMA
1452 # define USE_DUMA 1
1453 #endif /* ifdef USE_DUMA */
1454 
1455 #ifndef SIG_SETMASK
1456 # undef USE_BACKTRACE
1457 #endif /* ifndef SIG_SETMASK */
1458 
1459 #ifdef PERF_STRIP
1460 # undef USE_BACKTRACE
1461 #endif /* ifdef PERF_STRIP */
1462 
1463 #ifdef USE_BACKTRACE
1464 # include <backtrace.h>
1465 # include <backtrace-supported.h>
1466 # define BACKTRACE_SKIP 1
1467 # define BACKTRACE_MAIN "main"
1468 # undef USE_BACKTRACE
1469 # define USE_BACKTRACE 1
1470 #endif /* ifdef USE_BACKTRACE */
1471 
1472 #ifdef BACKTRACE_SUPPORTED
1473 # ifdef BACKTRACE_SUPPORTS_THREADS
1474 #  if !( BACKTRACE_SUPPORTED )
1475 #   undef USE_BACKTRACE
1476 #  endif /* if !( BACKTRACE_SUPPORTED ) */
1477 # else  /* ifdef BACKTRACE_SUPPORTS_THREADS */
1478 #  undef USE_BACKTRACE
1479 # endif /* ifdef BACKTRACE_SUPPORTS_THREADS */
1480 #else  /* ifdef BACKTRACE_SUPPORTED */
1481 # undef USE_BACKTRACE
1482 #endif /* ifdef BACKTRACE_SUPPORTED */
1483 
1484 #ifdef USE_BACKTRACE
1485 # ifdef BACKTRACE_SUPPORTED
1486 #  include "backtrace_func.c"
1487 # endif /* ifdef BACKTRACE_SUPPORTED */
1488 #endif /* ifdef USE_BACKTRACE */
1489 
1490 /* Main command loop */
1491 
1492 #ifndef PERF_STRIP
1493 int main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1494 {
1495 char *cptr, *cptr2;
1496 char nbuf[PATH_MAX + 7];
1497 char cbuf[4*CBUFSIZE];
1498 char **targv = NULL;
1499 # ifdef USE_BACKTRACE
1500 #  ifdef BACKTRACE_SUPPORTED
1501 #   ifdef _INC_BACKTRACE_FUNC
1502 bt_pid = (long)getpid();
1503 (void)bt_pid;
1504 #   endif /* ifdef _INC_BACKTRACE_FUNC */
1505 #  endif /* ifdef BACKTRACE_SUPPORTED */
1506 # endif /* ifdef USE_BACKTRACE */
1507 int32 i, sw;
1508 t_bool lookswitch;
1509 t_stat stat;
1510 
1511 # ifdef __MINGW32__
1512 #  undef IS_WINDOWS
1513 #  define IS_WINDOWS 1
1514 #  ifndef NEED_CONSOLE_SETUP
1515 #   define NEED_CONSOLE_SETUP
1516 #  endif
1517 # endif /* ifdef __MINGW32__ */
1518 
1519 # ifdef CROSS_MINGW32
1520 #  undef IS_WINDOWS
1521 #  define IS_WINDOWS 1
1522 #  ifndef NEED_CONSOLE_SETUP
1523 #   define NEED_CONSOLE_SETUP
1524 #  endif
1525 # endif /* ifdef CROSS_MINGW32 */
1526 
1527 # ifdef __MINGW64__
1528 #  undef IS_WINDOWS
1529 #  define IS_WINDOWS 1
1530 #  ifndef NEED_CONSOLE_SETUP
1531 #   define NEED_CONSOLE_SETUP
1532 #  endif
1533 # endif /* ifdef __MINGW64__ */
1534 
1535 # ifdef CROSS_MINGW64
1536 #  undef IS_WINDOWS
1537 #  define IS_WINDOWS 1
1538 #  ifndef NEED_CONSOLE_SETUP
1539 #   define NEED_CONSOLE_SETUP
1540 #  endif
1541 # endif /* ifdef CROSS_MINGW64 */
1542 
1543 # ifdef __CYGWIN__
1544 #  ifdef IS_WINDOWS
1545 #   undef IS_WINDOWS
1546 #  endif /* ifdef IS_WINDOWS */
1547 # endif /* ifdef __CYGWIN__ */
1548 
1549 # ifdef USE_DUMA
1550 #  ifdef DUMA_EXPLICIT_INIT
1551 duma_init();
1552 (void)fflush(stderr);
1553 #  endif /* ifdef DUMA_EXPLICIT_INIT */
1554 #  ifdef DUMA_MIN_ALIGNMENT
1555 #   if DUMA_MIN_ALIGNMENT > 0
1556 DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
1557 #   endif /* if DUMA_MIN_ALIGNMENT > 0 */
1558 #  endif /* ifdef DUMA_MIN_ALIGNMENT) */
1559 DUMA_SET_FILL(0x2E);
1560 (void)fflush(stderr);
1561 (void)atexit(CleanDUMA);
1562 # endif /* ifdef USE_DUMA */
1563 
1564 # ifdef USE_BACKTRACE
1565 #  ifdef BACKTRACE_SUPPORTED
1566 #   include "backtrace_main.c"
1567 #  endif /* ifdef BACKTRACE_SUPPORTED */
1568 # endif /* ifdef USE_BACKTRACE */
1569 
1570 setlocale(LC_NUMERIC, "");
1571 
1572 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1573 #  include <windows.h>
1574 #  ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
1575 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1576 #  endif
1577 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1578 
1579 # ifdef NEED_CONSOLE_SETUP
1580 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1581 if (handle != INVALID_HANDLE_VALUE)
1582   {
1583     DWORD mode = 0;
1584     if (GetConsoleMode(handle, &mode))
1585       {
1586         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1587         SetConsoleMode(handle, mode);
1588       }
1589   }
1590 puts ("\e[0m");
1591 # endif /* NEED_CONSOLE_SETUP */
1592 
1593 # ifdef __HAIKU__
1594 (void)disable_debugger(1);
1595 # endif /* ifdef __HAIKU__ */
1596 
1597 /* sanity checks */
1598 
1599 # ifdef __clang_analyzer__
1600 fprintf (stderr, "Error: Attempting to execute a Clang Analyzer build!\n");
1601 return 1;
1602 # endif
1603 
1604 if (argc == 0) {
1605     fprintf (stderr, "Error: main() called directly!\n");
1606     return 1;
1607 }
1608 
1609 /* Enable unlimited core dumps */
1610 # if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1611 allowCores();
1612 # endif /* !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1613 
1614 int testEndian = decContextTestEndian();
1615 if (testEndian != 0) {
1616   if (testEndian == 1) {
1617     fprintf (stderr,
1618       "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1619     return 1;
1620   }
1621   if (testEndian == -1) {
1622     fprintf (stderr,
1623       "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1624     return 1;
1625   }
1626   fprintf (stderr,
1627     "Error: Unable to determine system byte order; aborting.\n");
1628   return 1;
1629 }
1630 # ifdef NEED_128
1631 test_math128();
1632 # endif
1633 
1634 /* patch intel dispatcher */
1635 # ifdef __DISPATCH_H_
1636 #  if defined(__INTEL_COMPILER)      ||  \
1637      defined(__INTEL_CLANG_COMPILER) ||  \
1638      defined(__INTEL_LLVM_COMPILER)  ||  \
1639      defined(INTEL_MKL_VERSION)      ||  \
1640      defined(__INTEL_MKL__)
1641 (void)agner_compiler_patch();
1642 #  endif
1643 # endif
1644 
1645 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1646 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1647 if (!targv)
1648   {
1649     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1650              __func__, __FILE__, __LINE__);
1651 # if defined(USE_BACKTRACE)
1652 #  ifdef SIGUSR2
1653     (void)raise(SIGUSR2);
1654     /*NOTREACHED*/ /* unreachable */
1655 #  endif /* ifdef SIGUSR2 */
1656 # endif /* if defined(USE_BACKTRACE) */
1657     abort();
1658   }
1659 for (i=0; i<argc; i++)
1660     targv[i] = argv[i];
1661 argv = targv;
1662 
1663 /* setup defaults */
1664 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1665 *cbuf = 0;                                              /* init arg buffer */
1666 sim_switches = 0;                                       /* init switches */
1667 lookswitch = TRUE;
1668 stdnul = fopen(NULL_DEVICE,"wb");
1669 
1670 /* process arguments */
1671 for (i = 1; i < argc; i++) {                            /* loop thru args */
1672     if (argv[i] == NULL)                                /* paranoia */
1673         continue;
1674 
1675 /* requested only version? */
1676     int onlyvers  = strcmp(argv[i], "--version");
1677     if (onlyvers == 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\n",
1682                  sim_name, VER_H_GIT_VERSION);
1683 #   else
1684         fprintf (stdout, "%s simulator %s+%s\n",
1685                  sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1686 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1687 #  else
1688         fprintf (stdout, "%s simulator %s\n",
1689                  sim_name, VER_H_GIT_VERSION);
1690 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1691 # else
1692         fprintf (stdout, "%s simulator\n", sim_name);
1693 # endif /* ifdef VER_H_GIT_VERSION */
1694         FREE (targv);
1695         return 0;
1696     }
1697 
1698 /* requested short or long help? */
1699     int longhelp  = strcmp(argv[i], "--help");
1700     int shorthelp = strcmp(argv[i], "-h");
1701     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1702     if (longhelp == 0 || shorthelp == 0) {
1703 # ifdef VER_H_GIT_VERSION
1704 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1705 #   if VER_H_GIT_PATCH_INT < 1
1706         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1707 #   else
1708         fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1709 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1710 #  else
1711         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1712 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1713 # else
1714         fprintf (stdout, "%s simulator", sim_name);
1715 # endif /* ifdef VER_H_GIT_VERSION */
1716         fprintf (stdout, "\n");
1717         fprintf (stdout, "\n USAGE: %s { [ SWITCHES ] ... } { < SCRIPT > }", argv[0]);
1718         fprintf (stdout, "\n");
1719         fprintf (stdout, "\n Invokes the %s simulator, with optional switches and/or script file.", sim_name);
1720         fprintf (stdout, "\n");
1721         fprintf (stdout, "\n Switches:");
1722         fprintf (stdout, "\n  -e, -E            Aborts script processing immediately upon any error");
1723         fprintf (stdout, "\n  -h, -H, --help    Prints only this informational help text and exit");
1724         fprintf (stdout, "\n  -k, -K            Disables all support for exclusive file locking");
1725         fprintf (stdout, "\n  -l, -L            Reports but ignores all exclusive file locking errors");
1726         fprintf (stdout, "\n  -o, -O            Makes scripting ON conditions and actions inheritable");
1727         fprintf (stdout, "\n  -q, -Q            Disables printing of non-fatal informational messages");
1728         fprintf (stdout, "\n  -r, -R            Enables an unlinked ephemeral system state file");
1729         fprintf (stdout, "\n  -s, -S            Enables a randomized persistent system state file");
1730         fprintf (stdout, "\n  -t, -T            Disables fsync and creation/usage of system state file");
1731         fprintf (stdout, "\n  -v, -V            Prints commands read from script file before execution");
1732         fprintf (stdout, "\n  --version         Prints only the simulator identification text and exit");
1733         fprintf (stdout, "\n");
1734 # ifdef USE_DUMA
1735         nodist++;
1736 # endif /* ifdef USE_DUMA */
1737 if (!nodist) {
1738         fprintf (stdout, "\n This software is made available under the terms of the ICU License, version");
1739         fprintf (stdout, "\n 1.8.1 or later.  For complete details, see the \"LICENSE.md\" file included");
1740         fprintf (stdout, "\n with the software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1741 }
1742 else
1743 {
1744         fprintf (stdout, "\n********** LICENSE RESTRICTED BUILD ****** NOT FOR REDISTRIBUTION **********");
1745 }
1746         fprintf (stdout, "\n");
1747         FREE(argv); //-V726
1748         return 0;
1749     }
1750     /* invalid arguments? */
1751     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
1752         if ((sw = get_switches (argv[i])) < 0) {
1753             fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1754             FREE(argv); //-V726
1755             return 1;
1756             }
1757         sim_switches = sim_switches | sw;
1758         }
1759     /* parse arguments */
1760     else {
1761         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1762             fprintf (stderr, "Argument string too long\n");
1763             FREE(argv); //-V726
1764             return 1;
1765             }
1766         if (*cbuf)                                  /* concat args */
1767             strcat (cbuf, " ");
1768         sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : "");  //-V755
1769         lookswitch = FALSE;                         /* no more switches */
1770         }
1771     }                                               /* end for */
1772 sim_nolock = sim_switches & SWMASK ('K');           /* -k means skip locking     */
1773 sim_iglock = sim_switches & SWMASK ('L');           /* -l means ignore locking   */
1774 sim_randompst = sim_switches & SWMASK ('S');        /* -s means persist random   */
1775 sim_quiet = sim_switches & SWMASK ('Q');            /* -q means quiet            */
1776 sim_randstate = sim_switches & SWMASK ('R');        /* -r means random sys_state */
1777 if (sim_randompst) sim_randstate = 1;               /*    and is implied with -s */
1778 sim_nostate = sim_switches & SWMASK ('T');          /* -t means no sys_state     */
1779 if (sim_nostate)                                    /*    and disables -s and -r */
1780   {
1781     sim_randompst = 0;
1782     sim_randstate = 0;
1783   }
1784 sim_on_inherit = sim_switches & SWMASK ('O');       /* -o means inherit on state */
1785 
1786 sim_init_sock ();                                   /* init socket capabilities */
1787 if (sim_dflt_dev == NULL)                           /* if no default */
1788     sim_dflt_dev = sim_devices[0];
1789 if (sim_vm_init != NULL)                            /* call once only */
1790     (*sim_vm_init)();
1791 sim_finit ();                                       /* init fio package */
1792 for (i = 0; cmd_table[i].name; i++) {
1793     size_t alias_len = strlen (cmd_table[i].name);
1794     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
1795     if (!cmd_name)
1796       {
1797         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1798                  __func__, __FILE__, __LINE__);
1799 # if defined(USE_BACKTRACE)
1800 #  ifdef SIGUSR2
1801         (void)raise(SIGUSR2);
1802         /*NOTREACHED*/ /* unreachable */
1803 #  endif /* ifdef SIGUSR2 */
1804 # endif /* if defined(USE_BACKTRACE) */
1805         abort();
1806       }
1807 
1808     strcpy (cmd_name, cmd_table[i].name);
1809     while (alias_len > 1) {
1810         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
1811         --alias_len;
1812         if (getenv (cmd_name))                      /* Externally defined command alias? */
1813             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
1814         }
1815     FREE (cmd_name);
1816     }
1817 stop_cpu = 0;
1818 sim_interval = 0;
1819 sim_time = sim_rtime = 0;
1820 noqueue_time = 0;
1821 sim_clock_queue = QUEUE_LIST_END;
1822 sim_is_running = 0;
1823 sim_log = NULL;
1824 if (sim_emax <= 0)
1825     sim_emax = 1;
1826 sim_timer_init ();
1827 
1828 if ((stat = sim_ttinit ()) != SCPE_OK) {
1829     fprintf (stderr, "Fatal terminal initialization error\n%s\n",
1830         sim_error_text (stat));
1831     FREE(argv); //-V726
1832     return 1;
1833     }
1834 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
1835     fprintf (stderr, "Unable to allocate examine buffer\n");
1836     FREE(argv); //-V726
1837     return 1;
1838     };
1839 if ((stat = reset_all_p (0)) != SCPE_OK) {
1840     fprintf (stderr, "Fatal simulator initialization error\n%s\n",
1841         sim_error_text (stat));
1842     FREE(argv); //-V726
1843     return 1;
1844     }
1845 if ((stat = sim_brk_init ()) != SCPE_OK) {
1846     fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
1847         sim_error_text (stat));
1848     FREE(argv); //-V726
1849     return 1;
1850     }
1851 if (!sim_quiet) {
1852     printf ("\n");
1853     show_version (stdout, NULL, NULL, 0, NULL);
1854     }
1855 
1856 cptr = getenv("HOME");
1857 if (cptr == NULL) {
1858     cptr = getenv("HOMEPATH");
1859     cptr2 = getenv("HOMEDRIVE");
1860     }
1861 else
1862     cptr2 = NULL;
1863 (void)cptr2;
1864 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
1865     stat = do_cmd (0, cbuf);                            /* proc cmd file */
1866 else if (*argv[0]) {                                    /* sim name arg? */
1867     char *np;                                           /* "path.ini" */
1868     nbuf[0] = '"';                                      /* starting " */
1869     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
1870     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
1871         np = strrchr (nbuf, '/');                       /* stript path and try again in cwd */
1872         if (np == NULL)
1873             np = strrchr (nbuf, '\\');                  /* windows path separator */
1874         if (np != NULL) {
1875             *np = '"';
1876             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
1877             }
1878         }
1879     }
1880 
1881 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
1882 
1883 if (sim_vm_exit != NULL)                                /* call once only */
1884     (*sim_vm_exit)();
1885 
1886 detach_all (0, TRUE);                                   /* close files */
1887 sim_set_deboff (0, NULL);                               /* close debug */
1888 sim_set_logoff (0, NULL);                               /* close log */
1889 sim_set_notelnet (0, NULL);                             /* close Telnet */
1890 sim_ttclose ();                                         /* close console */
1891 sim_cleanup_sock ();                                    /* cleanup sockets */
1892 fclose (stdnul);                                        /* close bit bucket file handle */
1893 FREE (targv);                                           /* release any argv copy that was made */
1894 FREE (sim_prompt);
1895 FREE (sim_eval);
1896 FREE (sim_internal_devices);
1897 FREE (sim_brk_tab);
1898 FREE (sim_staba.comp);
1899 FREE (sim_staba.mask);
1900 FREE (sim_stabr.comp);
1901 FREE (sim_stabr.mask);
1902 return 0;
1903 }
1904 #endif
1905 
1906 t_stat process_stdin_commands (t_stat stat, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1907 {
1908 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
1909 CONST char *cptr;
1910 t_stat stat_nomessage;
1911 CTAB *cmdp = NULL;
1912 
1913 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
1914 while (stat != SCPE_EXIT) {                             /* in case exit */
1915     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
1916         printf ("%s%s\n", sim_prompt, cptr);            /* echo */
1917     else if (sim_vm_read != NULL) {                     /* sim routine? */
1918         printf ("%s", sim_prompt);                      /* prompt */
1919         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
1920         }
1921     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prompt*/
1922     if (cptr == NULL) {                                 /* EOF? */
1923         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
1924         else break;                                     /* otherwise exit */
1925         }
1926     if (*cptr == 0)                                     /* ignore blank */
1927         continue;
1928     sim_sub_args (cbuf, sizeof(cbuf), argv);
1929     if (sim_log)                                        /* log cmd */
1930         fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
1931     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
1932         fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
1933     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
1934     sim_switches = 0;                                   /* init switches */
1935     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
1936         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
1937     else
1938         stat = SCPE_UNK;
1939     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
1940     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
1941     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
1942     sim_last_cmd_stat = stat;                           /* save command error status */
1943     if (!stat_nomessage) {                              /* displaying message status? */
1944         if (cmdp && (cmdp->message))                    /* special message handler? */
1945             cmdp->message (NULL, stat);                 /* let it deal with display */
1946         else
1947             if (stat >= SCPE_BASE)                      /* error? */
1948                 sim_printf ("%s\n", sim_error_text (stat));
1949         }
1950     if (sim_vm_post != NULL)
1951         (*sim_vm_post) (TRUE);
1952     }                                                   /* end while */
1953 return stat;
1954 }
1955 
1956 /* Set prompt routine */
1957 
1958 t_stat set_prompt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1959 {
1960 char gbuf[CBUFSIZE], *gptr;
1961 
1962 if ((!cptr) || (*cptr == '\0'))
1963     return SCPE_ARG;
1964 
1965 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
1966 if (gbuf[0] == '\0') {                                  /* Token started with quote */
1967     gbuf[sizeof (gbuf)-1] = '\0';
1968     strncpy (gbuf, cptr, sizeof (gbuf)-1);
1969     gptr = strchr (gbuf, '"');
1970     if (gptr)
1971         *gptr = '\0';
1972     }
1973 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
1974 if (!sim_prompt)
1975   {
1976     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1977              __func__, __FILE__, __LINE__);
1978 #if defined(USE_BACKTRACE)
1979 # ifdef SIGUSR2
1980     (void)raise(SIGUSR2);
1981     /*NOTREACHED*/ /* unreachable */
1982 # endif /* ifdef SIGUSR2 */
1983 #endif /* if defined(USE_BACKTRACE) */
1984     abort();
1985   }
1986 sprintf (sim_prompt, "%s ", gbuf);
1987 return SCPE_OK;
1988 }
1989 
1990 /* Find command routine */
1991 
1992 CTAB *find_cmd (const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
1993 {
1994 CTAB *cmdp = NULL;
1995 
1996 if (sim_vm_cmd)                                         /* try ext commands */
1997     cmdp = find_ctab (sim_vm_cmd, gbuf);
1998 if (cmdp == NULL)                                       /* try regular cmds */
1999     cmdp = find_ctab (cmd_table, gbuf);
2000 return cmdp;
2001 }
2002 
2003 /* Exit command */
2004 
2005 t_stat exit_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2006 {
2007 return SCPE_EXIT;
2008 }
2009 
2010 /* Help command */
2011 
2012 /* Used when sorting a list of command names */
2013 static int _cmd_name_compare (const void *pa, const void *pb)
     /* [previous][next][first][last][top][bottom][index][help] */
2014 {
2015 CTAB * const *a = (CTAB * const *)pa;
2016 CTAB * const *b = (CTAB * const *)pb;
2017 
2018 return strcmp((*a)->name, (*b)->name);
2019 }
2020 
2021 void fprint_help (FILE *st)
     /* [previous][next][first][last][top][bottom][index][help] */
2022 {
2023 CTAB *cmdp;
2024 CTAB **hlp_cmdp = NULL;
2025 int cmd_cnt = 0;
2026 int cmd_size = 0;
2027 size_t max_cmdname_size = 0;
2028 int i, line_offset;
2029 
2030 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
2031     if (cmdp->help) {
2032         if (cmd_cnt >= cmd_size) {
2033             cmd_size += 20;
2034             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2035             if (!hlp_cmdp)
2036               {
2037                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2038                          __func__, __FILE__, __LINE__);
2039 #if defined(USE_BACKTRACE)
2040 # ifdef SIGUSR2
2041                 (void)raise(SIGUSR2);
2042                 /*NOTREACHED*/ /* unreachable */
2043 # endif /* ifdef SIGUSR2 */
2044 #endif /* if defined(USE_BACKTRACE) */
2045                 abort();
2046               }
2047             }
2048         hlp_cmdp[cmd_cnt] = cmdp;
2049         ++cmd_cnt;
2050         if (strlen(cmdp->name) > max_cmdname_size)
2051             max_cmdname_size = strlen(cmdp->name);
2052         }
2053     }
2054 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
2055     if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) {
2056         if (cmd_cnt >= cmd_size) {
2057             cmd_size += 20;
2058             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2059             if (!hlp_cmdp)
2060               {
2061                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2062                          __func__, __FILE__, __LINE__);
2063 #if defined(USE_BACKTRACE)
2064 # ifdef SIGUSR2
2065                 (void)raise(SIGUSR2);
2066                 /*NOTREACHED*/ /* unreachable */
2067 # endif /* ifdef SIGUSR2 */
2068 #endif /* if defined(USE_BACKTRACE) */
2069                 abort();
2070               }
2071             }
2072         hlp_cmdp[cmd_cnt] = cmdp;
2073         ++cmd_cnt;
2074         if (strlen (cmdp->name) > max_cmdname_size)
2075             max_cmdname_size = strlen(cmdp->name);
2076         }
2077     }
2078 fprintf (st, "HELP is available for the following commands:\n\n    ");
2079 if (hlp_cmdp)
2080   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
2081 line_offset = 4;
2082 for ( i = 0 ; i < cmd_cnt ; ++i ) {
2083     fputs (hlp_cmdp[i]->name, st);
2084     line_offset += 5 + max_cmdname_size;
2085     if (line_offset + max_cmdname_size > 79) {
2086         line_offset = 4;
2087         fprintf (st, "\n    ");
2088         }
2089     else
2090         fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
2091     }
2092 FREE (hlp_cmdp);
2093 fprintf (st, "\n");
2094 return;
2095 }
2096 
2097 static void fprint_header (FILE *st, t_bool *pdone, char *context)
     /* [previous][next][first][last][top][bottom][index][help] */
2098 {
2099 if (!*pdone)
2100     fprintf (st, "%s", context);
2101 *pdone = TRUE;
2102 }
2103 
2104 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2105 {
2106 REG *rptr, *trptr;
2107 t_bool found = FALSE;
2108 t_bool all_unique = TRUE;
2109 size_t max_namelen = 0;
2110 DEVICE *tdptr;
2111 CONST char *tptr;
2112 char *namebuf;
2113 char rangebuf[32];
2114 
2115 if (dptr->registers)
2116     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2117         if (rptr->flags & REG_HIDDEN)
2118             continue;
2119         if (rptr->depth > 1)
2120             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2121         else
2122             strcpy (rangebuf, "");
2123         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2124             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2125         found = TRUE;
2126         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2127         if ((trptr == NULL) || (tdptr != dptr))
2128             all_unique = FALSE;
2129         }
2130 if (!found) {
2131     if (!silent)
2132         fprintf (st, "No register HELP available for the %s device\n", dptr->name);
2133     }
2134 else {
2135     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2136     if (!namebuf)
2137       {
2138         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2139                  __func__, __FILE__, __LINE__);
2140 #if defined(USE_BACKTRACE)
2141 # ifdef SIGUSR2
2142         (void)raise(SIGUSR2);
2143         /*NOTREACHED*/ /* unreachable */
2144 # endif /* ifdef SIGUSR2 */
2145 #endif /* if defined(USE_BACKTRACE) */
2146         abort();
2147       }
2148     fprintf (st, "\nThe %s device implements these registers:\n\n", dptr->name);
2149     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2150         if (rptr->flags & REG_HIDDEN)
2151             continue;
2152         if (rptr->depth <= 1)
2153             sprintf (namebuf, "%*s", -((int)max_namelen), rptr->name);
2154         else {
2155             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2156             sprintf (namebuf, "%s%*s", rptr->name, (int)(strlen(rptr->name))-((int)max_namelen), rangebuf);
2157             }
2158         if (all_unique) {
2159             fprintf (st, "  %s %4d  %s\n", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2160             continue;
2161             }
2162         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2163         if ((trptr == NULL) || (tdptr != dptr))
2164             fprintf (st, "  %s %s %4d  %s\n", dptr->name, namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2165         else
2166             fprintf (st, "  %*s %s %4d  %s\n", (int)strlen(dptr->name), "", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2167         }
2168     FREE (namebuf);
2169     }
2170 }
2171 
2172 void fprint_reg_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2173 {
2174 fprint_reg_help_ex (st, dptr, TRUE);
2175 }
2176 
2177 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2178 {
2179 if (dptr->attach_help) {
2180     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2181     dptr->attach_help (st, dptr, NULL, 0, NULL);
2182     return;
2183     }
2184 if (DEV_TYPE(dptr) == DEV_DISK) {
2185     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2186     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2187     return;
2188     }
2189 if (DEV_TYPE(dptr) == DEV_TAPE) {
2190     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2191     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2192     return;
2193     }
2194 if (!silent) {
2195     fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2196     if (dptr->help)
2197         dptr->help (st, dptr, NULL, 0, NULL);
2198     }
2199 }
2200 
2201 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2202 {
2203 MTAB *mptr;
2204 DEBTAB *dep;
2205 t_bool found = FALSE;
2206 char buf[CBUFSIZE], header[CBUFSIZE];
2207 uint32 enabled_units = dptr->numunits;
2208 uint32 unit;
2209 
2210 sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2211 for (unit=0; unit < dptr->numunits; unit++)
2212     if (dptr->units[unit].flags & UNIT_DIS)
2213         --enabled_units;
2214 if (dptr->modifiers) {
2215     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2216         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2217             continue;                                       /* skip unit only extended modifiers */
2218         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2219             continue;                                       /* skip unit only simple modifiers */
2220         if (mptr->mstring) {
2221             fprint_header (st, &found, header);
2222             sprintf (buf, "SET %s %s%s", sim_dname (dptr), mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}" : "")));
2223             if ((strlen (buf) < 30) || (!mptr->help))
2224                 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2225             else
2226                 fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2227             }
2228         }
2229     }
2230 if (dptr->flags & DEV_DISABLE) {
2231     fprint_header (st, &found, header);
2232     sprintf (buf, "SET %s ENABLE", sim_dname (dptr));
2233     fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2234     sprintf (buf, "SET %s DISABLE", sim_dname (dptr));
2235     fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2236     }
2237 if (dptr->flags & DEV_DEBUG) {
2238     fprint_header (st, &found, header);
2239     sprintf (buf, "SET %s DEBUG", sim_dname (dptr));
2240     fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2241     sprintf (buf, "SET %s NODEBUG", sim_dname (dptr));
2242     fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2243     if (dptr->debflags) {
2244         t_bool desc_available = FALSE;
2245         strcpy (buf, "");
2246         fprintf (st, "SET %s DEBUG=", sim_dname (dptr));
2247         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2248             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2249             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2250             }
2251         fprintf (st, "\n");
2252         fprintf (st,  "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2253         fprintf (st, "SET %s NODEBUG=", sim_dname (dptr));
2254         for (dep = dptr->debflags; dep->name != NULL; dep++)
2255             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2256         fprintf (st, "\n");
2257         fprintf (st,  "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2258         if (desc_available) {
2259             fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2260             for (dep = dptr->debflags; dep->name != NULL; dep++)
2261                 fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2262             }
2263         }
2264     }
2265 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2266     if (dptr->units->flags & UNIT_DISABLE) {
2267         fprint_header (st, &found, header);
2268         sprintf (buf, "SET %sn ENABLE", sim_dname (dptr));
2269         fprintf (st,  "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2270         sprintf (buf, "SET %sn DISABLE", sim_dname (dptr));
2271         fprintf (st,  "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2272         }
2273     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2274         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2275             continue;                                           /* skip device only modifiers */
2276         if ((!mptr->valid) && MODMASK(mptr,MTAB_XTD))
2277             continue;                                           /* skip show only modifiers */
2278         if (mptr->mstring) {
2279             fprint_header (st, &found, header);
2280             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}": "")));
2281             fprintf (st, "%-30s\t%s\n", buf, (strchr(mptr->mstring, '=')) ? "" : (mptr->help ? mptr->help : ""));
2282             }
2283         }
2284     }
2285 if (!found && !silent)
2286     fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2287 }
2288 
2289 void fprint_set_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2290 {
2291   fprint_set_help_ex (st, dptr, TRUE);
2292 }
2293 
2294 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2295 {
2296 MTAB *mptr;
2297 t_bool found = FALSE;
2298 char buf[CBUFSIZE], header[CBUFSIZE];
2299 uint32 enabled_units = dptr->numunits;
2300 uint32 unit;
2301 
2302 sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2303 for (unit=0; unit < dptr->numunits; unit++)
2304     if (dptr->units[unit].flags & UNIT_DIS)
2305         --enabled_units;
2306 if (dptr->modifiers) {
2307     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2308         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2309             continue;                                       /* skip unit only extended modifiers */
2310         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2311             continue;                                       /* skip unit only simple modifiers */
2312         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2313             continue;
2314         fprint_header (st, &found, header);
2315         sprintf (buf, "SHOW %s %s%s", sim_dname (dptr), mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2316         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2317         }
2318     }
2319 if (dptr->flags & DEV_DEBUG) {
2320     fprint_header (st, &found, header);
2321     sprintf (buf, "SHOW %s DEBUG", sim_dname (dptr));
2322     fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2323     }
2324 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2325     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2326         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2327             continue;                                           /* skip device only modifiers */
2328         if ((!mptr->disp) || (!mptr->pstring))
2329             continue;
2330         fprint_header (st, &found, header);
2331         sprintf (buf, "SHOW %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2332         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2333         }
2334     }
2335 if (!found && !silent)
2336     fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2337 }
2338 
2339 void fprint_show_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2340     {
2341     fprint_show_help_ex (st, dptr, TRUE);
2342     }
2343 
2344 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2345 {
2346 BRKTYPTAB *brkt = dptr->brk_types;
2347 char gbuf[CBUFSIZE];
2348 
2349 if (sim_brk_types == 0) {
2350     if ((dptr != sim_dflt_dev) && (!silent)) {
2351         fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2352         if (dptr->help)
2353             dptr->help (st, dptr, NULL, 0, NULL);
2354         }
2355     return;
2356     }
2357 if (brkt == NULL) {
2358     int i;
2359 
2360     if (dptr == sim_dflt_dev) {
2361         if (sim_brk_types & ~sim_brk_dflt) {
2362             fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2363             for (i=0; i<26; i++) {
2364                 if (sim_brk_types & (1<<i))
2365                     fprintf (st, "  -%c\n", 'A'+i);
2366                 }
2367             }
2368         fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2369         }
2370     return;
2371     }
2372 fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2373 while (brkt->btyp) {
2374     fprintf (st, "  %s     %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2375     ++brkt;
2376     }
2377 fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2378 }
2379 
2380 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2381 {
2382 char gbuf[CBUFSIZE];
2383 CTAB *cmdp;
2384 
2385 if (*cptr) {
2386     (void)get_glyph (cptr, gbuf, 0);
2387     if ((cmdp = find_cmd (gbuf))) {
2388         if (cmdp->action == &exdep_cmd) {
2389             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2390                 return dptr->help (st, dptr, uptr, flag, cptr);
2391             else
2392                 fprintf (st, "No HELP available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2393             return SCPE_OK;
2394             }
2395         if (cmdp->action == &set_cmd) {
2396             fprint_set_help_ex (st, dptr, FALSE);
2397             return SCPE_OK;
2398             }
2399         if (cmdp->action == &show_cmd) {
2400             fprint_show_help_ex (st, dptr, FALSE);
2401             return SCPE_OK;
2402             }
2403         if (cmdp->action == &attach_cmd) {
2404             fprint_attach_help_ex (st, dptr, FALSE);
2405             return SCPE_OK;
2406             }
2407         if (cmdp->action == &brk_cmd) {
2408             fprint_brk_help_ex (st, dptr, FALSE);
2409             return SCPE_OK;
2410             }
2411         if (dptr->help)
2412             return dptr->help (st, dptr, uptr, flag, cptr);
2413         fprintf (st, "No %s HELP is available for the %s device\n", cmdp->name, dptr->name);
2414         return SCPE_OK;
2415         }
2416     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2417         fprint_reg_help_ex (st, dptr, FALSE);
2418         return SCPE_OK;
2419         }
2420     if (dptr->help)
2421         return dptr->help (st, dptr, uptr, flag, cptr);
2422     fprintf (st, "No %s HELP is available for the %s device\n", gbuf, dptr->name);
2423     return SCPE_OK;
2424     }
2425 if (dptr->help) {
2426     return dptr->help (st, dptr, uptr, flag, cptr);
2427     }
2428 if (dptr->description)
2429     fprintf (st, "%s %s HELP\n", dptr->description (dptr), dptr->name);
2430 else
2431     fprintf (st, "%s HELP\n", dptr->name);
2432 fprint_set_help_ex    (st, dptr, TRUE);
2433 fprint_show_help_ex   (st, dptr, TRUE);
2434 fprint_attach_help_ex (st, dptr, TRUE);
2435 fprint_reg_help_ex    (st, dptr, TRUE);
2436 fprint_brk_help_ex    (st, dptr, TRUE);
2437 return SCPE_OK;
2438 }
2439 
2440 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
     /* [previous][next][first][last][top][bottom][index][help] */
2441 {
2442 switch (help[0]) {
2443     case '*':
2444         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2445         if (sim_log)
2446             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2447         break;
2448     default:
2449         fputs (help, stdout);
2450         if (sim_log)
2451             fputs (help, sim_log);
2452         break;
2453     }
2454 return SCPE_OK;
2455 }
2456 
2457 t_stat help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2458 {
2459 char gbuf[CBUFSIZE];
2460 CTAB *cmdp;
2461 
2462 GET_SWITCHES (cptr);
2463 if (sim_switches & SWMASK ('F'))
2464     flag = flag | SCP_HELP_FLAT;
2465 if (*cptr) {
2466     cptr = get_glyph (cptr, gbuf, 0);
2467     if ((cmdp = find_cmd (gbuf))) {
2468         if (*cptr) {
2469             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2470                 DEVICE *dptr;
2471                 UNIT *uptr;
2472                 t_stat r;
2473                 cptr = get_glyph (cptr, gbuf, 0);
2474                 dptr = find_unit (gbuf, &uptr);
2475                 if (dptr == NULL)
2476                     dptr = find_dev (gbuf);
2477                 if (dptr != NULL) {
2478                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2479                     if (sim_log)
2480                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2481                     return r;
2482                     }
2483                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2484                     /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2485                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2486                          (cmdp->help))
2487                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2488                     }
2489                 else { /* HELP SHOW xxx (not device or unit) */
2490                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2491                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2492                         return SCPE_ARG;
2493                     return help_cmd_output (flag, shptr->help, NULL);
2494                     }
2495                 return SCPE_ARG;
2496                 }
2497             else
2498                 return SCPE_2MARG;
2499             }
2500         if (cmdp->help) {
2501             if (strcmp (cmdp->name, "HELP") == 0) {
2502 
2503 
2504 
2505 
2506 
2507 
2508 
2509 
2510 
2511 
2512 
2513 
2514 
2515 
2516 
2517 
2518 
2519 
2520 
2521 
2522 
2523 
2524 
2525 
2526 
2527 
2528 
2529 
2530 
2531 
2532                 }
2533             else {
2534                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2535                     sim_dflt_dev && sim_dflt_dev->help) {
2536                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2537                         if (sim_log)
2538                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2539                     }
2540                 }
2541             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2542             }
2543         else { /* no help so it is likely a command alias */
2544             CTAB *cmdpa;
2545             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2546                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2547                     sim_printf ("%s is an alias for the %s command:\n%s",
2548                                 cmdp->name, cmdpa->name, cmdpa->help);
2549                     break;
2550                     }
2551             if (cmdpa->name == NULL)                /* not found? */
2552                 sim_printf ("No help available for the %s command\n", cmdp->name);
2553             }
2554         }
2555     else {
2556         DEVICE *dptr;
2557         UNIT *uptr;
2558         t_stat r;
2559         dptr = find_unit (gbuf, &uptr);
2560         if (dptr == NULL) {
2561             dptr = find_dev (gbuf);
2562             if (dptr == NULL)
2563                 return SCPE_ARG;
2564             if (dptr->flags & DEV_DIS)
2565                 sim_printf ("Device %s is currently disabled\n", dptr->name);
2566             }
2567         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2568         if (sim_log)
2569             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2570         return r;
2571         }
2572     }
2573 else {
2574     fprint_help (stdout);
2575     if (sim_log)
2576         fprint_help (sim_log);
2577     }
2578 return SCPE_OK;
2579 }
2580 
2581 /* Spawn command */
2582 
2583 t_stat spawn_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2584 {
2585 t_stat status;
2586 if ((cptr == NULL) || (strlen (cptr) == 0))
2587     cptr = getenv("SHELL");
2588 if ((cptr == NULL) || (strlen (cptr) == 0))
2589     cptr = getenv("ComSpec");
2590 fflush(stdout);                                         /* flush stdout */
2591 if (sim_log)                                            /* flush log if enabled */
2592     fflush (sim_log);
2593 if (sim_deb)                                            /* flush debug if enabled */
2594     fflush (sim_deb);
2595 status = system (cptr);
2596 
2597 return status;
2598 }
2599 
2600 /* Echo command */
2601 
2602 t_stat echo_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2603 {
2604 sim_printf ("%s\n", cptr);
2605 return SCPE_OK;
2606 }
2607 
2608 /*
2609  * DO command
2610  *
2611  * Note that SCPE_STEP ("Step expired") is considered a note and
2612  * not an error; it does not abort command execution when using -E.
2613  *
2614  * Inputs:
2615  *      flag    =   caller and nesting level indicator
2616  *      fcptr   =   filename and optional arguments, space-separated
2617  * Outputs:
2618  *      status  =   error status
2619  *
2620  * The "flag" input value indicates the source of the call, as follows:
2621  *
2622  *      -1      =   initialization file (no error if not found)
2623  *       0      =   command line file
2624  *       1      =   "DO" command
2625  *      >1      =   nested "DO" command
2626  */
2627 
2628 t_stat do_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2629 {
2630 return do_cmd_label (flag, fcptr, NULL);
2631 }
2632 
2633 static char *do_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2634 {
2635 static char cbuf[4*CBUFSIZE];
2636 
2637 snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth],
2638     sim_do_label[sim_do_depth] ? "::" : "",
2639     sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "",
2640     sim_goto_line[sim_do_depth]);
2641 return cbuf;
2642 }
2643 
2644 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
2645 {
2646 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2647 CONST char *cptr;
2648 FILE *fpin;
2649 CTAB *cmdp = NULL;
2650 int32 echo, nargs, errabort, i;
2651 int32 saved_sim_do_echo = sim_do_echo,
2652       saved_sim_show_message = sim_show_message,
2653       saved_sim_on_inherit = sim_on_inherit,
2654       saved_sim_quiet = sim_quiet;
2655 t_bool staying;
2656 t_stat stat, stat_nomessage;
2657 
2658 stat = SCPE_OK;
2659 staying = TRUE;
2660 if (flag > 0)                                           /* need switches? */
2661     GET_SWITCHES (fcptr);                               /* get switches */
2662 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
2663 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2664 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2665 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
2666 
2667 abuf[sizeof(abuf)-1] = '\0';
2668 strncpy (abuf, fcptr, sizeof(abuf)-1);
2669 c = abuf;
2670 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
2671 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
2672     while (sim_isspace (*c))                            /* skip blanks */
2673         c++;
2674     if (*c == 0)                                        /* all done? */
2675         do_arg [nargs++] = NULL;                        /* null argument */
2676     else {
2677         if (*c == '\'' || *c == '"')                    /* quoted string? */
2678             quote = *c++;
2679         else quote = 0;
2680         do_arg[nargs++] = c;                            /* save start */
2681         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2682             c++;
2683         if (*c)                                         /* term at quote/spc */
2684             *c++ = 0;
2685         }
2686     }                                                   /* end for */
2687 
2688 if (do_arg [0] == NULL)                                 /* need at least 1 */
2689     return SCPE_2FARG;
2690 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
2691     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
2692     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
2693         if (flag == 0)                                  /* cmd line file? */
2694              fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2695         return SCPE_OPENERR;                            /* return failure */
2696         }
2697     }
2698 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2699     ++sim_do_depth;
2700     if (sim_on_inherit) {                               /* inherit ON condition actions? */
2701         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2702         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
2703             if (sim_on_actions[sim_do_depth-1][i]) {
2704                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2705                 if (NULL == sim_on_actions[sim_do_depth][i]) {
2706                     while (--i >= 0) {
2707                         FREE(sim_on_actions[sim_do_depth][i]);
2708                         sim_on_actions[sim_do_depth][i] = NULL;
2709                         }
2710                     sim_on_check[sim_do_depth] = 0;
2711                     sim_brk_clract ();                  /* defang breakpoint actions */
2712                     --sim_do_depth;                     /* unwind nesting */
2713                     fclose(fpin);
2714                     return SCPE_MEM;
2715                     }
2716                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2717                 }
2718             }
2719         }
2720     }
2721 
2722 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
2723 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
2724 sim_goto_line[sim_do_depth] = 0;
2725 if (label) {
2726     sim_gotofile = fpin;
2727     sim_do_echo = echo;
2728     stat = goto_cmd (0, label);
2729     if (stat != SCPE_OK) {
2730         strcpy(cbuf, "RETURN SCPE_ARG");
2731         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
2732         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
2733         goto Cleanup_Return;
2734         }
2735     }
2736 if (errabort)                                           /* -e flag? */
2737     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
2738 
2739 do {
2740     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
2741     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
2742         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
2743         sim_goto_line[sim_do_depth] += 1;
2744         }
2745     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
2746     if (cptr == NULL) {                                 /* EOF? */
2747         stat = SCPE_OK;                                 /* set good return */
2748         break;
2749         }
2750     if (*cptr == 0)                                     /* ignore blank */
2751         continue;
2752     if (echo)                                           /* echo if -v */
2753         sim_printf("%s> %s\n", do_position(), cptr);
2754     if (*cptr == ':')                                   /* ignore label */
2755         continue;
2756     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2757     sim_switches = 0;                                   /* init switches */
2758     sim_gotofile = fpin;
2759     sim_do_echo = echo;
2760     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
2761         if (cmdp->action == &return_cmd)                /* RETURN command? */
2762             break;                                      /*    done! */
2763         if (cmdp->action == &do_cmd) {                  /* DO command? */
2764             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
2765                 stat = SCPE_NEST;
2766             else
2767                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
2768             }
2769         else
2770             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
2771         }
2772     else stat = SCPE_UNK;                               /* bad cmd given */
2773     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
2774     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
2775     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2776     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2777     if (cmdp)
2778       if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
2779           ((cmdp->action != &return_cmd) &&
2780            (cmdp->action != &goto_cmd) &&
2781            (cmdp->action != &on_cmd) &&
2782            (cmdp->action != &echo_cmd)))
2783         sim_last_cmd_stat = stat;                       /* save command error status */
2784     switch (stat) {
2785         case SCPE_AFAIL:
2786             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
2787                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
2788             break;
2789         case SCPE_EXIT:
2790             staying = FALSE;
2791             break;
2792         case SCPE_OK:
2793         case SCPE_STEP:
2794             break;
2795         default:
2796             break;
2797         }
2798     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
2799         (stat != SCPE_STEP)) {
2800         if (!echo && !sim_quiet &&                      /* report if not echoing */
2801             !stat_nomessage &&                          /* and not suppressing messages */
2802             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
2803             sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
2804             }
2805         }
2806     if (!stat_nomessage) {                              /* report error if not suppressed */
2807         if (cmdp && cmdp->message)                      /* special message handler */
2808             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
2809         else
2810             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
2811                 sim_printf ("%s\n", sim_error_text (stat));
2812         }
2813     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
2814         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
2815     if (staying &&
2816         (sim_on_check[sim_do_depth]) &&
2817         (stat != SCPE_OK) &&
2818         (stat != SCPE_STEP)) {
2819         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
2820             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
2821         else
2822             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
2823         }
2824     if (sim_vm_post != NULL)
2825         (*sim_vm_post) (TRUE);
2826     } while (staying);
2827 Cleanup_Return:
2828 if (fpin) //-V547
2829     fclose (fpin);                                      /* close file */
2830 sim_gotofile = NULL;
2831 if (flag >= 0) {
2832     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
2833     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
2834     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
2835     }
2836 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
2837 if ((flag >= 0) || (!sim_on_inherit)) {
2838     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
2839         FREE (sim_on_actions[sim_do_depth][i]);
2840         sim_on_actions[sim_do_depth][i] = NULL;
2841         }
2842     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
2843     }
2844 if (flag >= 0)
2845     --sim_do_depth;                                     /* unwind nesting */
2846 sim_brk_clract ();                                      /* defang breakpoint actions */
2847 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
2848     sim_string_to_stat (cptr, &stat);
2849     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
2850     if (sim_switches & SWMASK ('Q'))
2851         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
2852     return stat;                                        /* return with explicit return status */
2853     }
2854 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
2855 }
2856 
2857 /*
2858  * Substitute_args - replace %n tokens in 'instr' with the do command's arguments
2859  *                   and other enviroment variables
2860  *
2861  * Calling sequence
2862  * instr        =       input string
2863  * instr_size   =       sizeof input string buffer
2864  * do_arg[10]   =       arguments
2865  *
2866  * Token "%0" expands to the command file name.
2867  * Token %n (n being a single digit) expands to the n'th argument
2868  * Tonen %* expands to the whole set of arguments (%1 ... %9)
2869  *
2870  * The input sequence "\%" represents a literal "%", and "\\" represents a
2871  * literal "\".  All other character combinations are rendered literally.
2872  *
2873  * Omitted parameters result in null-string substitutions.
2874  *
2875  * A Tokens preceeded and followed by % characters are expanded as environment
2876  * variables, and if one isn't found then can be one of several special
2877  * variables:
2878  *   %DATE%              yyyy-mm-dd
2879  *   %TIME%              hh:mm:ss
2880  *   %STIME%             hh_mm_ss
2881  *   %CTIME%             Www Mmm dd hh:mm:ss yyyy
2882  *   %STATUS%            Status value from the last command executed
2883  *   %TSTATUS%           The text form of the last status value
2884  *   %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
2885  *   %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
2886  *   %SIM_QUIET%         The Quiet mode of the current Do command file
2887  *   %SIM_MESSAGE%       The message display status of the current Do command file
2888  * Environment variable lookups are done first with the precise name between
2889  * the % characters and if that fails, then the name between the % characters
2890  * is upcased and a lookup of that value is attempted.
2891 
2892  * The first Space delimited token on the line is extracted in uppercase and
2893  * then looked up as an environment variable.  If found it the value is
2894  * substituted for the original string before expanding everything else.  If
2895  * it is not found, then the original beginning token on the line is left
2896  * untouched.
2897  */
2898 
2899 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
     /* [previous][next][first][last][top][bottom][index][help] */
2900 {
2901 char gbuf[CBUFSIZE];
2902 char *ip = instr, *op, *oend, *tmpbuf;
2903 const char *ap;
2904 char rbuf[CBUFSIZE];
2905 int i;
2906 time_t now;
2907 struct tm *tmnow;
2908 
2909 time(&now);
2910 tmnow = localtime(&now);
2911 tmpbuf = (char *)malloc(instr_size);
2912 if (!tmpbuf)
2913   {
2914      fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2915              __func__, __FILE__, __LINE__);
2916 #if defined(USE_BACKTRACE)
2917 # ifdef SIGUSR2
2918      (void)raise(SIGUSR2);
2919      /*NOTREACHED*/ /* unreachable */
2920 # endif /* ifdef SIGUSR2 */
2921 #endif /* if defined(USE_BACKTRACE) */
2922      abort();
2923   }
2924 op = tmpbuf;
2925 oend = tmpbuf + instr_size - 2;
2926 while (sim_isspace (*ip))                               /* skip leading spaces */
2927     *op++ = *ip++;
2928 for (; *ip && (op < oend); ) {
2929     if ((ip [0] == '\\') &&                             /* literal escape? */
2930         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
2931         ip++;                                           /* skip '\' */
2932         *op++ = *ip++;                                  /* copy escaped char */
2933         }
2934     else
2935         if ((*ip == '%') &&
2936             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
2937             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
2938                 ap = do_arg[ip[1] - '0'];
2939                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
2940                     if (do_arg[i] == NULL) {
2941                         ap = NULL;
2942                         break;
2943                         }
2944                 ip = ip + 2;
2945                 }
2946             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
2947                 memset (rbuf, '\0', sizeof(rbuf));
2948                 ap = rbuf;
2949                 for (i=1; i<=9; ++i)
2950                     if (do_arg[i] == NULL)
2951                         break;
2952                     else
2953                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
2954                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
2955                                 char quote = '"';
2956                                 if (strchr(do_arg[i], quote))
2957                                     quote = '\'';
2958                                 sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"", (i != 1) ? " " : "", quote, do_arg[i], quote);
2959                                 }
2960                             else
2961                                 sprintf(&rbuf[strlen(rbuf)], "%s%s", (i != 1) ? " " : "", do_arg[i]);
2962                             }
2963                         else
2964                             break;
2965                 ip = ip + 2;
2966                 }
2967             else {                                      /* environment variable */
2968                 ap = NULL;
2969                 (void)get_glyph_nc (ip+1, gbuf, '%');   /* first try using the literal name */
2970                 ap = getenv(gbuf);
2971                 if (!ap) {
2972                     (void)get_glyph (ip+1, gbuf, '%');  /* now try using the upcased name */
2973                     ap = getenv(gbuf);
2974                     }
2975                 ip += 1 + strlen (gbuf);
2976                 if (*ip == '%') ++ip;
2977                 if (!ap) {
2978                     /* ISO 8601 format date/time info */
2979                     if (!strcmp ("DATE", gbuf)) {
2980                         sprintf (rbuf, "%4d-%02d-%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday);
2981                         ap = rbuf;
2982                         }
2983                     else if (!strcmp ("TIME", gbuf)) {
2984                         sprintf (rbuf, "%02d:%02d:%02d", tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2985                         ap = rbuf;
2986                         }
2987                     else if (!strcmp ("DATETIME", gbuf)) {
2988                         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);
2989                         ap = rbuf;
2990                         }
2991                     /* Locale oriented formatted date/time info */
2992                     if (!strcmp ("LDATE", gbuf)) {
2993                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
2994                         ap = rbuf;
2995                         }
2996                     else if (!strcmp ("LTIME", gbuf)) {
2997                         strftime (rbuf, sizeof(rbuf), "%I:%M:%S %p", tmnow);
2998                         ap = rbuf;
2999                         }
3000                     else if (!strcmp ("CTIME", gbuf)) {
3001                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
3002                         ap = rbuf;
3003                         }
3004                     /* Separate Date/Time info */
3005                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
3006                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
3007                         ap = rbuf;
3008                         }
3009                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3010                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
3011                         ap = rbuf;
3012                         }
3013                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
3014                         sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
3015                         ap = rbuf;
3016                         }
3017                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
3018                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
3019                         int year = tmnow->tm_year + 1900;
3020                         int days = year - 2001;
3021                         int leaps = days/4 - days/100 + days/400;
3022                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
3023                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
3024                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
3025                                               96, 80, 92, 76, 88, 72, 84};
3026                         int cal_year = years[selector];
3027 
3028                         if (!strcmp ("DATE_19XX_YY", gbuf))
3029                             sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
3030                         else
3031                             sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
3032                         ap = rbuf;
3033                         }
3034                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
3035                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
3036                         ap = rbuf;
3037                         }
3038                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
3039                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
3040                         ap = rbuf;
3041                         }
3042                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
3043                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
3044                         ap = rbuf;
3045                         }
3046                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
3047                         sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
3048                         ap = rbuf;
3049                         }
3050                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
3051                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
3052                         int iso_yr = tmnow->tm_year + 1900;
3053                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
3054 
3055                         if (iso_wk == 0) {
3056                             iso_yr = iso_yr - 1;
3057                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
3058                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
3059                             }
3060                         else
3061                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
3062                                 ++iso_yr;
3063                                 iso_wk = 1;
3064                                 }
3065                         if (!strcmp ("DATE_WW", gbuf))
3066                             sprintf (rbuf, "%02d", iso_wk);
3067                         else
3068                             sprintf (rbuf, "%04d", iso_yr);
3069                         ap = rbuf;
3070                         }
3071                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
3072                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
3073                         ap = rbuf;
3074                         }
3075                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
3076                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
3077                         ap = rbuf;
3078                         }
3079                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
3080                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
3081                         ap = rbuf;
3082                         }
3083                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
3084                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
3085                         ap = rbuf;
3086                         }
3087                     else if (!strcmp ("STATUS", gbuf)) {
3088                         sprintf (rbuf, "%08X", sim_last_cmd_stat);
3089                         ap = rbuf;
3090                         }
3091                     else if (!strcmp ("TSTATUS", gbuf)) {
3092                         sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
3093                         ap = rbuf;
3094                         }
3095                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
3096                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3097                         ap = rbuf;
3098                         }
3099                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
3100                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3101                         ap = rbuf;
3102                         }
3103                     else if (!strcmp ("SIM_LOCALOPC", gbuf)) {
3104                         sprintf (rbuf, "%s", sim_localopc ? "1" : "");
3105                         ap = rbuf;
3106                         }
3107                     else if (!strcmp ("SIM_QUIET", gbuf)) {
3108                         sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
3109                         ap = rbuf;
3110                         }
3111                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
3112                         sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
3113                         ap = rbuf;
3114                         }
3115                     else if (!strcmp ("HOSTID", gbuf)) {
3116 #if defined( HAVE_UNISTD ) && !defined ( __HAIKU__ ) && !defined ( __ANDROID__ ) && !defined ( __serenity__  )
3117                         sprintf (rbuf, "%ld", (long)gethostid());
3118 #else
3119                         sprintf (rbuf, "00000000");
3120 #endif /* if defined( HAVE_UNISTD ) && !defined ( __HAIKU__ ) && !defined ( __ANDROID__ ) && !defined ( __serenity__ ) */
3121                         ap = rbuf;
3122                         }
3123                     else if (!strcmp ("UID", gbuf)) {
3124 #ifdef HAVE_UNISTD
3125                         sprintf (rbuf, "%ld", (long)getuid());
3126 #else
3127                         sprintf (rbuf, "0");
3128 #endif /* ifdef HAVE_UNISTD */
3129                         ap = rbuf;
3130                         }
3131                     else if (!strcmp ("GID", gbuf)) {
3132 #ifdef HAVE_UNISTD
3133                         sprintf (rbuf, "%ld", (long)getgid());
3134 #else
3135                         sprintf (rbuf, "0");
3136 #endif /* ifdef HAVE_UNISTD */
3137                         ap = rbuf;
3138                         }
3139                     else if (!strcmp ("EUID", gbuf)) {
3140 #ifdef HAVE_UNISTD
3141                         sprintf (rbuf, "%ld", (long)geteuid());
3142 #else
3143                         sprintf (rbuf, "0");
3144 #endif /* ifdef HAVE_UNISTD */
3145                         ap = rbuf;
3146                         }
3147                     else if (!strcmp ("EGID", gbuf)) {
3148 #ifdef HAVE_UNISTD
3149                         sprintf (rbuf, "%ld", (long)getegid());
3150 #else
3151                         sprintf (rbuf, "0");
3152 #endif /* ifdef HAVE_UNISTD */
3153                         ap = rbuf;
3154                         }
3155                     else if (!strcmp ("PID", gbuf)) {
3156 #ifdef HAVE_UNISTD
3157                         sprintf (rbuf, "%ld", (long)getpid());
3158 #else
3159                         sprintf (rbuf, "0");
3160 #endif /* ifdef HAVE_UNISTD */
3161                         ap = rbuf;
3162                         }
3163                     else if (!strcmp ("PPID", gbuf)) {
3164 #ifdef HAVE_UNISTD
3165                         sprintf (rbuf, "%ld", (long)getppid());
3166 #else
3167                         sprintf (rbuf, "0");
3168 #endif /* ifdef HAVE_UNISTD */
3169                         ap = rbuf;
3170                         }
3171                     else if (!strcmp ("PGID", gbuf)) {
3172 #ifdef HAVE_UNISTD
3173                         sprintf (rbuf, "%ld", (long)getpgid(getpid()));
3174 #else
3175                         sprintf (rbuf, "0");
3176 #endif /* ifdef HAVE_UNISTD */
3177                         ap = rbuf;
3178                         }
3179                     else if (!strcmp ("SID", gbuf)) {
3180 #ifdef HAVE_UNISTD
3181                         sprintf (rbuf, "%ld", (long)getsid(getpid()));
3182 #else
3183                         sprintf (rbuf, "0");
3184 #endif /* ifdef HAVE_UNISTD */
3185                         ap = rbuf;
3186                         }
3187                     else if (!strcmp ("ENDIAN", gbuf)) {
3188 #if ( defined(DECLITEND) && DECLITEND == 1 )
3189                         sprintf (rbuf, "LITTLE");
3190 #elif ( defined(DECLITEND) && DECLITEND == 0 )
3191                         sprintf (rbuf, "BIG");
3192 #else
3193                         sprintf (rbuf, "UNKNOWN");
3194 #endif /* if ( defined(DECLITEND) && DECLITEND == 1 ) */
3195                         ap = rbuf;
3196                         }
3197                     else if (!strcmp("SIM_NAME", gbuf)) {
3198                         sprintf (rbuf, "%s", sim_name);
3199                         ap = rbuf;
3200                         }
3201                     else if (!strcmp("SIM_VERSION", gbuf)) {
3202 #if defined(VER_H_GIT_VERSION)
3203                         sprintf (rbuf, "%s", VER_H_GIT_VERSION);
3204 #else
3205                         sprintf (rbuf, "UNKNOWN");
3206 #endif /* if defined(VER_H_GIT_VERSION) */
3207                         ap = rbuf;
3208                         }
3209                     else if (!strcmp("SIM_HASH", gbuf)) {
3210 #if defined(VER_H_GIT_HASH)
3211                         sprintf (rbuf, "%s", VER_H_GIT_HASH);
3212 #else
3213                         sprintf (rbuf, "0000000000000000000000000000000000000000");
3214 #endif /* if defined(VER_H_GIT_HASH) */
3215                         ap = rbuf;
3216                         }
3217                     else if (!strcmp("SIM_RELT", gbuf)) {
3218 #if defined(VER_H_GIT_RELT)
3219                         sprintf (rbuf, "%s", VER_H_GIT_RELT);
3220 #else
3221                         sprintf (rbuf, "X");
3222 #endif /* if defined(VER_H_GIT_RELT) */
3223                         ap = rbuf;
3224                         }
3225                     else if (!strcmp("SIM_DATE", gbuf)) {
3226 #if defined(VER_H_GIT_DATE)
3227                         sprintf (rbuf, "%s", VER_H_GIT_DATE);
3228 #else
3229                         sprintf (rbuf, "UNKNOWN");
3230 #endif /* if defined(VER_H_GIT_DATE) */
3231                         ap = rbuf;
3232                         }
3233                     else if ( (!strcmp("CPUS", gbuf)) \
3234                       || (!strcmp("PROCESSORS", gbuf) ) ) {
3235 #if defined(LINUX_OS) && !defined(__ANDROID__)
3236                         sprintf(rbuf, "%ld", (long)get_nprocs());
3237 #elif defined ( __HAIKU__ )
3238                         system_info hinfo;
3239                         get_system_info(&hinfo);
3240                         sprintf (rbuf, "%llu",
3241                             (long long unsigned int)hinfo.cpu_count);
3242 #else
3243                         sprintf(rbuf, "1");
3244 #endif /* if defined(LINUX_OS) && !defined(__ANDROID__) */
3245                         ap = rbuf;
3246                         }
3247                     }
3248                 }
3249             if (ap) {                                   /* non-null arg? */
3250                 while (*ap && (op < oend))              /* copy the argument */
3251                     *op++ = *ap++;
3252                 }
3253             }
3254         else
3255             *op++ = *ip++;
3256     }
3257 *op = 0;                                                /* term buffer */
3258 strcpy (instr, tmpbuf);
3259 FREE (tmpbuf);
3260 return;
3261 }
3262 
3263 static
3264 int sim_cmp_string (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
3265 {
3266 long int v1, v2;
3267 char *ep1, *ep2;
3268 
3269 v1 = strtol(s1+1, &ep1, 0);
3270 v2 = strtol(s2+1, &ep2, 0);
3271 if ((ep1 != s1 + strlen (s1) - 1) ||
3272     (ep2 != s2 + strlen (s2) - 1))
3273     return strcmp (s1, s2);
3274 if (v1 == v2)
3275     return 0;
3276 if (v1 < v2)
3277     return -1;
3278 return 1;
3279 }
3280 
3281 /* Assert command */
3282 
3283 t_stat assert_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3284 {
3285 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3286 CONST char *tptr, *gptr;
3287 REG *rptr;
3288 uint32 idx = 0;
3289 t_value val;
3290 t_stat r;
3291 t_bool Not = FALSE;
3292 t_bool result;
3293 t_addr addr = 0;
3294 t_stat reason;
3295 
3296 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3297                                                         /* get sw, default */
3298 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3299 if (*cptr == 0)                                         /* must be more */
3300     return SCPE_2FARG;
3301 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3302 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3303     Not = TRUE;                                         /* remember that, and */
3304     cptr = (CONST char *)tptr;
3305     }
3306 if (*cptr == '"') {                                     /* quoted string comparison? */
3307     char op[CBUFSIZE];
3308     static struct {
3309         const char *op;
3310         int aval;
3311         int bval;
3312         t_bool invert;
3313         } *optr, compare_ops[] =
3314         {
3315             { "==",   0,  0, FALSE },
3316             { "EQU",  0,  0, FALSE },
3317             { "!=",   0,  0, TRUE  },
3318             { "NEQ",  0,  0, TRUE  },
3319             { "<",   -1, -1, FALSE },
3320             { "LSS", -1, -1, FALSE },
3321             { "<=",   0, -1, FALSE },
3322             { "LEQ",  0, -1, FALSE },
3323             { ">",    1,  1, FALSE },
3324             { "GTR",  1,  1, FALSE },
3325             { ">=",   0,  1, FALSE },
3326             { "GEQ",  0,  1, FALSE },
3327             { NULL }
3328         };
3329 
3330     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3331                                                     /* get first string */
3332     if (!*tptr)
3333         return SCPE_2FARG;
3334     cptr += strlen (gbuf);
3335     while (sim_isspace (*cptr))                     /* skip spaces */
3336         ++cptr;
3337     (void)get_glyph (cptr, op, '"');
3338     for (optr = compare_ops; optr->op; optr++)
3339         if (0 == strcmp (op, optr->op))
3340             break;
3341     if (!optr->op)
3342         return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3343     cptr += strlen (op);
3344     while (sim_isspace (*cptr))                         /* skip spaces */
3345         ++cptr;
3346     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3347                                                         /* get second string */
3348     if (*cptr) {                                        /* more? */
3349         if (flag)                                       /* ASSERT has no more args */
3350             return SCPE_2MARG;
3351         }
3352     else {
3353         if (!flag)
3354             return SCPE_2FARG;                          /* IF needs actions! */
3355         }
3356     result = sim_cmp_string (gbuf, gbuf2);
3357     result = ((result == optr->aval) || (result == optr->bval));
3358     if (optr->invert)
3359         result = !result;
3360     }
3361 else {
3362     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3363     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3364     if (rptr) {                                         /* got register? */
3365         if (*gptr == '[') {                             /* subscript? */
3366             if (rptr->depth <= 1)                       /* array register? */
3367                 return SCPE_ARG;
3368             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3369             if ((gptr == tptr) || (*tptr++ != ']'))
3370                 return SCPE_ARG;
3371             gptr = tptr;                                /* update */
3372             }
3373         else idx = 0;                                   /* not array */
3374         if (idx >= rptr->depth)                         /* validate subscript */
3375             return SCPE_SUB;
3376         }
3377     else {                                              /* not reg, check for memory */
3378         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3379             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3380         else
3381             addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix);
3382         if (gbuf == gptr)                               /* error? */
3383             return SCPE_NXREG;
3384         }
3385     if (*gptr != 0)                                     /* more? must be search */
3386         (void)get_glyph (gptr, gbuf, 0);
3387     else {
3388         if (*cptr == 0)                                 /* must be more */
3389             return SCPE_2FARG;
3390         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3391         }
3392     if (*cptr) {                                        /* more? */
3393         if (flag)                                       /* ASSERT has no more args */
3394             return SCPE_2MARG;
3395         }
3396     else {
3397         if (!flag)
3398             return SCPE_2FARG;                          /* IF needs actions! */
3399         }
3400     if (rptr) {                                         /* Handle register case */
3401         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3402             (sim_stabr.boolop == -1))                   /* relational op reqd */
3403             return SCPE_MISVAL;
3404         val = get_rval (rptr, idx);                     /* get register value */
3405         result = test_search (&val, &sim_stabr);        /* test condition */
3406         }
3407     else {                                              /* Handle memory case */
3408         if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3409             (sim_staba.boolop == -1))                    /* relational op reqd */
3410             return SCPE_MISVAL;
3411         reason = get_aval (addr, sim_dfdev, sim_dfunit);/* get data */
3412         if (reason != SCPE_OK)                          /* return if error */
3413             return reason;
3414         result = test_search (sim_eval, &sim_staba);    /* test condition */
3415         }
3416     }
3417 if (Not ^ result) {
3418     if (!flag)
3419         sim_brk_setact (cptr);                          /* set up IF actions */
3420     }
3421 else
3422     if (flag)
3423         return SCPE_AFAIL;                              /* return assert status */
3424 return SCPE_OK;
3425 }
3426 
3427 /* Send command */
3428 
3429 t_stat send_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3430 {
3431 char gbuf[CBUFSIZE];
3432 CONST char *tptr;
3433 uint8 dbuf[CBUFSIZE];
3434 uint32 dsize = 0;
3435 uint32 delay = 0;
3436 uint32 after = 0;
3437 t_stat r;
3438 SEND *snd = NULL;
3439 
3440 GET_SWITCHES (cptr);                                    /* get switches */
3441 tptr = get_glyph (cptr, gbuf, ',');
3442 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3443     r = tmxr_locate_line_send (gbuf, &snd);
3444     if (r != SCPE_OK)
3445       return r;
3446     cptr = tptr;
3447     tptr = get_glyph (tptr, gbuf, ',');
3448     }
3449 else
3450     snd = sim_cons_get_send ();
3451 
3452 while (*cptr) {
3453     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3454         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3455         if (r != SCPE_OK)
3456             return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3457         cptr = tptr;
3458         tptr = get_glyph (cptr, gbuf, ',');
3459         continue;
3460         }
3461     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3462         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3463         if (r != SCPE_OK)
3464             return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3465         cptr = tptr;
3466         tptr = get_glyph (cptr, gbuf, ',');
3467         continue;
3468         }
3469     if ((*cptr == '"') || (*cptr == '\''))
3470         break;
3471     return SCPE_ARG;
3472     }
3473 if (*cptr) {
3474     if ((*cptr != '"') && (*cptr != '\'')) //-V560
3475         return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3476     cptr = get_glyph_quoted (cptr, gbuf, 0);
3477     if (*cptr != '\0')
3478         return SCPE_2MARG;                  /* No more arguments */
3479 
3480     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3481         return sim_messagef (SCPE_ARG, "Invalid String\n");
3482     }
3483 if ((dsize == 0) && (delay == 0) && (after == 0))
3484     return SCPE_2FARG;
3485 return sim_send_input (snd, dbuf, dsize, after, delay);
3486 }
3487 
3488 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3489 {
3490 char gbuf[CBUFSIZE];
3491 CONST char *tptr;
3492 t_stat r;
3493 SEND *snd = NULL;
3494 
3495 tptr = get_glyph (cptr, gbuf, ',');
3496 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3497     r = tmxr_locate_line_send (gbuf, &snd);
3498     if (r != SCPE_OK)
3499       return r;
3500     cptr = tptr;
3501     }
3502 else
3503     snd = sim_cons_get_send ();
3504 if (*cptr)
3505     return SCPE_2MARG;
3506 return sim_show_send_input (st, snd);
3507 }
3508 
3509 t_stat expect_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3510 {
3511 char gbuf[CBUFSIZE];
3512 CONST char *tptr;
3513 EXPECT *exp = NULL;
3514 
3515 GET_SWITCHES (cptr);                                    /* get switches */
3516 tptr = get_glyph (cptr, gbuf, ',');
3517 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3518     cptr = tptr;
3519     }
3520 else
3521     exp = sim_cons_get_expect ();
3522 if (flag)
3523     return sim_set_expect (exp, cptr);
3524 else
3525     return sim_set_noexpect (exp, cptr);
3526 }
3527 
3528 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3529 {
3530 char gbuf[CBUFSIZE];
3531 CONST char *tptr;
3532 EXPECT *exp = NULL;
3533 t_stat r;
3534 
3535 tptr = get_glyph (cptr, gbuf, ',');
3536 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3537     r = tmxr_locate_line_expect (gbuf, &exp);
3538     if (r != SCPE_OK)
3539         return r;
3540     cptr = tptr;
3541     }
3542 else
3543     exp = sim_cons_get_expect ();
3544 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3545     return SCPE_ARG;            /* String must be quote delimited */
3546 tptr = get_glyph_quoted (cptr, gbuf, 0);
3547 if (*tptr != '\0')
3548     return SCPE_2MARG;          /* No more arguments */
3549 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3550     return SCPE_ARG;            /* String must be quote delimited */
3551 return sim_exp_show (st, exp, gbuf);
3552 }
3553 
3554 /* Goto command */
3555 
3556 t_stat goto_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3557 {
3558 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3559 const char *cptr;
3560 long fpos;
3561 int32 saved_do_echo = sim_do_echo;
3562 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3563 
3564 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3565 (void)get_glyph (fcptr, gbuf1, 0);
3566 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
3567 fpos = ftell(sim_gotofile);                             /* Save start position */
3568 rewind(sim_gotofile);                                   /* start search for label */
3569 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
3570 sim_do_echo = 0;                                        /* Don't echo while searching for label */
3571 while (1) {
3572     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3573     if (cptr == NULL) break;                            /* exit on eof */
3574     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
3575     if (*cptr == 0) continue;                           /* ignore blank */
3576     if (*cptr != ':') continue;                         /* ignore non-labels */
3577     ++cptr;                                             /* skip : */
3578     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
3579     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
3580     if (0 == strcmp(gbuf, gbuf1)) {
3581         sim_brk_clract ();                              /* goto defangs current actions */
3582         sim_do_echo = saved_do_echo;                    /* restore echo mode */
3583         if (sim_do_echo)                                /* echo if -v */
3584             sim_printf("%s> %s\n", do_position(), cbuf);
3585         return SCPE_OK;
3586         }
3587     }
3588 sim_do_echo = saved_do_echo;                       /* restore echo mode         */
3589 fseek(sim_gotofile, fpos, SEEK_SET);               /* restore start position    */
3590 sim_goto_line[sim_do_depth] = saved_goto_line;     /* restore start line number */
3591 return SCPE_ARG;
3592 }
3593 
3594 /* Return command */
3595 
3596 /* The return command is invalid unless encountered in a do_cmd context,    */
3597 /* and in that context, it is handled as a special case inside of do_cmd()  */
3598 /* and not dispatched here, so if we get here a return has been issued from */
3599 /* interactive input */
3600 
3601 t_stat return_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3602 {
3603 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3604 }
3605 
3606 /* Shift command */
3607 
3608 /* The shift command is invalid unless encountered in a do_cmd context,    */
3609 /* and in that context, it is handled as a special case inside of do_cmd() */
3610 /* and not dispatched here, so if we get here a shift has been issued from */
3611 /* interactive input (it is not valid interactively since it would have to */
3612 /* mess with the program's argv which is owned by the C runtime library    */
3613 
3614 t_stat shift_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3615 {
3616 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3617 }
3618 
3619 /* Call command */
3620 
3621 /* The call command is invalid unless encountered in a do_cmd context,     */
3622 /* and in that context, it is handled as a special case inside of do_cmd() */
3623 /* and not dispatched here, so if we get here a call has been issued from  */
3624 /* interactive input                                                       */
3625 
3626 t_stat call_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3627 {
3628 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3629 const char *cptr;
3630 
3631 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3632 cptr = get_glyph (fcptr, gbuf, 0);
3633 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
3634 snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3635 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
3636 return do_cmd_label (flag, cbuf, gbuf);
3637 }
3638 
3639 /* On command */
3640 
3641 t_stat on_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3642 {
3643 char gbuf[CBUFSIZE];
3644 t_stat cond;
3645 
3646 cptr = get_glyph (cptr, gbuf, 0);
3647 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
3648 if (0 == strcmp("ERROR", gbuf))
3649     cond = 0;
3650 else
3651     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3652         return SCPE_ARG;
3653 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
3654     FREE(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
3655     sim_on_actions[sim_do_depth][cond] = NULL; }
3656 else {
3657     sim_on_actions[sim_do_depth][cond] =
3658         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3659     if (!sim_on_actions[sim_do_depth][cond])
3660       {
3661         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3662                  __func__, __FILE__, __LINE__);
3663 #if defined(USE_BACKTRACE)
3664 # ifdef SIGUSR2
3665         (void)raise(SIGUSR2);
3666         /*NOTREACHED*/ /* unreachable */
3667 # endif /* ifdef SIGUSR2 */
3668 #endif /* if defined(USE_BACKTRACE) */
3669         abort();
3670       }
3671     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3672     }
3673 return SCPE_OK;
3674 }
3675 
3676 /* noop command */
3677 
3678 t_stat noop_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3679 {
3680 if (cptr && (*cptr != 0))                               /* now eol? */
3681     return SCPE_2MARG;
3682 return SCPE_OK;                                         /* we're happy doing nothing */
3683 }
3684 
3685 /* Set on/noon routine */
3686 
3687 t_stat set_on (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3688 {
3689 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
3690     char gbuf[CBUFSIZE];
3691 
3692     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
3693     if (((MATCH_CMD(gbuf,"INHERIT")) &&
3694          (MATCH_CMD(gbuf,"NOINHERIT"))) || //-V600
3695         (*cptr))
3696         return SCPE_2MARG;
3697     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT"))) //-V560
3698         sim_on_inherit = 1;
3699     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT"))) //-V560
3700         sim_on_inherit = 0;
3701     return SCPE_OK;
3702     }
3703 if (cptr && (*cptr != 0))                               /* now eol? */
3704     return SCPE_2MARG;
3705 sim_on_check[sim_do_depth] = flag;
3706 if ((sim_do_depth != 0) &&
3707     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
3708     sim_on_actions[sim_do_depth][0] =                   /* No, so make "RETURN" */
3709         (char *)malloc(1+strlen("RETURN"));             /* be the default action */
3710     strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
3711     }
3712 if ((sim_do_depth != 0) &&
3713     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
3714     sim_on_actions[sim_do_depth][SCPE_AFAIL] =          /* No, so make "RETURN" */
3715         (char *)malloc(1+strlen("RETURN"));             /* be the action */
3716     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
3717     }
3718 return SCPE_OK;
3719 }
3720 
3721 /* Set verify/noverify routine */
3722 
3723 t_stat set_verify (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3724 {
3725 if (cptr && (*cptr != 0))                               /* now eol? */
3726     return SCPE_2MARG;
3727 if (flag == sim_do_echo)                                /* already set correctly? */
3728     return SCPE_OK;
3729 sim_do_echo = flag;
3730 return SCPE_OK;
3731 }
3732 
3733 /* Set message/nomessage routine */
3734 
3735 t_stat set_message (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3736 {
3737 if (cptr && (*cptr != 0))                               /* now eol? */
3738     return SCPE_2MARG;
3739 if (flag == sim_show_message)                           /* already set correctly? */
3740     return SCPE_OK;
3741 sim_show_message = flag;
3742 return SCPE_OK;
3743 }
3744 
3745 /* Set localopc/nolocalopc routine */
3746 
3747 t_stat set_localopc (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3748 {
3749 if (cptr && (*cptr != 0))                               /* now eol? */
3750     return SCPE_2MARG;
3751 if (flag == sim_localopc)                               /* already set correctly? */
3752     return SCPE_OK;
3753 sim_localopc = flag;
3754 return SCPE_OK;
3755 }
3756 /* Set quiet/noquiet routine */
3757 
3758 t_stat set_quiet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3759 {
3760 if (cptr && (*cptr != 0))                               /* now eol? */
3761     return SCPE_2MARG;
3762 if (flag == sim_quiet)                                  /* already set correctly? */
3763     return SCPE_OK;
3764 sim_quiet = flag;
3765 return SCPE_OK;
3766 }
3767 
3768 /* Set environment routine */
3769 
3770 t_stat sim_set_environment (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3771 {
3772 char varname[CBUFSIZE];
3773 
3774 if ((!cptr) || (*cptr == 0))                            /* now eol? */
3775     return SCPE_2FARG;
3776 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
3777 setenv(varname, cptr, 1);
3778 return SCPE_OK;
3779 }
3780 
3781 /* Set command */
3782 
3783 t_stat set_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3784 {
3785 uint32 lvl = 0;
3786 t_stat r;
3787 char gbuf[CBUFSIZE], *cvptr;
3788 CONST char *svptr;
3789 DEVICE *dptr;
3790 UNIT *uptr;
3791 MTAB *mptr;
3792 CTAB *gcmdp;
3793 C1TAB *ctbr = NULL, *glbr;
3794 
3795 GET_SWITCHES (cptr);                                    /* get switches */
3796 if (*cptr == 0)                                         /* must be more */
3797     return SCPE_2FARG;
3798 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
3799 
3800 if ((dptr = find_dev (gbuf))) {                         /* device match? */
3801     uptr = dptr->units;                                 /* first unit */
3802     ctbr = set_dev_tab;                                 /* global table */
3803     lvl = MTAB_VDV;                                     /* device match */
3804     GET_SWITCHES (cptr);                                /* get more switches */
3805     }
3806 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
3807     if (uptr == NULL)                                   /* invalid unit */
3808         return SCPE_NXUN;
3809     ctbr = set_unit_tab;                                /* global table */
3810     lvl = MTAB_VUN;                                     /* unit match */
3811     GET_SWITCHES (cptr);                                /* get more switches */
3812     }
3813 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
3814     GET_SWITCHES (cptr);                                /* get more switches */
3815     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
3816     }
3817 else {
3818     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3819         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
3820             *cvptr++ = 0;
3821         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
3822             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
3823                 dptr = sim_dflt_dev;
3824                 cptr = svptr;
3825                 while (sim_isspace(*cptr))
3826                     ++cptr;
3827                 break;
3828                 }
3829             }
3830         }
3831     if (!dptr)
3832         return SCPE_NXDEV;                              /* no match */
3833     lvl = MTAB_VDV;                                     /* device match */
3834     uptr = dptr->units;                                 /* first unit */
3835     }
3836 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
3837     return SCPE_2FARG;
3838 GET_SWITCHES (cptr);                                    /* get more switches */
3839 
3840 while (*cptr != 0) {                                    /* do all mods */
3841     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
3842     if (0 == strcmp (gbuf, ";"))
3843         break;
3844     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
3845         *cvptr++ = 0;
3846     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3847         if ((mptr->mstring) &&                          /* match string */
3848             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
3849             if (mptr->mask & MTAB_XTD) {                /* extended? */
3850                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
3851                     return SCPE_ARG;
3852                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
3853                     return SCPE_UDIS;                   /* unit disabled? */
3854                 if (mptr->valid) {                      /* validation rtn? */
3855                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
3856                         svptr = get_glyph_quoted (svptr, gbuf, ',');
3857                         if ((cvptr = strchr (gbuf, '='))) {
3858                             *cvptr++ = 0;
3859                             cptr = svptr;
3860                             }
3861                         }
3862                     else {
3863                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
3864                             (void)get_glyph_nc (svptr, gbuf, ',');
3865                             if ((cvptr = strchr (gbuf, '=')))
3866                                 *cvptr++ = 0;
3867                             }
3868                         }
3869                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
3870                     if (r != SCPE_OK)
3871                         return r;
3872                     }
3873                 else if (!mptr->desc)                   /* value desc? */
3874                     break;
3875                 else if (cvptr)                         /* = value? */
3876                     return SCPE_ARG;
3877                 else *((int32 *) mptr->desc) = mptr->match;
3878                 }                                       /* end if xtd */
3879             else {                                      /* old style */
3880                 if (cvptr)                              /* = value? */
3881                     return SCPE_ARG;
3882                 if (uptr->flags & UNIT_DIS)             /* disabled? */
3883                      return SCPE_UDIS;
3884                 if ((mptr->valid) &&                    /* invalid? */
3885                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
3886                     return r;
3887                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
3888                     (mptr->match & mptr->mask);         /* set new value */
3889                 }                                       /* end else xtd */
3890             break;                                      /* terminate for */
3891             }                                           /* end if match */
3892         }                                               /* end for */
3893     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
3894         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
3895             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
3896             if (r != SCPE_OK)
3897                 return r;
3898             }
3899         else if (!dptr->modifiers)                      /* no modifiers? */
3900             return SCPE_NOPARAM;
3901         else return SCPE_NXPAR;
3902         }                                               /* end if no mat */
3903     }                                                   /* end while */
3904 return SCPE_OK;                                         /* done all */
3905 }
3906 
3907 /* Match CTAB/CTAB1 name */
3908 
3909 CTAB *find_ctab (CTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
3910 {
3911 if (!tab)
3912     return NULL;
3913 for (; tab->name != NULL; tab++) {
3914     if (MATCH_CMD (gbuf, tab->name) == 0)
3915         return tab;
3916     }
3917 return NULL;
3918 }
3919 
3920 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
3921 {
3922 if (!tab)
3923     return NULL;
3924 for (; tab->name != NULL; tab++) {
3925     if (MATCH_CMD (gbuf, tab->name) == 0)
3926         return tab;
3927     }
3928 return NULL;
3929 }
3930 
3931 /* Set device data radix routine */
3932 
3933 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3934 {
3935 if (cptr)
3936     return SCPE_ARG;
3937 dptr->dradix = flag & 037;
3938 return SCPE_OK;
3939 }
3940 
3941 /* Set device enabled/disabled routine */
3942 
3943 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3944 {
3945 UNIT *up;
3946 uint32 i;
3947 
3948 if (cptr)
3949     return SCPE_ARG;
3950 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
3951     return SCPE_NOFNC;
3952 if (flag) {                                             /* enable? */
3953     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
3954         return SCPE_OK;
3955     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
3956     }
3957 else {
3958     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
3959         return SCPE_OK;
3960     for (i = 0; i < dptr->numunits; i++) {              /* check units */
3961         up = (dptr->units) + i;                         /* att or active? */
3962         if ((up->flags & UNIT_ATT) || sim_is_active (up))
3963             return SCPE_NOFNC;                          /* can't do it */
3964         }
3965     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
3966     }
3967 if (dptr->reset)                                        /* reset device */
3968     return dptr->reset (dptr);
3969 else return SCPE_OK;
3970 }
3971 
3972 /* Set unit enabled/disabled routine */
3973 
3974 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3975 {
3976 if (cptr)
3977     return SCPE_ARG;
3978 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
3979     return SCPE_NOFNC;
3980 if (flag)                                               /* enb? enable */
3981     uptr->flags = uptr->flags & ~UNIT_DIS;
3982 else {
3983     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
3984         sim_is_active (uptr))                           /* more tests */
3985         return SCPE_NOFNC;
3986     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
3987     }
3988 return SCPE_OK;
3989 }
3990 
3991 /* Set device debug enabled/disabled routine */
3992 
3993 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3994 {
3995 char gbuf[CBUFSIZE];
3996 DEBTAB *dep;
3997 
3998 if ((dptr->flags & DEV_DEBUG) == 0)
3999     return SCPE_NOFNC;
4000 if (cptr == NULL) {                                     /* no arguments? */
4001     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
4002     if (flag && dptr->debflags) {                       /* enable with table? */
4003         for (dep = dptr->debflags; dep->name != NULL; dep++)
4004             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
4005         }
4006     return SCPE_OK;
4007     }
4008 if (dptr->debflags == NULL)                             /* must have table */
4009     return SCPE_ARG;
4010 while (*cptr) {
4011     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
4012     for (dep = dptr->debflags; dep->name != NULL; dep++) {
4013         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
4014             if (flag)
4015                 dptr->dctrl = dptr->dctrl | dep->mask;
4016             else dptr->dctrl = dptr->dctrl & ~dep->mask;
4017             break;
4018             }
4019         }                                               /* end for */
4020     if (dep->mask == 0)                                 /* no match? */
4021         return SCPE_ARG;
4022     }                                                   /* end while */
4023 return SCPE_OK;
4024 }
4025 
4026 /* Show command */
4027 
4028 t_stat show_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4029 {
4030 t_stat r = SCPE_IERR;
4031 
4032 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
4033                                                         /* get sw, ofile */
4034 if (!cptr)                                              /* error? */
4035     return r;
4036 if (sim_ofile) {                                        /* output file? */
4037     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
4038     fclose (sim_ofile);
4039     }
4040 else {
4041     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
4042     if (sim_log && (sim_log != stdout))
4043         show_cmd_fi (sim_log, flag, cptr);
4044     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
4045         show_cmd_fi (sim_deb, flag, cptr);
4046     }
4047 return r;
4048 }
4049 
4050 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4051 {
4052 uint32 lvl = 0xFFFFFFFF;
4053 char gbuf[CBUFSIZE], *cvptr;
4054 CONST char *svptr;
4055 DEVICE *dptr;
4056 UNIT *uptr;
4057 MTAB *mptr;
4058 SHTAB *shtb = NULL, *shptr;
4059 
4060 GET_SWITCHES (cptr);                                    /* get switches */
4061 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4062     return SCPE_2FARG;
4063 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
4064 
4065 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4066     uptr = dptr->units;                                 /* first unit */
4067     shtb = show_dev_tab;                                /* global table */
4068     lvl = MTAB_VDV;                                     /* device match */
4069     GET_SWITCHES (cptr);                                /* get more switches */
4070     }
4071 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4072     if (uptr == NULL)                                   /* invalid unit */
4073         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
4074     if (uptr->flags & UNIT_DIS)                         /* disabled? */
4075         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
4076     shtb = show_unit_tab;                               /* global table */
4077     lvl = MTAB_VUN;                                     /* unit match */
4078     GET_SWITCHES (cptr);                                /* get more switches */
4079     }
4080 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
4081     GET_SWITCHES (cptr);                                /* get more switches */
4082     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
4083     }
4084 else {
4085     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4086         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4087             *cvptr++ = 0;
4088         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
4089             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
4090                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) || //-V600
4091                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
4092                 dptr = sim_dflt_dev;
4093                 lvl = MTAB_VDV;                         /* device match */
4094                 cptr = svptr;
4095                 while (sim_isspace(*cptr))
4096                     ++cptr;
4097                 break;
4098                 }
4099             }
4100         }
4101     if (!dptr) {
4102         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
4103             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
4104         else
4105             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
4106         }
4107     }
4108 
4109 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
4110     return (lvl == MTAB_VDV)?
4111         show_device (ofile, dptr, 0):
4112         show_unit (ofile, dptr, uptr, -1);
4113     }
4114 GET_SWITCHES (cptr);                                    /* get more switches */
4115 
4116 while (*cptr != 0) {                                    /* do all mods */
4117     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
4118     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4119         *cvptr++ = 0;
4120     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4121         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
4122             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
4123             ((mptr->disp && mptr->pstring &&            /* named disp? */
4124             (MATCH_CMD (gbuf, mptr->pstring) == 0))
4125             )) {
4126             if (cvptr && !MODMASK(mptr,MTAB_SHP))
4127                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
4128             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
4129             break;
4130             }                                           /* end if */
4131         }                                               /* end for */
4132     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4133         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
4134             t_stat r;
4135 
4136             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
4137             if (r != SCPE_OK)
4138                 return r;
4139             }
4140         else {
4141             if (!dptr->modifiers)                       /* no modifiers? */
4142                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
4143             else
4144                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
4145             }
4146         }                                               /* end if */
4147     }                                                   /* end while */
4148 return SCPE_OK;
4149 }
4150 
4151 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4152 {
4153 if (!tab)
4154     return NULL;
4155 for (; tab->name != NULL; tab++) {
4156     if (MATCH_CMD (gbuf, tab->name) == 0)
4157         return tab;
4158     }
4159 return NULL;
4160 }
4161 
4162 /* Show device and unit */
4163 
4164 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4165 {
4166 uint32 j, udbl, ucnt;
4167 UNIT *uptr;
4168 int32 toks = 0;
4169 
4170 if (strcmp(sim_dname (dptr),"SYS") != 0) {
4171 fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
4172 if ((flag == 2) && dptr->description) {
4173     fprintf (st, "\t%s\n", dptr->description(dptr));
4174     }
4175 else {
4176     if ((sim_switches & SWMASK ('D')) && dptr->description)
4177         fprintf (st, "\t%s\n", dptr->description(dptr));
4178     }
4179 if (qdisable (dptr)) {                                  /* disabled? */
4180     fprintf (st, "\tdisabled\n");
4181     return SCPE_OK;
4182     }
4183 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
4184     uptr = dptr->units + j;
4185     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
4186         ucnt++;
4187     else if (uptr->flags & UNIT_DISABLE)
4188         udbl++;                                         /* count user-disabled */
4189     }
4190 //show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
4191 if (dptr->numunits == 0) {
4192     if (toks) //-V547
4193         fprintf (st, "\n");
4194     }
4195 else {
4196     if (ucnt == 0) {
4197         fprint_sep (st, &toks);
4198         fprintf (st, "all units disabled\n");
4199         }
4200     else if ((ucnt + udbl) == 1) {
4201         fprint_sep (st, &toks);
4202         fprintf (st, " 1 unit\n");
4203         }
4204     else if ((ucnt > 1) || (udbl > 0)) {
4205         fprint_sep (st, &toks);
4206         fprintf (st, "%2.d units\n", ucnt + udbl);
4207         }
4208     else
4209         if ((flag != 2) || !dptr->description || toks)
4210             fprintf (st, "\n");
4211     toks = 0;
4212     }
4213 if (flag)                                               /* dev only? */
4214     return SCPE_OK;
4215 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
4216     uptr = dptr->units + j;
4217     if ((uptr->flags & UNIT_DIS) == 0)
4218         show_unit (st, dptr, uptr, ucnt + udbl);
4219     }
4220 }
4221 return SCPE_OK;
4222 }
4223 
4224 void fprint_sep (FILE *st, int32 *tokens)
     /* [previous][next][first][last][top][bottom][index][help] */
4225 {
4226 fprintf (st, "%s", (*tokens > 0) ? "" : "\t");
4227 *tokens += 1;
4228 }
4229 
4230 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4231 {
4232 int32 u = (int32)(uptr - dptr->units);
4233 int32 toks = 0;
4234 
4235 if (flag > 1)
4236     fprintf (st, "   %s%d \n", sim_dname (dptr), u);
4237 else if (flag < 0)
4238     fprintf (st, " %s%d ", sim_dname (dptr), u);
4239 if (uptr->flags & UNIT_ATT) {
4240     fprint_sep (st, &toks);
4241     fprintf (st, "status   : attached to %s", uptr->filename);
4242     if (uptr->flags & UNIT_RO)
4243         fprintf (st, ", read only");
4244     }
4245 else {
4246     if (uptr->flags & UNIT_ATTABLE) {
4247         fprint_sep (st, &toks);
4248         fprintf (st, "status   : not attached");
4249         }
4250     }
4251 if ((uptr->capac > 0) && (uptr->flags & UNIT_FIX)) {
4252     fprint_sep (st, &toks);
4253     fprint_capac (st, dptr, uptr);
4254     }
4255 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
4256 if (toks || (flag < 0) || (flag > 1))
4257     fprintf (st, "\n");
4258 return SCPE_OK;
4259 }
4260 
4261 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4262 {
4263 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4264 t_offset kval = (t_offset)((uptr->flags & UNIT_BINK) ? 1024: 1000);
4265 t_offset mval;
4266 t_offset psize = (t_offset)uptr->capac;
4267 char *scale, *width;
4268 
4269 if (sim_switches & SWMASK ('B'))
4270     kval = 1024;
4271 mval = kval * kval;
4272 if (dptr->flags & DEV_SECTORS) {
4273     kval = kval / 512;
4274     mval = mval / 512;
4275     }
4276 if ((dptr->dwidth / dptr->aincr) > 8)
4277     width = "W";
4278 else
4279     width = "B";
4280 if ((psize < (kval * 10)) &&
4281     (0 != (psize % kval))) {
4282     scale = "";
4283     }
4284 else if ((psize < (mval * 10)) &&
4285          (0 != (psize % mval))){
4286     scale = "K";
4287     psize = psize / kval;
4288     }
4289 else {
4290     scale = "M";
4291     psize = psize / mval;
4292     }
4293 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4294 sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4295 return capac_buf;
4296 }
4297 
4298 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4299 {
4300 fprintf (st, " %s", sprint_capac (dptr, uptr));
4301 }
4302 
4303 /* Show <global name> processors  */
4304 
4305 extern void print_default_base_system_script (void);
4306 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4307 {
4308 #ifndef PERF_STRIP
4309   print_default_base_system_script();
4310 #endif /* ifndef PERF_STRIP */
4311   return 0;
4312 }
4313 
4314 static void printp (unsigned char * PROM, char * label, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4315   sim_printf ("  %s '", label);
4316   for (int l = 0; l < length; l ++) {
4317     unsigned int byte = PROM[offset + l];
4318     sim_printf (isprint (byte) ? "%c" : "\\%03o", byte);
4319   }
4320   sim_printf ("'\r\n");
4321 }
4322 
4323 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4324 {
4325   unsigned char PROM[1024];
4326   setupPROM (0, PROM);
4327   /* int n = 6; */
4328   /* int l = 174; /1* end of populated area *1/ */
4329   /* sim_printf(" PROM initialization data: \r\n"); */
4330   /* for (int prombyte = 0; prombyte < l; ++prombyte) { */
4331   /*   if (prombyte % n == 0 && prombyte != 0) { */
4332   /*     sim_printf("\r\n"); */
4333   /*   } */
4334   /*   sim_printf("%03d: %02x [", prombyte, PROM[prombyte]); */
4335   /*   if (PROM[prombyte] > 31 && PROM[prombyte] < 128) { */
4336   /*     sim_printf("%c]  ", (unsigned char)PROM[prombyte]); */
4337   /*   } else { */
4338   /*     sim_printf(".]  "); */
4339   /*   } */
4340   /* } */
4341   /* sim_printf("\r\n"); */
4342   printp (PROM, "CPU Model:           ",   0, 11);
4343   printp (PROM, "CPU Serial:          ",  11, 11);
4344   printp (PROM, "Ship Date:           ",  22,  6);
4345   return 0;
4346 }
4347 
4348 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4349 {
4350     fprintf (st, "\r\n\r Build Information:\n");
4351 #if defined(BUILDINFO_scp) && defined(SYSDEFS_USED)
4352     fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4353 # ifndef __OPEN64__
4354     fprintf (st, "\r\n  Relevant definitions: %s\n", SYSDEFS_USED );
4355 # endif
4356 #elif defined(BUILDINFO_scp)
4357     fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4358 #else
4359     fprintf (st, "\r\n      Compilation info: Not available\n" );
4360 #endif
4361 #if defined (UV_VERSION_MAJOR) && \
4362     defined (UV_VERSION_MINOR) && \
4363     defined (UV_VERSION_PATCH)
4364 # ifdef UV_VERSION_MAJOR
4365 #  ifndef UV_VERSION_MINOR
4366 #   ifndef UV_VERSION_PATCH
4367 #    ifndef UV_VERSION_SUFFIX
4368     fprintf (st, "\r\n    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4369 #    endif /* ifndef UV_VERSION_SUFFIX */
4370 #   endif /* ifndef UV_VERSION_PATCH */
4371 #  endif /* ifndef UV_VERSION_MINOR */
4372 #  ifdef UV_VERSION_MINOR
4373 #   ifndef UV_VERSION_PATCH
4374 #    ifndef UV_VERSION_SUFFIX
4375     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4376             UV_VERSION_MINOR);
4377 #    endif /* ifndef UV_VERSION_SUFFIX */
4378 #   endif /* ifndef UV_VERSION_PATCH */
4379 #   ifdef UV_VERSION_PATCH
4380 #    ifndef UV_VERSION_SUFFIX
4381     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4382             UV_VERSION_MINOR, UV_VERSION_PATCH);
4383 #    endif /* ifndef UV_VERSION_SUFFIX */
4384 #    ifdef UV_VERSION_SUFFIX
4385     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4386             UV_VERSION_MINOR, UV_VERSION_PATCH);
4387 #     ifdef UV_VERSION_IS_RELEASE
4388 #      if UV_VERSION_IS_RELEASE == 1
4389 #       define UV_RELEASE_TYPE " (release)"
4390 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4391 #      if UV_VERSION_IS_RELEASE == 0
4392 #       define UV_RELEASE_TYPE "-dev"
4393 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4394 #      ifndef UV_RELEASE_TYPE
4395 #       define UV_RELEASE_TYPE ""
4396 #      endif /* ifndef UV_RELEASE_TYPE */
4397 #      ifdef UV_RELEASE_TYPE
4398     fprintf (st, "%s", UV_RELEASE_TYPE);
4399 #      endif /* ifdef UV_RELEASE_TYPE */
4400 #     endif /* ifdef UV_VERSION_IS_RELEASE */
4401 #    endif /* ifdef UV_VERSION_SUFFIX */
4402 #   endif /* ifdef UV_VERSION_PATCH */
4403 #  endif /* ifdef UV_VERSION_MINOR */
4404     unsigned int CurrentUvVersion = uv_version();
4405     if (CurrentUvVersion > 0)
4406         if (uv_version_string() != NULL)
4407             fprintf (st, "; %s in use", uv_version_string());
4408 # endif /* ifdef UV_VERSION_MAJOR */
4409 #else
4410     fprintf (st, "\r\n    Event loop library: Using libuv (or compatible) library, unknown version");
4411 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4412         *    defined(UV_VERSION_MINOR) &&  \
4413         *    defined(UV_VERSION_PATCH)     \
4414         */
4415 #ifdef DECNUMBERLOC
4416 # ifdef DECVERSION
4417 #  ifdef DECVERSEXT
4418     fprintf (st, "\r\n          Math library: %s-%s", DECVERSION, DECVERSEXT);
4419 #  else
4420 #   ifdef DECNLAUTHOR
4421     fprintf (st, "\r\n          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4422 #   else
4423     fprintf (st, "\r\n          Math library: %s", DECVERSION);
4424 #   endif /* ifdef DECNLAUTHOR */
4425 #  endif /* ifdef DECVERSEXT */
4426 # else
4427     fprintf (st, "\r\n          Math library: decNumber, unknown version");
4428 # endif /* ifdef DECVERSION */
4429 #endif /* ifdef DECNUMBERLOC */
4430 #ifdef LOCKLESS
4431     fprintf (st, "\r\n     Atomic operations: ");
4432 # if defined(AIX_ATOMICS)
4433     fprintf (st, "IBM AIX-style");
4434 # elif defined(BSD_ATOMICS)
4435     fprintf (st, "FreeBSD-style");
4436 # elif defined(GNU_ATOMICS)
4437     fprintf (st, "GNU-style");
4438 # elif defined(SYNC_ATOMICS)
4439     fprintf (st, "GNU sync-style");
4440 # elif defined(ISO_ATOMICS)
4441     fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4442 # elif defined(NT_ATOMICS)
4443     fprintf (st, "Windows NT interlocked operations");
4444 # endif
4445 #endif /* ifdef LOCKLESS */
4446     fprintf (st, "\r\n          File locking: ");
4447 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4448     fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4449 #endif
4450 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4451     fprintf (st, "POSIX-style fcntl() locking");
4452 #endif
4453 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4454     fprintf (st, "BSD-style flock() locking");
4455 #endif
4456 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4457     fprintf (st, "No file locking available");
4458 #endif
4459 #if defined(USE_BACKTRACE)
4460     fprintf (st, "\r\n     Backtrace support: ");
4461     fprintf (st, "libbacktrace");
4462 #endif /* if defined(USE_BACKTRACE) */
4463     fprintf (st, "\r\n");
4464     return 0;
4465 }
4466 
4467 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4468 {
4469 const char *arch = "";
4470 int dirty = 0;
4471 
4472 if (cptr && (*cptr != 0))
4473     return SCPE_2MARG;
4474 if (flag) {
4475         fprintf (st, " %s Simulator:", sim_name);
4476 #if defined(USE_DUMA)
4477 # undef NO_SUPPORT_VERSION
4478 # define NO_SUPPORT_VERSION 1
4479         nodist++;
4480 #endif /* if defined(USE_DUMA) */
4481 #if defined(NO_SUPPORT_VERSION) ||  \
4482     defined(TESTING)            ||  \
4483     defined(ISOLTS)             ||  \
4484     defined(USE_DUMA)
4485 # ifndef NO_SUPPORT_VERSION
4486 #  define NO_SUPPORT_VERSION 1
4487 # endif
4488 #endif
4489 #if defined(NO_SUPPORT_VERSION)
4490         dirty++;
4491 #endif
4492 #if defined(GENERATED_MAKE_VER_H)
4493 # if defined(VER_H_GIT_VERSION)
4494 
4495         /* Dirty if git source is dirty */
4496         if (strstr(VER_H_GIT_VERSION, "*"))
4497           {
4498                 dirty++;
4499           }
4500 
4501         /* Dirty if post-tag patches detected */
4502         if (strstr(VER_H_GIT_VERSION, "+"))
4503           {
4504                 dirty++;
4505           }
4506 
4507         /* Dirty if version contains "Z", "D", "A", or "B" */
4508         if ((strstr(VER_H_GIT_VERSION, "Z")) ||  \
4509             (strstr(VER_H_GIT_VERSION, "D")) ||  \
4510             (strstr(VER_H_GIT_VERSION, "A")) ||  \
4511             (strstr(VER_H_GIT_VERSION, "B"))) {
4512                 dirty++;
4513         }
4514 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4515 #   if defined(VER_H_GIT_HASH)
4516 #    if VER_H_GIT_PATCH_INT < 1
4517     fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4518              VER_H_GIT_VERSION,
4519              (long)(CHAR_BIT*sizeof(void *)),
4520              VER_H_GIT_HASH);
4521 #    else
4522 #     define NO_SUPPORT_VERSION 1
4523     fprintf (st, "\n   Version: %s+%s (%ld-bit)\n    Commit: %s",
4524              VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4525              (long)(CHAR_BIT*sizeof(void *)),
4526              VER_H_GIT_HASH);
4527 #    endif
4528 #   else
4529 #    if VER_H_GIT_PATCH_INT < 1
4530         fprintf (st, "\n   Version: %s (%ld-bit)",
4531                  VER_H_GIT_VERSION,
4532                  (long)(CHAR_BIT*sizeof(void *)));
4533 #    else
4534 #     define NO_SUPPORT_VERSION 1
4535         fprintf (st, "\n   Version: %s+%s (%ld-bit)",
4536                  VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4537                  (long)(CHAR_BIT*sizeof(void *)));
4538 #    endif
4539 #   endif
4540 #  else
4541 #   if defined(VER_H_GIT_HASH)
4542         fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4543                  VER_H_GIT_VERSION,
4544                  (long)(CHAR_BIT*sizeof(void *)),
4545                  VER_H_GIT_HASH);
4546 #   else
4547         fprintf (st, "\n   Version: %s (%ld-bit)",
4548                  VER_H_GIT_VERSION,
4549                  (long)(CHAR_BIT*sizeof(void *)));
4550 #   endif
4551 #  endif
4552 # endif
4553 #endif
4554 #ifdef TESTING
4555     fprintf (st, "\n   Options: ");
4556 # ifndef HAVE_DPSOPT
4557 #  define HAVE_DPSOPT 1
4558 # endif
4559     fprintf (st, "TESTING");
4560 #endif
4561 #ifdef ISOLTS
4562 # ifdef HAVE_DPSOPT
4563     fprintf (st, ", ");
4564 # else
4565     fprintf (st, "\n   Options: ");
4566 # endif
4567 # ifndef HAVE_DPSOPT
4568 #  define HAVE_DPSOPT 1
4569 # endif
4570     fprintf (st, "ISOLTS");
4571 #endif
4572 #ifdef NEED_128
4573 # ifdef HAVE_DPSOPT
4574     fprintf (st, ", ");
4575 # else
4576     fprintf (st, "\n   Options: ");
4577 # endif
4578 # ifndef HAVE_DPSOPT
4579 #  define HAVE_DPSOPT 1
4580 # endif
4581     fprintf (st, "NEED_128");
4582 #endif
4583 #ifdef WAM
4584 # ifdef HAVE_DPSOPT
4585     fprintf (st, ", ");
4586 # else
4587     fprintf (st, "\n   Options: ");
4588 # endif
4589 # ifndef HAVE_DPSOPT
4590 #  define HAVE_DPSOPT 1
4591 # endif
4592     fprintf (st, "WAM");
4593 #endif
4594 #ifdef ROUND_ROBIN
4595 # ifdef HAVE_DPSOPT
4596     fprintf (st, ", ");
4597 # else
4598     fprintf (st, "\n   Options: ");
4599 # endif
4600 # ifndef HAVE_DPSOPT
4601 #  define HAVE_DPSOPT 1
4602 # endif
4603     fprintf (st, "ROUND_ROBIN");
4604 #endif
4605 #ifndef LOCKLESS
4606 # ifdef HAVE_DPSOPT
4607     fprintf (st, ", ");
4608 # else
4609     fprintf (st, "\n   Options: ");
4610 # endif
4611 # ifndef HAVE_DPSOPT
4612 #  define HAVE_DPSOPT 1
4613 # endif
4614     fprintf (st, "NO_LOCKLESS");
4615 #endif
4616 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
4617 # if defined(NO_SUPPORT_VERSION)
4618     fprintf (st, "\n  Modified: %s", VER_H_GIT_DATE);
4619 # else
4620     fprintf (st, "\n  Released: %s", VER_H_GIT_DATE);
4621 # endif
4622 #endif
4623 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
4624     fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
4625 #endif
4626 #ifdef VER_CURRENT_TIME
4627     fprintf (st, "\n  Compiled: %s", VER_CURRENT_TIME);
4628 #endif
4629     if (dirty)
4630       {
4631         fprintf (st, "\r\n\r\n ****** THIS BUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******");
4632       }
4633     fprintf (st, "\r\n\r\n Build Information:");
4634 #if defined (BUILD_PROM_OSV_TEXT) && defined (BUILD_PROM_OSA_TEXT)
4635     char build_os_version_raw[255];
4636     char build_os_arch_raw[255];
4637     sprintf(build_os_version_raw, "%.254s", BUILD_PROM_OSV_TEXT);
4638     sprintf(build_os_arch_raw, "%.254s", BUILD_PROM_OSA_TEXT);
4639     char *build_os_version = strdup(build_os_version_raw);
4640     if (!build_os_version)
4641       {
4642         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
4643                  __func__, __FILE__, __LINE__);
4644 # if defined(USE_BACKTRACE)
4645 #  ifdef SIGUSR2
4646         (void)raise(SIGUSR2);
4647         /*NOTREACHED*/ /* unreachable */
4648 #  endif /* ifdef SIGUSR2 */
4649 # endif /* if defined(USE_BACKTRACE) */
4650         abort();
4651       }
4652     char *build_os_arch = strdup(build_os_arch_raw);
4653     if (!build_os_arch)
4654       {
4655         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
4656                  __func__, __FILE__, __LINE__);
4657 # if defined(USE_BACKTRACE)
4658 #  ifdef SIGUSR2
4659         (void)raise(SIGUSR2);
4660         /*NOTREACHED*/ /* unreachable */
4661 #  endif /* ifdef SIGUSR2 */
4662 # endif /* if defined(USE_BACKTRACE) */
4663         abort();
4664       }
4665     strtrimspace(build_os_version, build_os_version_raw);
4666     strtrimspace(build_os_arch, build_os_arch_raw);
4667     fprintf (st, "\n  Build OS: %s %s", build_os_version, build_os_arch);
4668     FREE(build_os_version);
4669     FREE(build_os_arch);
4670 #endif
4671 #if defined (__VERSION__)
4672     char gnumver[2];
4673     char postver[1024];
4674     sprintf(gnumver, "%.1s", __VERSION__);
4675     sprintf(postver, "%.1023s", __VERSION__);
4676     strremove(postver, "(TM)");
4677     strremove(postver, "(R)");
4678     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
4679     strremove(postver, "https://github.com/OpenIndiana/oi-userland.git ");
4680     strremove(postver, "4.2.1 Compatible ");
4681     strremove(postver, "git@github.com:llvm/llvm-project.git ");
4682     strremove(postver, "https://github.com/llvm/llvm-project.git ");
4683     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
4684     strremove(postver, "https://github.com/yrnkrn/zapcc ");
4685 #endif
4686 #if ( defined (__GNUC__) && defined (__VERSION__) ) && !defined (__EDG__)
4687 # ifndef __clang_version__
4688     if (isdigit((unsigned char)gnumver[0])) {
4689         fprintf (st, "\n  Compiler: GCC %s", postver);
4690     } else {
4691         fprintf (st, "\n  Compiler: %s", postver);
4692     }
4693 # endif
4694 # if defined (__clang_analyzer__ )
4695     fprintf (st, "\n  Compiler: Clang C/C++ Static Analyzer");
4696 # elif defined (__clang_version__) && defined (__VERSION__)
4697     char clangllvmver[1024];
4698     sprintf(clangllvmver, "%.1023s", __clang_version__);
4699     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
4700     strremove(clangllvmver, "https://github.com/OpenIndiana/oi-userland.git ");
4701     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
4702         fprintf (st, "\n  Compiler: Clang %s", clangllvmver);
4703     } else {
4704         fprintf (st, "\n  Compiler: %s", postver);
4705     }
4706 # elif defined (__clang_version__)
4707     fprintf (st, "\n  Compiler: %s", postver);
4708 # endif
4709 #elif defined (__PGI) && !defined(__NVCOMPILER)
4710     fprintf (st, "\n  Compiler: Portland Group, Inc. (PGI) C Compiler ");
4711 # ifdef __PGIC__
4712     fprintf (st, "%d", __PGIC__);
4713 #  ifdef __PGIC_MINOR__
4714     fprintf (st, ".%d", __PGIC_MINOR__);
4715 #   ifdef __PGIC_PATCHLEVEL__
4716     fprintf (st, ".%d", __PGIC_PATCHLEVEL__);
4717 #   endif
4718 #  endif
4719 # endif
4720 #elif defined(__NVCOMPILER)
4721     fprintf (st, "\n  Compiler: NVIDIA HPC SDK C Compiler ");
4722 # ifdef __NVCOMPILER_MAJOR__
4723     fprintf (st, "%d", __NVCOMPILER_MAJOR__);
4724 #  ifdef __NVCOMPILER_MINOR__
4725     fprintf (st, ".%d", __NVCOMPILER_MINOR__);
4726 #   ifdef __NVCOMPILER_PATCHLEVEL__
4727     fprintf (st, ".%d", __NVCOMPILER_PATCHLEVEL__);
4728 #   endif
4729 #  endif
4730 # endif
4731 #elif defined (_MSC_FULL_VER) && defined (_MSC_BUILD)
4732     fprintf (st, "\n  Compiler: Microsoft C %d.%02d.%05d.%02d",
4733              _MSC_FULL_VER/10000000,
4734             (_MSC_FULL_VER/100000)%100,
4735              _MSC_FULL_VER%100000,
4736              _MSC_BUILD);
4737 #elif ( defined (__xlc__) && !defined(__clang_version__) )
4738 # if defined (_AIX) && defined (PASE)
4739     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
4740 # endif
4741 # if defined (_AIX) && !defined (PASE)
4742     fprintf (st, "\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
4743 # endif
4744 # if defined (__linux__) && ( !defined(_AIX) || !defined(PASE) )
4745     fprintf (st, "\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
4746 # endif
4747 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(PASE) && !defined(__linux__) && defined(__xlc__) )
4748     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s", __xlc__);
4749 # endif
4750 #elif defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__SUNPRO_CC_COMPAT)
4751     fprintf (st, "\n  Compiler: Oracle Developer Studio C/C++");
4752 #elif defined (__DMC__)
4753     fprintf (st, "\n  Compiler: Digital Mars C/C++");
4754 #elif defined (__PCC__)
4755     fprintf (st, "\n  Compiler: Portable C Compiler");
4756 #elif defined (KENC) || defined (KENCC) || defined (__KENC__) || defined (__KENCC__)
4757     fprintf (st, "\n  Compiler: Plan 9 Compiler Suite");
4758 #elif defined (__ACK__)
4759     fprintf (st, "\n  Compiler: Amsterdam Compiler Kit");
4760 #elif defined (__COMO__)
4761     fprintf (st, "\n  Compiler: Comeau C++");
4762 #elif defined (__COMPCERT__)
4763     fprintf (st, "\n  Compiler: CompCert C");
4764 #elif defined (__COVERITY__)
4765     fprintf (st, "\n  Compiler: Coverity C/C++ Static Analyzer");
4766 #elif defined (__LCC__)
4767     fprintf (st, "\n  Compiler: Local C Compiler (lcc)");
4768 #elif defined (sgi) || defined (__sgi) || defined (_sgi) || defined (_SGI_COMPILER_VERSION)
4769     fprintf (st, "\n  Compiler: SGI MIPSpro");
4770 #elif defined (__OPEN64__)
4771     fprintf (st, "\n  Compiler: Open64 %s", __OPEN64__);
4772 #elif defined (__PGI) || defined (__PGIC__)
4773     fprintf (st, "\n  Compiler: Portland Group/PGI C/C++");
4774 #elif defined (__VBCC__)
4775     fprintf (st, "\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
4776 #elif defined (__WATCOMC__)
4777     fprintf (st, "\n  Compiler: Watcom C/C++ %d.%d",
4778             __WATCOMC__ / 100,
4779             __WATCOMC__ % 100);
4780 #elif defined (__xlC__)
4781     fprintf (st, "\n  Compiler: IBM XL C/C++");
4782 #elif defined (SIM_COMPILER)
4783 # define S_xstr(a) S_str(a)
4784 # define S_str(a) #a
4785     fprintf (st, "\n  Compiler: %s", S_xstr(SIM_COMPILER));
4786 # undef S_str
4787 # undef S_xstr
4788 #else
4789     fprintf (st, "\n  Compiler: Unknown");
4790 #endif
4791 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
4792     arch = " x86_64";
4793 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
4794     arch = " x86";
4795 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
4796     arch = " arm64";
4797 #elif defined(_M_ARM) || defined(__arm__)
4798     arch = " arm";
4799 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
4800     arch = " ia64";
4801 #elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
4802 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4803     arch = " ppc64be";
4804 # else
4805     arch = " ppc64el";
4806 # endif
4807 #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)
4808 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4809     arch = " ppc";
4810 # else
4811     arch = " ppcel";
4812 # endif
4813 #elif defined(__s390x__)
4814     arch = " s390x";
4815 #elif defined(__s390__)
4816     arch = " s390";
4817 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
4818     arch = " j2";
4819 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
4820     arch = " sh4";
4821 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
4822     arch = " sh2";
4823 #elif defined(__alpha__)
4824     arch = " alpha";
4825 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
4826     arch = " hppa";
4827 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
4828     arch = " ice9";
4829 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
4830 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4831     arch = " mips64be";
4832 # else
4833     arch = " mips64el";
4834 # endif
4835 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
4836 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4837     arch = " mipsbe";
4838 # else
4839     arch = " mipsel";
4840 # endif
4841 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
4842     arch = " openrisc";
4843 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
4844     arch = " sparc64";
4845 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
4846     arch = " sparc";
4847 #elif defined(__riscv) || defined(__riscv__)
4848     arch = " riscv";
4849 #elif defined(__myriad2__)
4850     arch = " myriad2";
4851 #else
4852     arch = " ";
4853 #endif
4854     fprintf (st, "%s", arch);
4855 #if defined(BUILD_BY_USER)
4856         fprintf (st, "\n  Built by: %s", BUILD_BY_USER);
4857 #else
4858 # if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
4859         fprintf (st, "\n  Built by: %s", VER_H_PREP_USER);
4860 # endif
4861 #endif
4862                 fprintf (st, "\n\n Host System Information:");
4863 #if defined(_WIN32)
4864     if (1) {
4865         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
4866         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
4867         char osversion[PATH_MAX+1] = "";
4868         FILE *f;
4869 
4870         if ((f = _popen ("ver", "r"))) {
4871             memset (osversion, 0, sizeof(osversion));
4872             do {
4873                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
4874                     break;
4875                 sim_trim_endspc (osversion);
4876                 } while (osversion[0] == '\0');
4877             _pclose (f);
4878             }
4879         fprintf (st, "\n   Host OS: %s", osversion);
4880         fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
4881         }
4882 #else
4883     if (1) {
4884         char osversion[2*PATH_MAX+1] = "";
4885         FILE *f;
4886 # ifndef _AIX
4887         if ((f = popen ("uname -mrs 2> /dev/null", "r"))) {
4888 # else
4889         if ((f = popen                                              \
4890           ("sh -c 'command -p env uname -svM   2> /dev/null'        \
4891                                                2> /dev/null    ||   \
4892               /bin/sh -c 'command -p env uname -svM                 \
4893                                                2> /dev/null'        \
4894                                                2> /dev/null    ||   \
4895                   uname -svM                   2> /dev/null    ||   \
4896                     sh -c 'command -p env uname -svp                \
4897                                                2> /dev/null'        \
4898                                                2> /dev/null    ||   \
4899                         /bin/sh -c 'command -p env uname -svp       \
4900                                                2> /dev/null'        \
4901                                                2> /dev/null    ||   \
4902                             uname -svp         2> /dev/null         \
4903                               ", "r")))                             \
4904          {
4905 # endif /* ifndef _AIX */
4906             memset (osversion, 0, sizeof(osversion));
4907             do {
4908               if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
4909                     break;
4910               }
4911             sim_trim_endspc (osversion);
4912             } while (osversion[0] == '\0');
4913             pclose (f);
4914             strremove(osversion, "0000000000000000 ");
4915             strremove(osversion, " 0000000000000000");
4916             strremove(osversion, "000000000000 ");
4917             strremove(osversion, " 000000000000");
4918             strremove(osversion, "IBM ");
4919             strremove(osversion, " (emulated by qemu)");
4920             strremove(osversion, " (emulated by QEMU)");
4921         }
4922 # ifndef _AIX
4923             fprintf (st, "\n   Host OS: %s", osversion);
4924 # else
4925             strremove(osversion, "AIX ");
4926             fprintf (st, "\n   Host OS: IBM AIX %s", osversion);
4927 # endif /* ifndef _AIX */
4928     } else {
4929 # ifndef _AIX
4930         fprintf (st, "\n   Host OS: Unknown");
4931 # else
4932         fprintf (st, "\n   Host OS: IBM AIX");
4933 # endif /* ifndef _AIX */
4934     }
4935 #endif
4936 #if defined(__APPLE__)
4937     int isRosetta = processIsTranslated();
4938     if (isRosetta == 1) {
4939         sim_printf ("\n\n  ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
4940     }
4941 #endif
4942     if (nodist)
4943       {
4944         sim_printf ("\n\n ********* LICENSE RESTRICTED BUILD *** NOT FOR REDISTRIBUTION *********\n");
4945       }
4946     else
4947       {
4948         fprintf (st, "\n");
4949         fprintf (st, "\n This software is made available under the terms of the ICU License,");
4950         fprintf (st, "\n version 1.8.1 or later.  For complete details, see the \"LICENSE.md\"");
4951         fprintf (st, "\n included or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
4952       }
4953         fprintf (st, "\n");
4954     }
4955 return SCPE_OK;
4956 }
4957 
4958 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4959 {
4960 int32 i;
4961 DEVICE *dptr;
4962 t_bool only_enabled = (sim_switches & SWMASK ('E'));
4963 
4964 if (cptr && (*cptr != 0))
4965     return SCPE_2MARG;
4966 fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
4967 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4968     if (!only_enabled || !qdisable (dptr))
4969         show_device (st, dptr, flag);
4970 return SCPE_OK;
4971 }
4972 
4973 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4974 {
4975 int32 i;
4976 DEVICE *dptr;
4977 
4978 if (cptr && (*cptr != 0))
4979     return SCPE_2MARG;
4980 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4981     show_dev_logicals (st, dptr, NULL, 1, cptr);
4982 return SCPE_OK;
4983 }
4984 
4985 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4986 {
4987 if (dptr->lname)
4988     fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
4989 else if (!flag)
4990     fputs ("no logical name assigned\n", st);
4991 return SCPE_OK;
4992 }
4993 
4994 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4995 {
4996 DEVICE *dptr;
4997 UNIT *uptr;
4998 int32 accum;
4999 
5000 if (cptr && (*cptr != 0))
5001     return SCPE_2MARG;
5002 if (sim_clock_queue == QUEUE_LIST_END)
5003     fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructions/sec\n",
5004              sim_name, sim_time, sim_timer_inst_per_sec ());
5005 else {
5006     const char *tim;
5007 
5008     fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
5009              sim_name, sim_time, sim_timer_inst_per_sec ());
5010     accum = 0;
5011     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
5012         if (uptr == &sim_step_unit)
5013             fprintf (st, "  Step timer");
5014         else
5015             if (uptr == &sim_expect_unit)
5016                 fprintf (st, "  Expect fired");
5017             else
5018                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
5019                     fprintf (st, "  %s", sim_dname (dptr));
5020                     if (dptr->numunits > 1)
5021                         fprintf (st, " unit %d", (int32) (uptr - dptr->units));
5022                     }
5023                 else
5024                     fprintf (st, "  Unknown");
5025         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
5026         fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
5027                                         (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
5028                                         (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
5029         accum = accum + uptr->time;
5030         }
5031     }
5032 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
5033 return SCPE_OK;
5034 }
5035 
5036 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5037 {
5038 if (cptr && (*cptr != 0))
5039     return SCPE_2MARG;
5040 fprintf (st, "Time:\t%.0f\n", sim_gtime());
5041 return SCPE_OK;
5042 }
5043 
5044 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5045 {
5046 t_stat r;
5047 
5048 if (cptr && (*cptr != 0))
5049     r = ssh_break (st, cptr, 1);  /* more? */
5050 else
5051     r = sim_brk_showall (st, sim_switches);
5052 return r;
5053 }
5054 
5055 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5056 {
5057 fprintf (st, "Radix=%d\n", dptr->dradix);
5058 return SCPE_OK;
5059 }
5060 
5061 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5062 {
5063 int32 any = 0;
5064 DEBTAB *dep;
5065 
5066 if (dptr->flags & DEV_DEBUG) {
5067     if (dptr->dctrl == 0)
5068         fputs ("Debugging disabled", st);
5069     else if (dptr->debflags == NULL)
5070         fputs ("Debugging enabled", st);
5071     else {
5072         uint32 dctrl = dptr->dctrl;
5073 
5074         fputs ("Debug=", st);
5075         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
5076             if ((dctrl & dep->mask) == dep->mask) {
5077                 dctrl &= ~dep->mask;
5078                 if (any)
5079                     fputc (';', st);
5080                 fputs (dep->name, st);
5081                 any = 1;
5082                 }
5083             }
5084         }
5085     fputc ('\n', st);
5086     return SCPE_OK;
5087     }
5088 else return SCPE_NOFNC;
5089 }
5090 
5091 /* Show On actions */
5092 
5093 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5094 {
5095 int32 lvl, i;
5096 
5097 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
5098 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
5099     if (lvl > 0)
5100         fprintf(st, "On Processing at Do Nest Level: %d", lvl);
5101     else
5102         fprintf(st, "On Processing for input commands");
5103     fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
5104     for (i=1; i<SCPE_BASE; ++i) {
5105         if (sim_on_actions[lvl][i])
5106             fprintf(st, "    on %5d    %s\n", i, sim_on_actions[lvl][i]); }
5107     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
5108         if (sim_on_actions[lvl][i])
5109             fprintf(st, "    on %-5s    %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
5110     if (sim_on_actions[lvl][0])
5111         fprintf(st, "    on ERROR    %s\n", sim_on_actions[lvl][0]);
5112     fprintf(st, "\n");
5113     }
5114 if (sim_on_inherit)
5115     fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
5116 return SCPE_OK;
5117 }
5118 
5119 /* Show modifiers */
5120 
5121 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5122 {
5123 int32 i;
5124 DEVICE *dptr;
5125 
5126 if (cptr && (*cptr != 0))                               /* now eol? */
5127     return SCPE_2MARG;
5128 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5129     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5130 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5131     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5132 return SCPE_OK;
5133 }
5134 
5135 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5136 {
5137 fprint_set_help (st, dptr);
5138 return SCPE_OK;
5139 }
5140 
5141 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
     /* [previous][next][first][last][top][bottom][index][help] */
5142 {
5143 MTAB *mptr;
5144 t_stat r = SCPE_OK;
5145 
5146 if (dptr->modifiers == NULL)
5147     return SCPE_OK;
5148 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
5149     if (mptr->pstring &&
5150         ((mptr->mask & MTAB_XTD)?
5151             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
5152             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
5153         if (*toks > 0) {
5154             fprintf (st, "\n");
5155             *toks = 0;
5156             }
5157         if (r == SCPE_OK)
5158             fprint_sep (st, toks);
5159         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
5160         }
5161     }
5162 return SCPE_OK;
5163 }
5164 
5165 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
     /* [previous][next][first][last][top][bottom][index][help] */
5166     CONST char *cptr, int32 flag)
5167 {
5168 t_stat r = SCPE_OK;
5169 
5170 if (mptr->disp)
5171     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
5172 else
5173     fputs (mptr->pstring, st);
5174 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
5175     fputc ('\n', st);
5176 return r;
5177 }
5178 
5179 /* Show show commands */
5180 
5181 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5182 {
5183 int32 i;
5184 DEVICE *dptr;
5185 
5186 if (cptr && (*cptr != 0))                               /* now eol? */
5187     return SCPE_2MARG;
5188 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5189     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5190 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5191     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5192 return SCPE_OK;
5193 }
5194 
5195 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5196 {
5197 fprint_show_help (st, dptr);
5198 return SCPE_OK;
5199 }
5200 
5201 /* Breakpoint commands */
5202 
5203 t_stat brk_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5204 {
5205 GET_SWITCHES (cptr);                                    /* get switches */
5206 return ssh_break (NULL, cptr, flg);                     /* call common code */
5207 }
5208 
5209 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
     /* [previous][next][first][last][top][bottom][index][help] */
5210 {
5211 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
5212 CONST char *tptr, *t1ptr;
5213 DEVICE *dptr = sim_dflt_dev;
5214 UNIT *uptr;
5215 t_stat r;
5216 t_addr lo, hi, max;
5217 int32 cnt;
5218 
5219 if (sim_brk_types == 0)
5220     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5221 if (dptr == NULL)
5222     return SCPE_IERR;
5223 uptr = dptr->units;
5224 if (uptr == NULL)
5225     return SCPE_IERR;
5226 max = uptr->capac - 1;
5227 abuf[sizeof(abuf)-1] = '\0';
5228 strncpy (abuf, cptr, sizeof(abuf)-1);
5229 cptr = abuf;
5230 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
5231     if (flg != SSH_ST)                                  /* only on SET */
5232         return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
5233     *aptr++ = 0;                                        /* separate strings */
5234     }
5235 if (*cptr == 0) {                                       /* no argument? */
5236     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
5237     return ssh_break_one (st, flg, lo, 0, aptr);
5238     }
5239 while (*cptr) {
5240     cptr = get_glyph (cptr, gbuf, ',');
5241     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
5242     if (tptr == NULL)
5243         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
5244     if (*tptr == '[') {
5245         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
5246         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
5247             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
5248         tptr = t1ptr + 1;
5249         }
5250     else cnt = 0;
5251     if (*tptr != 0)
5252         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
5253     if ((lo == 0) && (hi == max)) {
5254         if (flg == SSH_CL)
5255             sim_brk_clrall (sim_switches);
5256         else
5257             if (flg == SSH_SH)
5258                 sim_brk_showall (st, sim_switches);
5259             else
5260                 return SCPE_ARG;
5261         }
5262     else {
5263         for ( ; lo <= hi; lo = lo + 1) {
5264             r = ssh_break_one (st, flg, lo, cnt, aptr);
5265             if (r != SCPE_OK)
5266                 return r;
5267             }
5268         }
5269     }
5270 return SCPE_OK;
5271 }
5272 
5273 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5274 {
5275 if (!sim_brk_types)
5276     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5277 switch (flg) {
5278 
5279     case SSH_ST:
5280         return sim_brk_set (lo, sim_switches, cnt, aptr);
5281         /*NOTREACHED*/ /* unreachable */
5282         break;
5283 
5284     case SSH_CL:
5285         return sim_brk_clr (lo, sim_switches);
5286         /*NOTREACHED*/ /* unreachable */
5287         break;
5288 
5289     case SSH_SH:
5290         return sim_brk_show (st, lo, sim_switches);
5291         /*NOTREACHED*/ /* unreachable */
5292         break;
5293 
5294     default:
5295         return SCPE_ARG;
5296     }
5297 }
5298 
5299 /* Reset command and routines */
5300 
5301 static t_bool run_cmd_did_reset = FALSE;
5302 
5303 t_stat reset_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5304 {
5305 char gbuf[CBUFSIZE];
5306 DEVICE *dptr;
5307 
5308 GET_SWITCHES (cptr);                                    /* get switches */
5309 run_cmd_did_reset = FALSE;
5310 if (*cptr == 0)                                         /* reset(cr) */
5311     return (reset_all (0));
5312 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5313 if (*cptr != 0)                                         /* now eol? */
5314     return SCPE_2MARG;
5315 if (strcmp (gbuf, "ALL") == 0)
5316     return (reset_all (0));
5317 dptr = find_dev (gbuf);                                 /* locate device */
5318 if (dptr == NULL)                                       /* found it? */
5319     return SCPE_NXDEV;
5320 if (dptr->reset != NULL)
5321     return dptr->reset (dptr);
5322 else return SCPE_OK;
5323 }
5324 
5325 /* Reset devices start..end
5326 
5327    Inputs:
5328         start   =       number of starting device
5329    Outputs:
5330         status  =       error status
5331 */
5332 
5333 t_stat reset_all (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5334 {
5335 DEVICE *dptr;
5336 uint32 i;
5337 t_stat reason;
5338 
5339 for (i = 0; i < start; i++) {
5340     if (sim_devices[i] == NULL)
5341         return SCPE_IERR;
5342     }
5343 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
5344     if (dptr->reset != NULL) {
5345         reason = dptr->reset (dptr);
5346         if (reason != SCPE_OK)
5347             return reason;
5348         }
5349     }
5350 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
5351     if (dptr->reset != NULL) {
5352         reason = dptr->reset (dptr);
5353         if (reason != SCPE_OK)
5354             return reason;
5355         }
5356     }
5357 return SCPE_OK;
5358 }
5359 
5360 /* Reset to powerup state
5361 
5362    Inputs:
5363         start   =       number of starting device
5364    Outputs:
5365         status  =       error status
5366 */
5367 
5368 t_stat reset_all_p (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5369 {
5370 t_stat r;
5371 int32 old_sw = sim_switches;
5372 
5373 sim_switches = SWMASK ('P');
5374 r = reset_all (start);
5375 sim_switches = old_sw;
5376 return r;
5377 }
5378 
5379 /* Attach command */
5380 
5381 t_stat attach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5382 {
5383 char gbuf[4*CBUFSIZE];
5384 DEVICE *dptr;
5385 UNIT *uptr;
5386 t_stat r;
5387 
5388 GET_SWITCHES (cptr);                                    /* get switches */
5389 if (*cptr == 0)                                         /* must be more */
5390     return SCPE_2FARG;
5391 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5392 GET_SWITCHES (cptr);                                    /* get switches */
5393 if (*cptr == 0)                                         /* now eol? */
5394     return SCPE_2FARG;
5395 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5396 if (dptr == NULL)                                       /* found dev? */
5397     return SCPE_NXDEV;
5398 if (uptr == NULL)                                       /* valid unit? */
5399     return SCPE_NXUN;
5400 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
5401     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
5402         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
5403         r = scp_detach_unit (dptr, uptr);               /* detach it */
5404         if (r != SCPE_OK)                               /* error? */
5405             return r; }
5406     else {
5407         if (!(uptr->dynflags & UNIT_ATTMULT))
5408             return SCPE_ALATT;                          /* Already attached */
5409         }
5410     }
5411 gbuf[sizeof(gbuf)-1] = '\0';
5412 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5413 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
5414 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
5415 }
5416 
5417 /* Call device-specific or file-oriented attach unit routine */
5418 
5419 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5420 {
5421 if (dptr->attach != NULL)                               /* device routine? */
5422     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
5423 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
5424 }
5425 
5426 /* Attach unit to file */
5427 
5428 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5429 {
5430 DEVICE *dptr;
5431 
5432 if (uptr->flags & UNIT_DIS)                             /* disabled? */
5433     return SCPE_UDIS;
5434 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
5435     return SCPE_NOATT;
5436 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5437     return SCPE_NOATT;
5438 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
5439 if (uptr->filename == NULL)
5440     return SCPE_MEM;
5441 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
5442 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
5443     ((uptr->flags & UNIT_RO) != 0)) {
5444     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
5445         ((uptr->flags & UNIT_RO) == 0))
5446         return attach_err (uptr, SCPE_NORO);            /* no, error */
5447     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
5448     if (uptr->fileref == NULL)                          /* open fail? */
5449         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
5450     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
5451     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5452         sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
5453         }
5454     }
5455 else {
5456     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
5457         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
5458         if (uptr->fileref == NULL)                      /* open fail? */
5459             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
5460         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5461             sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
5462             }
5463         }
5464     else {                                              /* normal */
5465         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
5466         if (uptr->fileref == NULL) {                    /* open fail? */
5467 #if defined (EWOULDBLOCK)
5468             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
5469 #else
5470             if ((errno == EAGAIN))
5471 #endif
5472                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5473 
5474 #if defined(EPERM)
5475             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
5476 #else
5477             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
5478 #endif
5479                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
5480                     return attach_err (uptr, SCPE_NORO);/* no error */
5481                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
5482                 if (uptr->fileref == NULL)              /* open fail? */
5483                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5484                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
5485                 if (!sim_quiet) {
5486                     sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
5487                     }
5488                 }
5489             else {                                      /* doesn't exist */
5490                 if (sim_switches & SWMASK ('E'))        /* must exist? */
5491                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5492                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
5493                 if (uptr->fileref == NULL)              /* open fail? */
5494                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5495                 if (!sim_quiet) {
5496                     sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
5497                     }
5498                 }
5499             }                                           /* end if null */
5500         }                                               /* end else */
5501     }
5502 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
5503     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
5504     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
5505         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
5506     if (uptr->filebuf == NULL)                          /* no buffer? */
5507         return attach_err (uptr, SCPE_MEM);             /* error */
5508     if (!sim_quiet) {
5509         sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
5510         }
5511     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
5512         SZ_D (dptr), cap, uptr->fileref);
5513     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
5514     }
5515 uptr->flags = uptr->flags | UNIT_ATT;
5516 uptr->pos = 0;
5517 return SCPE_OK;
5518 }
5519 
5520 t_stat attach_err (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
5521 {
5522 FREE (uptr->filename);
5523 uptr->filename = NULL;
5524 return stat;
5525 }
5526 
5527 /* Detach command */
5528 
5529 t_stat detach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5530 {
5531 char gbuf[CBUFSIZE];
5532 DEVICE *dptr;
5533 UNIT *uptr;
5534 
5535 GET_SWITCHES (cptr);                                    /* get switches */
5536 if (*cptr == 0)                                         /* must be more */
5537     return SCPE_2FARG;
5538 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5539 if (*cptr != 0)                                         /* now eol? */
5540     return SCPE_2MARG;
5541 if (strcmp (gbuf, "ALL") == 0)
5542     return (detach_all (0, FALSE));
5543 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5544 if (dptr == NULL)                                       /* found dev? */
5545     return SCPE_NXDEV;
5546 if (uptr == NULL)                                       /* valid unit? */
5547     return SCPE_NXUN;
5548 return scp_detach_unit (dptr, uptr);                    /* detach */
5549 }
5550 
5551 /* Detach devices start..end
5552 
5553    Inputs:
5554         start   =       number of starting device
5555         shutdown =      TRUE if simulator shutting down
5556    Outputs:
5557         status  =       error status
5558 
5559    Note that during shutdown, detach routines for non-attachable devices
5560    will be called.  These routines can implement simulator shutdown.  Error
5561    returns during shutdown are ignored.
5562 */
5563 
5564 t_stat detach_all (int32 start, t_bool shutdown)
     /* [previous][next][first][last][top][bottom][index][help] */
5565 {
5566 uint32 i, j;
5567 DEVICE *dptr;
5568 UNIT *uptr;
5569 t_stat r;
5570 
5571 if ((start < 0) || (start > 1))
5572     return SCPE_IERR;
5573 if (shutdown)
5574     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
5575 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5576     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
5577         uptr = (dptr->units) + j;
5578         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
5579             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
5580             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
5581             r = scp_detach_unit (dptr, uptr);           /* detach unit */
5582 
5583             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
5584                 return r;                               /* bail out now with error status */
5585             }
5586         }
5587     }
5588 return SCPE_OK;
5589 }
5590 
5591 /* Call device-specific or file-oriented detach unit routine */
5592 
5593 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5594 {
5595 if (dptr->detach != NULL)                               /* device routine? */
5596     return dptr->detach (uptr);
5597 return detach_unit (uptr);                              /* no, standard */
5598 }
5599 
5600 /* Detach unit from file */
5601 
5602 t_stat detach_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5603 {
5604 DEVICE *dptr;
5605 
5606 if (uptr == NULL)
5607     return SCPE_IERR;
5608 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
5609     return SCPE_NOATT;
5610 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
5611     if (sim_switches & SIM_SW_REST)                     /* restoring? */
5612         return SCPE_OK;                                 /* allow detach */
5613     else
5614         return SCPE_NOTATT;                             /* complain */
5615     }
5616 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5617     return SCPE_OK;
5618 if (uptr->flags & UNIT_BUF) {
5619     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5620     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
5621         if (!sim_quiet) {
5622             sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
5623             }
5624         rewind (uptr->fileref);
5625         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5626         if (ferror (uptr->fileref))
5627             sim_printf ("%s: I/O error - %s", sim_dname (dptr), strerror (errno));
5628         }
5629     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
5630         FREE (uptr->filebuf);                           /* free buf */
5631         uptr->filebuf = NULL;
5632         }
5633     uptr->flags = uptr->flags & ~UNIT_BUF;
5634     }
5635 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
5636 FREE (uptr->filename);
5637 uptr->filename = NULL;
5638 if (fclose (uptr->fileref) == EOF)
5639     return SCPE_IOERR;
5640 return SCPE_OK;
5641 }
5642 
5643 /* Get device display name */
5644 
5645 const char *sim_dname (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5646 {
5647 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
5648 }
5649 
5650 /* Get unit display name */
5651 
5652 const char *sim_uname (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5653 {
5654 DEVICE *d = find_dev_from_unit(uptr);
5655 static char uname[CBUFSIZE];
5656 
5657 if (!d)
5658     return "";
5659 if (d->numunits == 1)
5660     return sim_dname (d);
5661 sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
5662 return uname;
5663 }
5664 
5665 /* Run, go, boot, cont, step, next commands */
5666 
5667 t_stat run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5668 {
5669 char gbuf[CBUFSIZE] = "";
5670 CONST char *tptr;
5671 uint32 i, j;
5672 int32 sim_next = 0;
5673 int32 unitno;
5674 t_value pcv, orig_pcv;
5675 t_stat r;
5676 DEVICE *dptr;
5677 UNIT *uptr;
5678 
5679 GET_SWITCHES (cptr);                                    /* get switches */
5680 sim_step = 0;
5681 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
5682     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
5683     if (*cptr != 0) {                                   /* argument? */
5684         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5685         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
5686             if (sim_dflt_dev && sim_vm_parse_addr)      /* address parser? */
5687                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
5688             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
5689             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
5690                 (pcv > width_mask[sim_PC->width]))
5691                 return SCPE_ARG;
5692             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
5693             }
5694         }
5695     if ((flag == RU_RUN) &&                             /* run? */
5696         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
5697         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
5698         return r;
5699         }
5700     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { //-V600 /* should be end */
5701         int32 saved_switches = sim_switches;
5702 
5703         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5704             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
5705         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5706             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
5707                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
5708         sim_switches = 0;
5709         GET_SWITCHES (cptr);
5710         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
5711             r = expect_cmd (1, cptr);
5712             if (r != SCPE_OK)
5713                 return r;
5714             }
5715         else {                                          /* BREAK UNTIL condition */
5716             if (sim_switches == 0)
5717                 sim_switches = sim_brk_dflt;
5718             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
5719             sim_brk_types |= BRK_TYP_TEMP;
5720             r = ssh_break (NULL, cptr, SSH_ST);
5721             if (r != SCPE_OK)
5722                 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
5723             }
5724         sim_switches = saved_switches;
5725         }
5726     }
5727 
5728 else if ((flag == RU_STEP) ||
5729          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
5730     static t_bool not_implemented_message = FALSE;
5731 
5732     if ((!not_implemented_message) && (flag == RU_NEXT)) {
5733         not_implemented_message = TRUE;
5734         flag = RU_STEP;
5735         }
5736     if (*cptr != 0) {                                   /* argument? */
5737         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5738         if (*cptr != 0)                                 /* should be end */
5739             return SCPE_2MARG;
5740         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5741         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
5742             return SCPE_ARG;
5743         }
5744     else sim_step = 1;
5745     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
5746         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
5747     }
5748 else if (flag == RU_NEXT) {                             /* next */
5749     t_addr *addrs;
5750 
5751     if (*cptr != 0) {                                   /* argument? */
5752         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5753         if (*cptr != 0)                                 /* should be end */
5754             return SCPE_2MARG;
5755         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5756         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
5757             return SCPE_ARG;
5758         }
5759     else sim_next = 1;
5760     if (sim_vm_is_subroutine_call(&addrs)) {
5761         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
5762         for (i=0; addrs[i]; i++)
5763             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
5764         }
5765     else
5766         sim_step = 1;
5767     }
5768 else if (flag == RU_BOOT) {                             /* boot */
5769     if (*cptr == 0)                                     /* must be more */
5770         return SCPE_2FARG;
5771     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
5772     if (*cptr != 0)                                     /* should be end */
5773         return SCPE_2MARG;
5774     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
5775     if (dptr == NULL)                                   /* found dev? */
5776         return SCPE_NXDEV;
5777     if (uptr == NULL)                                   /* valid unit? */
5778         return SCPE_NXUN;
5779     if (dptr->boot == NULL)                             /* can it boot? */
5780         return SCPE_NOFNC;
5781     if (uptr->flags & UNIT_DIS)                         /* disabled? */
5782         return SCPE_UDIS;
5783     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
5784         !(uptr->flags & UNIT_ATT))
5785         return SCPE_UNATT;
5786     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
5787     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
5788         return r;
5789     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
5790         return r;
5791     }
5792 
5793 else
5794     if (flag != RU_CONT)                                /* must be cont */
5795         return SCPE_IERR;
5796     else                                                /* CONTINUE command */
5797         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
5798             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
5799 
5800 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
5801     return SCPE_OK;
5802 
5803 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
5804     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
5805         uptr = dptr->units + j;
5806         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
5807             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
5808         }
5809     }
5810 stop_cpu = 0;
5811 sim_is_running = 1;                                     /* flag running */
5812 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
5813     sim_is_running = 0;                                 /* flag idle */
5814     sim_ttcmd ();
5815     return SCPE_TTYERR;
5816     }
5817 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
5818     sim_is_running = 0;                                 /* flag idle */
5819     sim_ttcmd ();
5820     return r;
5821     }
5822 #ifndef IS_WINDOWS
5823 # ifdef SIGINT
5824 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
5825     sim_is_running = 0;                                 /* flag idle */
5826     sim_ttcmd ();
5827     return SCPE_SIGERR;
5828     }
5829 # endif
5830 #endif
5831 #ifndef IS_WINDOWS
5832 # ifdef SIGHUP
5833 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
5834     sim_is_running = 0;                                 /* flag idle */
5835     sim_ttcmd ();
5836     return SCPE_SIGERR;
5837     }
5838 # endif
5839 #endif
5840 #ifndef IS_WINDOWS
5841 # ifdef SIGTERM
5842 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
5843     sim_is_running = 0;                                 /* flag idle */
5844     sim_ttcmd ();
5845     return SCPE_SIGERR;
5846     }
5847 # endif
5848 #endif
5849 if (sim_step)                                           /* set step timer */
5850     sim_activate (&sim_step_unit, sim_step);
5851 fflush(stdout);                                         /* flush stdout */
5852 if (sim_log)                                            /* flush log if enabled */
5853     fflush (sim_log);
5854 sim_rtcn_init_all ();                                   /* re-init clocks */
5855 sim_start_timer_services ();                            /* enable wall clock timing */
5856 
5857 do {
5858     t_addr *addrs;
5859 
5860     while (1) {
5861         r = sim_instr();
5862         if (r != SCPE_REMOTE)
5863             break;
5864         sim_remote_process_command ();                  /* Process the command and resume processing */
5865         }
5866     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
5867         (--sim_next <=0))
5868         break;
5869     if (sim_step == 0) {                                /* doing a NEXT? */
5870         t_addr val;
5871         BRKTAB *bp;
5872 
5873         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
5874             break;
5875         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
5876             val = (t_addr)(*sim_vm_pc_value)();
5877         else
5878             val = (t_addr)get_rval (sim_PC, 0);
5879         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
5880             break;
5881         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
5882         }
5883     else {
5884         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
5885             break;
5886         }
5887     /* setup another next/step */
5888     sim_step = 0;
5889     if (sim_vm_is_subroutine_call(&addrs)) {
5890         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
5891         for (i=0; addrs[i]; i++)
5892             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
5893         }
5894     else
5895         sim_step = 1;
5896     if (sim_step)                                       /* set step timer */
5897         sim_activate (&sim_step_unit, sim_step);
5898     } while (1);
5899 
5900 sim_is_running = 0;                                     /* flag idle */
5901 sim_stop_timer_services ();                             /* disable wall clock timing */
5902 sim_ttcmd ();                                           /* restore console */
5903 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
5904 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
5905 #ifdef SIGHUP
5906 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
5907 #endif
5908 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
5909 if (sim_log)                                            /* flush console log */
5910     fflush (sim_log);
5911 if (sim_deb)                                            /* flush debug log */
5912     sim_debug_flush ();
5913 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
5914     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
5915         uptr = dptr->units + j;
5916         if (uptr->flags & UNIT_ATT) {                   /* attached, */
5917             if (uptr->io_flush)                         /* unit specific flush routine */
5918                 uptr->io_flush (uptr);                  /* call it */
5919             else {
5920                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
5921                     (uptr->fileref) &&                  /* real file, */
5922                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
5923                     !(uptr->flags & UNIT_RO))           /* not read only? */
5924                     fflush (uptr->fileref);
5925                 }
5926             }
5927         }
5928     }
5929 sim_cancel (&sim_step_unit);                            /* cancel step timer */
5930 UPDATE_SIM_TIME;                                        /* update sim time */
5931 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
5932 }
5933 
5934 /* run command message handler */
5935 
5936 void
5937 run_cmd_message (const char *unechoed_cmdline, t_stat r)
     /* [previous][next][first][last][top][bottom][index][help] */
5938 {
5939 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
5940     sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
5941 #ifdef WIN_STDIO
5942 (void)fflush(stderr);
5943 (void)fflush(stdout);
5944 #endif /* ifdef WIN_STDIO */
5945 fprint_stopped (stdout, r);                         /* print msg */
5946 if (sim_log && (sim_log != stdout))                 /* log if enabled */
5947     fprint_stopped (sim_log, r);
5948 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
5949     fprint_stopped (sim_deb, r);
5950 #ifdef WIN_STDIO
5951 (void)fflush(stderr);
5952 (void)fflush(stdout);
5953 #endif /* ifdef WIN_STDIO */
5954 }
5955 
5956 /* Common setup for RUN or BOOT */
5957 
5958 t_stat sim_run_boot_prep (int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
5959 {
5960 UNIT *uptr;
5961 t_stat r;
5962 
5963 sim_interval = 0;                                       /* reset queue */
5964 sim_time = sim_rtime = 0;
5965 noqueue_time = 0;
5966 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
5967     sim_clock_queue = uptr->next;
5968     uptr->next = NULL;
5969     }
5970 r = reset_all (0);
5971 if ((r == SCPE_OK) && (flag == RU_RUN)) {
5972     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
5973         sim_printf ("Resetting all devices...  This may not have been your intention.\n");
5974         sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
5975         }
5976     run_cmd_did_reset = TRUE;
5977     }
5978 return r;
5979 }
5980 
5981 /* Print stopped message
5982  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
5983  * call the indicated routine to print additional information after the message
5984  * and before the PC value is printed.  If the routine returns FALSE, skip
5985  * printing the PC and its related instruction.
5986  */
5987 
5988 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5989 {
5990 int32 i;
5991 t_stat r = 0;
5992 t_addr k;
5993 t_value pcval;
5994 
5995 fputc ('\n', st);                                       /* start on a new line */
5996 
5997 if (v >= SCPE_BASE)                                     /* SCP error? */
5998     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
5999 else {                                                  /* VM error */
6000     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6001 
6002     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6003         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6004         return;                                         /*     we're done */
6005     }
6006 
6007 fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6008 
6009 pcval = get_rval (pc, 0);
6010 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6011     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6012 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6013     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6014 if ((dptr != NULL) && (dptr->examine != NULL)) {
6015     for (i = 0; i < sim_emax; i++)
6016         sim_eval[i] = 0;
6017     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6018         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6019             break;
6020         }
6021     if ((r == SCPE_OK) || (i > 0)) {
6022         fprintf (st, " (");
6023         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6024             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6025         fprintf (st, ")");
6026         }
6027     }
6028 fprintf (st, "\n");
6029 return;
6030 }
6031 
6032 void fprint_stopped (FILE *st, t_stat v)
     /* [previous][next][first][last][top][bottom][index][help] */
6033 {
6034 #ifdef WIN_STDIO
6035 (void)fflush(stderr);
6036 (void)fflush(stdout);
6037 #endif /* ifdef WIN_STDIO */
6038 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6039 return;
6040 }
6041 
6042 /* Unit service for step timeout, originally scheduled by STEP n command
6043    Return step timeout SCP code, will cause simulation to stop */
6044 
6045 t_stat step_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6046 {
6047 return SCPE_STEP;
6048 }
6049 
6050 /* Unit service to facilitate expect matching to stop simulation.
6051    Return expect SCP code, will cause simulation to stop */
6052 
6053 t_stat expect_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6054 {
6055 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6056 }
6057 
6058 /* Signal handler for ^C signal - set stop simulation flag */
6059 
6060 void int_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help] */
6061 {
6062 stop_cpu = 1;
6063 return;
6064 }
6065 
6066 /* Examine/deposit commands */
6067 
6068 t_stat exdep_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6069 {
6070 char gbuf[CBUFSIZE];
6071 CONST char *gptr;
6072 CONST char *tptr = NULL;
6073 int32 opt;
6074 t_addr low, high;
6075 t_stat reason = SCPE_IERR;
6076 DEVICE *tdptr;
6077 REG *lowr, *highr;
6078 FILE *ofile;
6079 
6080 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6081 if (flag == EX_E)                                       /* extra for EX */
6082     opt = opt | CMD_OPT_OF;
6083 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6084 if (!cptr)                                              /* error? */
6085     return reason;
6086 if (*cptr == 0)                                         /* must be more */
6087     return SCPE_2FARG;
6088 if (sim_dfunit == NULL)                                 /* got a unit? */
6089     return SCPE_NXUN;
6090 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6091 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6092 
6093     return SCPE_2FARG;
6094 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6095 
6096 for (gptr = gbuf, reason = SCPE_OK;
6097     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6098     tdptr = sim_dfdev;                                  /* working dptr */
6099     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6100         tptr = gptr + strlen ("STATE");
6101         if (*tptr && (*tptr++ != ','))
6102             return SCPE_ARG;
6103         if ((lowr = sim_dfdev->registers) == NULL)
6104             return SCPE_NXREG;
6105         for (highr = lowr; highr->name != NULL; highr++) ;
6106         sim_switches = sim_switches | SIM_SW_HIDE;
6107         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6108             lowr, --highr, 0, 0);
6109         continue;
6110         }
6111 
6112     /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
6113     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6114         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6115         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6116         low = high = 0;
6117         if ((*tptr == '-') || (*tptr == ':')) {
6118             highr = find_reg (tptr + 1, &tptr, tdptr);
6119             if (highr == NULL)
6120                 return SCPE_NXREG;
6121             }
6122         else {
6123             highr = lowr;
6124             if (*tptr == '[') {
6125                 if (lowr->depth <= 1)
6126                     return SCPE_ARG;
6127                 tptr = get_range (NULL, tptr + 1, &low, &high,
6128                     10, lowr->depth - 1, ']');
6129                 if (tptr == NULL)
6130                     return SCPE_ARG;
6131                 }
6132             }
6133         if (*tptr && (*tptr++ != ','))
6134             return SCPE_ARG;
6135         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6136             lowr, highr, (uint32) low, (uint32) high);
6137         continue;
6138         }
6139 
6140     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6141         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6142         sim_dfunit->capac - sim_dfdev->aincr), 0);
6143     if (tptr == NULL)
6144         return SCPE_ARG;
6145     if (*tptr && (*tptr++ != ','))
6146         return SCPE_ARG;
6147     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6148         sim_dfdev, sim_dfunit);
6149     }                                                   /* end for */
6150 if (sim_ofile)                                          /* close output file */
6151     fclose (sim_ofile);
6152 return reason;
6153 }
6154 
6155 /* Loop controllers for examine/deposit
6156 
6157    exdep_reg_loop       examine/deposit range of registers
6158    exdep_addr_loop      examine/deposit range of addresses
6159 */
6160 
6161 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6162     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6163 {
6164 t_stat reason;
6165 uint32 idx, val_start=lows;
6166 t_value val, last_val;
6167 REG *rptr;
6168 
6169 if ((lowr == NULL) || (highr == NULL))
6170     return SCPE_IERR;
6171 if (lowr > highr)
6172     return SCPE_ARG;
6173 for (rptr = lowr; rptr <= highr; rptr++) {
6174     if ((sim_switches & SIM_SW_HIDE) &&
6175         (rptr->flags & REG_HIDDEN))
6176         continue;
6177     val = last_val = 0;
6178     for (idx = lows; idx <= highs; idx++) {
6179         if (idx >= rptr->depth)
6180             return SCPE_SUB;
6181         val = get_rval (rptr, idx);
6182         if (schptr && !test_search (&val, schptr))
6183             continue;
6184         if (flag == EX_E) {
6185             if ((idx > lows) && (val == last_val))
6186                 continue;
6187             if (idx > val_start+1) {
6188                 if (idx-1 == val_start+1) {
6189                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6190                     if (reason != SCPE_OK)
6191                         return reason;
6192                     if (sim_log && (ofile == stdout))
6193                         ex_reg (sim_log, val, flag, rptr, idx-1);
6194                     }
6195                 else {
6196                     if (val_start+1 != idx-1) {
6197                         Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6198                         if (sim_log && (ofile == stdout))
6199                             Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6200                         }
6201                     else {
6202                         Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6203                         if (sim_log && (ofile == stdout))
6204                             Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6205                         }
6206                     }
6207                 }
6208             sim_last_val = last_val = val;
6209             val_start = idx;
6210             reason = ex_reg (ofile, val, flag, rptr, idx);
6211             if (reason != SCPE_OK)
6212                 return reason;
6213             if (sim_log && (ofile == stdout))
6214                 ex_reg (sim_log, val, flag, rptr, idx);
6215             }
6216         if (flag != EX_E) {
6217             reason = dep_reg (flag, cptr, rptr, idx);
6218             if (reason != SCPE_OK)
6219                 return reason;
6220             }
6221         }
6222     if ((flag == EX_E) && (val_start != highs)) {
6223         if (highs == val_start+1) {
6224             reason = ex_reg (ofile, val, flag, rptr, highs);
6225             if (reason != SCPE_OK)
6226                 return reason;
6227             if (sim_log && (ofile == stdout))
6228                 ex_reg (sim_log, val, flag, rptr, highs);
6229             }
6230         else {
6231             if (val_start+1 != highs) {
6232                 Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6233                 if (sim_log && (ofile == stdout))
6234                     Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6235                 }
6236             else {
6237                 Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6238                 if (sim_log && (ofile == stdout))
6239                     Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6240                 }
6241             }
6242         }
6243     }
6244 return SCPE_OK;
6245 }
6246 
6247 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6248     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6249 {
6250 t_addr i, mask;
6251 t_stat reason;
6252 
6253 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6254     return SCPE_UDIS;
6255 mask = (t_addr) width_mask[dptr->awidth];
6256 if ((low > mask) || (high > mask) || (low > high))
6257     return SCPE_ARG;
6258 for (i = low; i <= high; ) {                            /* all paths must incr!! */
6259     reason = get_aval (i, dptr, uptr);                  /* get data */
6260     if (reason != SCPE_OK)                              /* return if error */
6261         return reason;
6262     if (schptr && !test_search (sim_eval, schptr))
6263         i = i + dptr->aincr;                            /* sch fails, incr */
6264     else {                                              /* no sch or success */
6265         if (flag != EX_D) {                             /* ex, ie, or id? */
6266             reason = ex_addr (ofile, flag, i, dptr, uptr);
6267             if (reason > SCPE_OK)
6268                 return reason;
6269             if (sim_log && (ofile == stdout))
6270                 ex_addr (sim_log, flag, i, dptr, uptr);
6271             }
6272         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
6273         if (flag != EX_E) {                             /* ie, id, or d? */
6274             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6275             if (reason > SCPE_OK)
6276                 return reason;
6277             }
6278         i = i + (1 - reason);                           /* incr */
6279         }
6280     }
6281 return SCPE_OK;
6282 }
6283 
6284 /* Examine register routine
6285 
6286    Inputs:
6287         ofile   =       output stream
6288         val     =       current register value
6289         flag    =       type of ex/mod command (ex, iex, idep)
6290         rptr    =       pointer to register descriptor
6291         idx     =       index
6292    Outputs:
6293         return  =       error status
6294 */
6295 
6296 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6297 {
6298 int32 rdx;
6299 
6300 if (rptr == NULL)
6301     return SCPE_IERR;
6302 if (rptr->depth > 1)
6303     Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6304 else Fprintf (ofile, "%s:\t", rptr->name);
6305 if (!(flag & EX_E))
6306     return SCPE_OK;
6307 GET_RADIX (rdx, rptr->radix);
6308 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6309     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6310 else if (!(rptr->flags & REG_VMFLAGS) ||
6311     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6312                  NULL, sim_switches | SIM_SW_REG) > 0)) {
6313         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6314         if (rptr->fields) {
6315             Fprintf (ofile, "\t");
6316             fprint_fields (ofile, val, val, rptr->fields);
6317             }
6318         }
6319 if (flag & EX_I)
6320     Fprintf (ofile, "\t");
6321 else Fprintf (ofile, "\n");
6322 return SCPE_OK;
6323 }
6324 
6325 /* Get register value
6326 
6327    Inputs:
6328         rptr    =       pointer to register descriptor
6329         idx     =       index
6330    Outputs:
6331         return  =       register value
6332 */
6333 
6334 t_value get_rval (REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6335 {
6336 size_t sz;
6337 t_value val;
6338 uint32 *ptr;
6339 
6340 sz = SZ_R (rptr);
6341 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6342     idx = idx + rptr->qptr;
6343     if (idx >= rptr->depth) idx = idx - rptr->depth;
6344     }
6345 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6346     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6347     if (sz <= sizeof (uint32))
6348         val = *ptr;
6349     else val = *((t_uint64 *) ptr); //-V1032
6350     }
6351 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6352     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6353     if (sz <= sizeof (uint32))
6354         val = *ptr;
6355     else val = *((t_uint64 *) ptr);
6356     }
6357 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6358     (sz == sizeof (uint8)))
6359     val = *(((uint8 *) rptr->loc) + idx);
6360 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6361     (sz == sizeof (uint16)))
6362     val = *(((uint16 *) rptr->loc) + idx);
6363 else if (sz <= sizeof (uint32))
6364      val = *(((uint32 *) rptr->loc) + idx);
6365 else val = *(((t_uint64 *) rptr->loc) + idx);
6366 val = (val >> rptr->offset) & width_mask[rptr->width];
6367 return val;
6368 }
6369 
6370 /* Deposit register routine
6371 
6372    Inputs:
6373         flag    =       type of deposit (normal/interactive)
6374         cptr    =       pointer to input string
6375         rptr    =       pointer to register descriptor
6376         idx     =       index
6377    Outputs:
6378         return  =       error status
6379 */
6380 
6381 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6382 {
6383 t_stat r;
6384 t_value val, mask;
6385 int32 rdx;
6386 CONST char *tptr;
6387 char gbuf[CBUFSIZE];
6388 
6389 if ((cptr == NULL) || (rptr == NULL))
6390     return SCPE_IERR;
6391 if (rptr->flags & REG_RO)
6392     return SCPE_RO;
6393 if (flag & EX_I) {
6394     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6395     if (sim_log)
6396         fprintf (sim_log, "%s\n", cptr? cptr: "");
6397     if (cptr == NULL)                                   /* force exit */
6398         return 1;
6399     if (*cptr == 0)                                     /* success */
6400         return SCPE_OK;
6401     }
6402 mask = width_mask[rptr->width];
6403 GET_RADIX (rdx, rptr->radix);
6404 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
6405     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
6406     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
6407         return SCPE_ARG;
6408     }
6409 else
6410     if (!(rptr->flags & REG_VMFLAGS) ||                 /* don't use sym? */
6411         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
6412                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
6413     val = get_uint (cptr, rdx, mask, &r);
6414     if (r != SCPE_OK)
6415         return SCPE_ARG;
6416     }
6417 if ((rptr->flags & REG_NZ) && (val == 0))
6418     return SCPE_ARG;
6419 put_rval (rptr, idx, val);
6420 return SCPE_OK;
6421 }
6422 
6423 /* Put register value
6424 
6425    Inputs:
6426         rptr    =       pointer to register descriptor
6427         idx     =       index
6428         val     =       new value
6429         mask    =       mask
6430    Outputs:
6431         none
6432 */
6433 
6434 void put_rval (REG *rptr, uint32 idx, t_value val)
     /* [previous][next][first][last][top][bottom][index][help] */
6435 {
6436 size_t sz;
6437 t_value mask;
6438 uint32 *ptr;
6439 
6440 #define PUT_RVAL(sz,rp,id,v,m)            \
6441     *(((sz *) rp->loc) + id) =            \
6442         (sz)((*(((sz *) rp->loc) + id) &  \
6443             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
6444 
6445 if (rptr == sim_PC)
6446     sim_brk_npc (0);
6447 sz = SZ_R (rptr);
6448 mask = width_mask[rptr->width];
6449 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6450     idx = idx + rptr->qptr;
6451     if (idx >= rptr->depth)
6452         idx = idx - rptr->depth;
6453     }
6454 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6455     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6456     if (sz <= sizeof (uint32))
6457         *ptr = (*ptr &
6458         ~(((uint32) mask) << rptr->offset)) |
6459         (((uint32) val) << rptr->offset);
6460     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr) //-V1032
6461         & ~(mask << rptr->offset)) | (val << rptr->offset);
6462     }
6463 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6464     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6465     if (sz <= sizeof (uint32))
6466         *((uint32 *) ptr) = (*((uint32 *) ptr) &
6467         ~(((uint32) mask) << rptr->offset)) |
6468         (((uint32) val) << rptr->offset);
6469     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6470         & ~(mask << rptr->offset)) | (val << rptr->offset);
6471     }
6472 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6473     (sz == sizeof (uint8)))
6474     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
6475 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6476     (sz == sizeof (uint16)))
6477     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
6478 else if (sz <= sizeof (uint32))
6479     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
6480 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
6481 return;
6482 }
6483 
6484 /* Examine address routine
6485 
6486    Inputs: (sim_eval is an implicit argument)
6487         ofile   =       output stream
6488         flag    =       type of ex/mod command (ex, iex, idep)
6489         addr    =       address to examine
6490         dptr    =       pointer to device
6491         uptr    =       pointer to unit
6492    Outputs:
6493         return  =       if > 0, error status
6494                         if <= 0,-number of extra addr units retired
6495 */
6496 
6497 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6498 {
6499 t_stat reason;
6500 int32 rdx;
6501 
6502 if (sim_vm_fprint_addr)
6503     sim_vm_fprint_addr (ofile, dptr, addr);
6504 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
6505 Fprintf (ofile, ":\t");
6506 if (!(flag & EX_E))
6507     return (1 - dptr->aincr);
6508 
6509 GET_RADIX (rdx, dptr->dradix);
6510 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
6511     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
6512     reason = 1 - dptr->aincr;
6513     }
6514 if (flag & EX_I)
6515     Fprintf (ofile, "\t");
6516 else Fprintf (ofile, "\n");
6517 return reason;
6518 }
6519 
6520 /* Get address routine
6521 
6522    Inputs:
6523         flag    =       type of ex/mod command (ex, iex, idep)
6524         addr    =       address to examine
6525         dptr    =       pointer to device
6526         uptr    =       pointer to unit
6527    Outputs: (sim_eval is an implicit output)
6528         return  =       error status
6529 */
6530 
6531 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6532 {
6533 int32 i;
6534 t_value mask;
6535 t_addr j, loc;
6536 size_t sz;
6537 t_stat reason = SCPE_OK;
6538 
6539 if ((dptr == NULL) || (uptr == NULL))
6540     return SCPE_IERR;
6541 mask = width_mask[dptr->dwidth];
6542 for (i = 0; i < sim_emax; i++)
6543     sim_eval[i] = 0;
6544 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
6545     if (dptr->examine != NULL) {
6546         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
6547         if (reason != SCPE_OK)
6548             break;
6549         }
6550     else {
6551         if (!(uptr->flags & UNIT_ATT))
6552             return SCPE_UNATT;
6553         if (uptr->dynflags & UNIT_NO_FIO)
6554             return SCPE_NOFNC;
6555         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
6556             reason = SCPE_NXM;
6557             break;
6558             }
6559         sz = SZ_D (dptr);
6560         loc = j / dptr->aincr;
6561         if (uptr->flags & UNIT_BUF) {
6562             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
6563             }
6564         else {
6565             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6566             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
6567             if ((feof (uptr->fileref)) &&
6568                !(uptr->flags & UNIT_FIX)) {
6569                 reason = SCPE_EOF;
6570                 break;
6571                 }
6572             else if (ferror (uptr->fileref)) {
6573                 clearerr (uptr->fileref);
6574                 reason = SCPE_IOERR;
6575                 break;
6576                 }
6577             }
6578         }
6579     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
6580     }
6581 if ((reason != SCPE_OK) && (i == 0))
6582     return reason;
6583 return SCPE_OK;
6584 }
6585 
6586 /* Deposit address routine
6587 
6588    Inputs:
6589         flag    =       type of deposit (normal/interactive)
6590         cptr    =       pointer to input string
6591         addr    =       address to examine
6592         dptr    =       pointer to device
6593         uptr    =       pointer to unit
6594         dfltinc =       value to return on cr input
6595    Outputs:
6596         return  =       if > 0, error status
6597                         if <= 0, -number of extra address units retired
6598 */
6599 
6600 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6601     UNIT *uptr, int32 dfltinc)
6602 {
6603 int32 i, count, rdx;
6604 t_addr j, loc;
6605 t_stat r, reason;
6606 t_value mask;
6607 size_t sz;
6608 char gbuf[CBUFSIZE];
6609 
6610 if (dptr == NULL)
6611     return SCPE_IERR;
6612 if (flag & EX_I) {
6613     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6614     if (sim_log)
6615         fprintf (sim_log, "%s\n", cptr? cptr: "");
6616     if (cptr == NULL)                                   /* force exit */
6617         return 1;
6618     if (*cptr == 0)                                     /* success */
6619         return dfltinc;
6620     }
6621 if (uptr->flags & UNIT_RO)                              /* read only? */
6622     return SCPE_RO;
6623 mask = width_mask[dptr->dwidth];
6624 
6625 GET_RADIX (rdx, dptr->dradix);
6626 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
6627     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
6628     if (reason != SCPE_OK)
6629         return reason;
6630     reason = dfltinc;
6631     }
6632 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
6633 
6634 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
6635     sim_eval[i] = sim_eval[i] & mask;
6636     if (dptr->deposit != NULL) {
6637         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
6638         if (r != SCPE_OK)
6639             return r;
6640         }
6641     else {
6642         if (!(uptr->flags & UNIT_ATT))
6643             return SCPE_UNATT;
6644         if (uptr->dynflags & UNIT_NO_FIO)
6645             return SCPE_NOFNC;
6646         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
6647             return SCPE_NXM;
6648         sz = SZ_D (dptr);
6649         loc = j / dptr->aincr;
6650         if (uptr->flags & UNIT_BUF) {
6651             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
6652             if (loc >= uptr->hwmark)
6653                 uptr->hwmark = (uint32) loc + 1;
6654             }
6655         else {
6656             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6657             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
6658             if (ferror (uptr->fileref)) {
6659                 clearerr (uptr->fileref);
6660                 return SCPE_IOERR;
6661                 }
6662             }
6663         }
6664     }
6665 return reason;
6666 }
6667 
6668 /* Evaluate command */
6669 
6670 t_stat eval_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6671 {
6672 if (!sim_dflt_dev)
6673   return SCPE_ARG;
6674 DEVICE *dptr = sim_dflt_dev;
6675 int32 i, rdx, a, lim;
6676 t_stat r;
6677 
6678 GET_SWITCHES (cptr);
6679 GET_RADIX (rdx, dptr->dradix);
6680 for (i = 0; i < sim_emax; i++)
6681 sim_eval[i] = 0;
6682 if (*cptr == 0)
6683     return SCPE_2FARG;
6684 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
6685     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
6686     if (r != SCPE_OK)
6687         return r;
6688     }
6689 lim = 1 - r;
6690 for (i = a = 0; a < lim; ) {
6691     sim_printf ("%d:\t", a);
6692     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6693         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6694     if (sim_log) {
6695         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6696             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6697         }
6698     sim_printf ("\n");
6699     if (r < 0)
6700         a = a + 1 - r;
6701     else a = a + dptr->aincr;
6702     i = a / dptr->aincr;
6703     }
6704 return SCPE_OK;
6705 }
6706 
6707 /* String processing routines
6708 
6709    read_line            read line
6710 
6711    Inputs:
6712         cptr    =       pointer to buffer
6713         size    =       maximum size
6714         stream  =       pointer to input stream
6715    Outputs:
6716         optr    =       pointer to first non-blank character
6717                         NULL if EOF
6718 */
6719 
6720 char *read_line (char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
6721 {
6722 return read_line_p (NULL, cptr, size, stream);
6723 }
6724 
6725 /* read_line_p          read line with prompt
6726 
6727    Inputs:
6728         prompt  =       pointer to prompt string
6729         cptr    =       pointer to buffer
6730         size    =       maximum size
6731         stream  =       pointer to input stream
6732    Outputs:
6733         optr    =       pointer to first non-blank character
6734                         NULL if EOF
6735 */
6736 
6737 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
6738 {
6739 char *tptr;
6740 
6741 if (prompt) {                                           /* interactive? */
6742 #ifdef HAVE_LINEHISTORY
6743         char *tmpc = linenoise (prompt);                /* get cmd line */
6744         if (tmpc == NULL)                               /* bad result? */
6745             cptr = NULL;
6746         else {
6747             strncpy (cptr, tmpc, size-1);               /* copy result */
6748             linenoiseHistoryAdd (tmpc);                 /* add to history */
6749             FREE (tmpc);                                /* free temp */
6750             }
6751         }
6752 #else
6753         fflush (stdout);                                /* flush output */
6754         printf ("%s", prompt);                          /* display prompt */
6755         fflush (stdout);                                /* flush output */
6756         cptr = fgets (cptr, size, stream);              /* get cmd line */
6757         }
6758 #endif /* ifdef HAVE_LINEHISTORY */
6759 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
6760 
6761 if (cptr == NULL) {
6762     clearerr (stream);                                  /* clear error */
6763     return NULL;                                        /* ignore EOF */
6764     }
6765 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
6766     if ((*tptr == '\n') || (*tptr == '\r') ||
6767         (tptr == (cptr + size - 1))) {                  /* str max length? */
6768         *tptr = 0;                                      /* terminate */
6769         break;
6770         }
6771     }
6772 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
6773     memmove (cptr, cptr + 3, strlen (cptr + 3));
6774 while (sim_isspace (*cptr))                             /* trim leading spc */
6775     cptr++;
6776 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
6777     if (sim_do_echo)                                    /* echo comments if -v */
6778         sim_printf("%s> %s\n", do_position(), cptr);
6779     *cptr = 0;
6780     }
6781 
6782 return cptr;
6783 }
6784 
6785 /* get_glyph            get next glyph (force upper case)
6786    get_glyph_nc         get next glyph (no conversion)
6787    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
6788    get_glyph_cmd        get command glyph (force upper case, extract leading !)
6789    get_glyph_gen        get next glyph (general case)
6790 
6791    Inputs:
6792         iptr        =   pointer to input string
6793         optr        =   pointer to output string
6794         mchar       =   optional end of glyph character
6795         uc          =   TRUE for convert to upper case (_gen only)
6796         quote       =   TRUE to allow quote enclosing values (_gen only)
6797         escape_char =   optional escape character within quoted strings (_gen only)
6798 
6799    Outputs
6800         result      =   pointer to next character in input string
6801 */
6802 
6803 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char)
     /* [previous][next][first][last][top][bottom][index][help] */
6804 {
6805 t_bool quoting = FALSE;
6806 t_bool escaping = FALSE;
6807 char quote_char = 0;
6808 
6809 while ((*iptr != 0) &&
6810        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
6811     if (quote) {
6812         if (quoting) {
6813             if (!escaping) {
6814                 if (*iptr == escape_char)
6815                     escaping = TRUE;
6816                 else
6817                     if (*iptr == quote_char)
6818                         quoting = FALSE;
6819                 }
6820             else
6821                 escaping = FALSE;
6822             }
6823         else {
6824             if ((*iptr == '"') || (*iptr == '\'')) {
6825                 quoting = TRUE;
6826                 quote_char = *iptr;
6827                 }
6828             }
6829         }
6830     if (sim_islower (*iptr) && uc)
6831         *optr = (char)toupper (*iptr);
6832     else *optr = *iptr;
6833     iptr++; optr++;
6834     }
6835 if (mchar && (*iptr == mchar))              /* skip input terminator */
6836     iptr++;
6837 *optr = 0;                                  /* terminate result string */
6838 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
6839     iptr++;
6840 return iptr;
6841 }
6842 
6843 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
6844 {
6845 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
6846 }
6847 
6848 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
6849 {
6850 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
6851 }
6852 
6853 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
6854 {
6855 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
6856 }
6857 
6858 CONST char *get_glyph_cmd (const char *iptr, char *optr)
     /* [previous][next][first][last][top][bottom][index][help] */
6859 {
6860 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
6861 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
6862     strcpy (optr, "!");                     /* return ! as command glyph */
6863     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
6864     }
6865 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
6866 }
6867 
6868 /* Trim trailing spaces from a string
6869 
6870     Inputs:
6871         cptr    =       pointer to string
6872     Outputs:
6873         cptr    =       pointer to string
6874 */
6875 
6876 char *sim_trim_endspc (char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6877 {
6878 char *tptr;
6879 
6880 tptr = cptr + strlen (cptr);
6881 while ((--tptr >= cptr) && sim_isspace (*tptr))
6882     *tptr = 0;
6883 return cptr;
6884 }
6885 
6886 int sim_isspace (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6887 {
6888 return (c & 0x80) ? 0 : isspace (c);
6889 }
6890 
6891 int sim_islower (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6892 {
6893 return (c & 0x80) ? 0 : islower (c);
6894 }
6895 
6896 int sim_isalpha (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6897 {
6898 return (c & 0x80) ? 0 : isalpha (c);
6899 }
6900 
6901 int sim_isprint (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6902 {
6903 return (c & 0x80) ? 0 : isprint (c);
6904 }
6905 
6906 int sim_isdigit (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6907 {
6908 return (c & 0x80) ? 0 : isdigit (c);
6909 }
6910 
6911 int sim_isgraph (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6912 {
6913 return (c & 0x80) ? 0 : isgraph (c);
6914 }
6915 
6916 int sim_isalnum (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
6917 {
6918 return (c & 0x80) ? 0 : isalnum (c);
6919 }
6920 
6921 /* get_uint             unsigned number
6922 
6923    Inputs:
6924         cptr    =       pointer to input string
6925         radix   =       input radix
6926         max     =       maximum acceptable value
6927         *status =       pointer to error status
6928    Outputs:
6929         val     =       value
6930 */
6931 
6932 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
6933 {
6934 t_value val;
6935 CONST char *tptr;
6936 
6937 *status = SCPE_OK;
6938 val = strtotv ((CONST char *)cptr, &tptr, radix);
6939 if ((cptr == tptr) || (val > max))
6940     *status = SCPE_ARG;
6941 else {
6942     while (sim_isspace (*tptr)) tptr++;
6943     if (*tptr != 0)
6944         *status = SCPE_ARG;
6945     }
6946 return val;
6947 }
6948 
6949 /* get_range            range specification
6950 
6951    Inputs:
6952         dptr    =       pointer to device (NULL if none)
6953         cptr    =       pointer to input string
6954         *lo     =       pointer to low result
6955         *hi     =       pointer to high result
6956         aradix  =       radix
6957         max     =       default high value
6958         term    =       terminating character, 0 if none
6959    Outputs:
6960         tptr    =       input pointer after processing
6961                         NULL if error
6962 */
6963 
6964 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
     /* [previous][next][first][last][top][bottom][index][help] */
6965     uint32 rdx, t_addr max, char term)
6966 {
6967 CONST char *tptr;
6968 
6969 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
6970     tptr = cptr + strlen ("ALL");
6971     *lo = 0;
6972     *hi = max;
6973     }
6974 else {
6975     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
6976         ((cptr[1] == '\0') ||
6977          (cptr[1] == '-')  ||
6978          (cptr[1] == ':')  ||
6979          (cptr[1] == '/'))) {
6980         tptr = cptr + strlen (".");
6981         *lo = *hi = sim_last_addr;
6982         }
6983     else {
6984         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
6985             tptr = cptr + strlen ("$");
6986             *hi = *lo = (t_addr)sim_last_val;
6987             }
6988         else {
6989             if (dptr && sim_vm_parse_addr)                      /* get low */
6990                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
6991             else
6992                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
6993             if (cptr == tptr)                                   /* error? */
6994                     return NULL;
6995             }
6996         }
6997     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
6998         cptr = tptr + 1;
6999         if (dptr && sim_vm_parse_addr)                  /* get high */
7000             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7001         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7002         if (cptr == tptr)
7003             return NULL;
7004         if (*lo > *hi)
7005             return NULL;
7006         }
7007     else if (*tptr == '/') {                            /* relative? */
7008         cptr = tptr + 1;
7009         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7010         if ((cptr == tptr) || (*hi == 0))
7011             return NULL;
7012         *hi = *lo + *hi - 1;
7013         }
7014     else *hi = *lo;
7015     }
7016 sim_last_addr = *hi;
7017 if (term && (*tptr++ != term))
7018     return NULL;
7019 return tptr;
7020 }
7021 
7022 /* sim_decode_quoted_string
7023 
7024    Inputs:
7025         iptr        =   pointer to input string
7026         optr        =   pointer to output buffer
7027                         the output buffer must be allocated by the caller
7028                         and to avoid overrunat it must be at least as big
7029                         as the input string.
7030 
7031    Outputs
7032         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7033         osize       =   size of the data in the optr buffer
7034 
7035    The input string must be quoted.  Quotes may be either single or
7036    double but the opening anc closing quote characters must match.
7037    Within quotes C style character escapes are allowed.
7038 */
7039 
7040 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
     /* [previous][next][first][last][top][bottom][index][help] */
7041 {
7042 char quote_char;
7043 uint8 *ostart = optr;
7044 
7045 *osize = 0;
7046 if ((strlen(iptr) == 1) ||
7047     (iptr[0] != iptr[strlen(iptr)-1]) ||
7048     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7049     return SCPE_ARG;            /* String must be quote delimited */
7050 quote_char = *iptr++;           /* Save quote character */
7051 while (iptr[1]) {               /* Skip trailing quote */
7052     if (*iptr != '\\') {
7053         if (*iptr == quote_char)
7054             return SCPE_ARG;    /* Embedded quotes must be escaped */
7055         *(optr++) = (uint8)(*(iptr++));
7056         continue;
7057         }
7058     ++iptr; /* Skip backslash */
7059     switch (*iptr) {
7060         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7061             *(optr++) = 13; ++iptr;
7062             break;
7063         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7064             *(optr++) = 10; ++iptr;
7065             break;
7066         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7067             *(optr++) = 12; ++iptr;
7068             break;
7069         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7070             *(optr++) = 9; ++iptr;
7071             break;
7072         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7073             *(optr++) = 11; ++iptr;
7074             break;
7075         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7076             *(optr++) = 8; ++iptr;
7077             break;
7078         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7079             *(optr++) = 92; ++iptr;
7080             break;
7081         case 'e':   /* ASCII Escape character (Decimal value 27) */
7082             *(optr++) = 27; ++iptr;
7083             break;
7084         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7085             *(optr++) = 39; ++iptr;
7086             break;
7087         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7088             *(optr++) = 34; ++iptr;
7089             break;
7090         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7091             *(optr++) = 63; ++iptr;
7092             break;
7093         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7094             *optr = *(iptr++) - '0';
7095             if ((*iptr >= '0') && (*iptr <= '7'))
7096                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7097             if ((*iptr >= '0') && (*iptr <= '7'))
7098                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7099             ++optr;
7100             break;
7101         case 'x':
7102             if (1) {
7103                 static const char *hex_digits = "0123456789ABCDEF";
7104                 const char *c;
7105 
7106                 ++iptr;
7107                 *optr = 0;
7108                 c = strchr (hex_digits, toupper(*iptr));
7109                 if (c) {
7110                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7111                     ++iptr;
7112                     }
7113                 c = strchr (hex_digits, toupper(*iptr));
7114                 if (c) {
7115                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7116                     ++iptr;
7117                     }
7118                 ++optr;
7119                 }
7120             break;
7121         default:
7122             return SCPE_ARG;    /* Invalid escape */
7123         }
7124     }
7125 *optr = '\0';
7126 *osize = (uint32)(optr-ostart);
7127 return SCPE_OK;
7128 }
7129 
7130 /* sim_encode_quoted_string
7131 
7132    Inputs:
7133         iptr        =   pointer to input buffer
7134         size        =   number of bytes of data in the buffer
7135 
7136    Outputs
7137         optr        =   pointer to output buffer
7138                         the output buffer must be freed by the caller
7139 
7140    The input data will be encoded into a simply printable form.
7141 */
7142 
7143 char *sim_encode_quoted_string (const uint8 *iptr, uint32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
7144 {
7145 uint32 i;
7146 t_bool double_quote_found = FALSE;
7147 t_bool single_quote_found = FALSE;
7148 char quote = '"';
7149 char *tptr, *optr;
7150 
7151 optr = (char *)malloc (4*size + 3);
7152 if (optr == NULL)
7153     return NULL;
7154 tptr = optr;
7155 for (i=0; i<size; i++)
7156     switch ((char)iptr[i]) {
7157         case '"':
7158             double_quote_found = TRUE;
7159             break;
7160         case '\'':
7161             single_quote_found = TRUE;
7162             break;
7163         }
7164 if (double_quote_found && (!single_quote_found))
7165     quote = '\'';
7166 *tptr++ = quote;
7167 while (size--) {
7168     switch (*iptr) {
7169         case '\r':
7170             *tptr++ = '\\'; *tptr++ = 'r'; break;
7171         case '\n':
7172             *tptr++ = '\\'; *tptr++ = 'n'; break;
7173         case '\f':
7174             *tptr++ = '\\'; *tptr++ = 'f'; break;
7175         case '\t':
7176             *tptr++ = '\\'; *tptr++ = 't'; break;
7177         case '\v':
7178             *tptr++ = '\\'; *tptr++ = 'v'; break;
7179         case '\b':
7180             *tptr++ = '\\'; *tptr++ = 'b'; break;
7181         case '\\':
7182             *tptr++ = '\\'; *tptr++ = '\\'; break;
7183         case '"':
7184         case '\'':
7185             if (quote == *iptr)
7186                 *tptr++ = '\\';
7187         /*FALLTHRU*/ /* fall through */ /* fallthrough */
7188         default:
7189             if (sim_isprint (*iptr))
7190                 *tptr++ = *iptr;
7191             else {
7192                 sprintf (tptr, "\\%03o", *iptr);
7193                 tptr += 4;
7194                 }
7195             break;
7196         }
7197     ++iptr;
7198     }
7199 *tptr++ = quote;
7200 *tptr++ = '\0';
7201 return optr;
7202 }
7203 
7204 void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
7205 {
7206 char *string;
7207 
7208 string = sim_encode_quoted_string (buf, size);
7209 fprintf (st, "%s", string);
7210 FREE (string);
7211 }
7212 
7213 /* Find_device          find device matching input string
7214 
7215    Inputs:
7216         cptr    =       pointer to input string
7217    Outputs:
7218         result  =       pointer to device
7219 */
7220 
7221 DEVICE *find_dev (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7222 {
7223 int32 i;
7224 DEVICE *dptr;
7225 
7226 if (cptr == NULL)
7227     return NULL;
7228 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7229     if ((strcmp (cptr, dptr->name) == 0) ||
7230         (dptr->lname &&
7231         (strcmp (cptr, dptr->lname) == 0)))
7232         return dptr;
7233     }
7234 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7235     if ((strcmp (cptr, dptr->name) == 0) ||
7236         (dptr->lname &&
7237         (strcmp (cptr, dptr->lname) == 0)))
7238         return dptr;
7239     }
7240 return NULL;
7241 }
7242 
7243 /* Find_unit            find unit matching input string
7244 
7245    Inputs:
7246         cptr    =       pointer to input string
7247         uptr    =       pointer to unit pointer
7248    Outputs:
7249         result  =       pointer to device (null if no dev)
7250         *iptr   =       pointer to unit (null if nx unit)
7251 
7252 */
7253 
7254 DEVICE *find_unit (const char *cptr, UNIT **uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7255 {
7256 uint32 i, u;
7257 const char *nptr;
7258 const char *tptr;
7259 t_stat r;
7260 DEVICE *dptr;
7261 
7262 if (uptr == NULL)                                       /* arg error? */
7263     return NULL;
7264 *uptr = NULL;
7265 if ((dptr = find_dev (cptr))) {                         /* exact match? */
7266     if (qdisable (dptr))                                /* disabled? */
7267         return NULL;
7268     *uptr = dptr->units;                                /* unit 0 */
7269     return dptr;
7270     }
7271 
7272 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
7273     if (qdisable (dptr))                                /* device disabled? */
7274         continue;
7275     if (dptr->numunits && /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/ /* any units? */
7276         (((nptr = dptr->name) &&
7277           (strncmp (cptr, nptr, strlen (nptr)) == 0)) || /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7278          ((nptr = dptr->lname) &&
7279           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7280         tptr = cptr + strlen (nptr);
7281         if (sim_isdigit (*tptr)) {
7282             if (qdisable (dptr))                        /* disabled? */
7283                 return NULL;
7284             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7285             if (r != SCPE_OK)                           /* error? */
7286                 *uptr = NULL;
7287             else
7288                 *uptr = dptr->units + u;
7289             return dptr;
7290             }
7291         }
7292     for (u = 0; u < dptr->numunits; u++) {
7293         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7294             *uptr = &dptr->units[u];
7295             return dptr;
7296             }
7297         }
7298     }
7299 return NULL;
7300 }
7301 
7302 /* sim_register_internal_device   Add device to internal device list
7303 
7304    Inputs:
7305         dptr    =       pointer to device
7306 */
7307 
7308 t_stat sim_register_internal_device (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7309 {
7310 uint32 i;
7311 
7312 for (i = 0; (sim_devices[i] != NULL); i++)
7313     if (sim_devices[i] == dptr)
7314         return SCPE_OK;
7315 for (i = 0; i < sim_internal_device_count; i++)
7316     if (sim_internal_devices[i] == dptr)
7317         return SCPE_OK;
7318 ++sim_internal_device_count;
7319 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7320 if (!sim_internal_devices)
7321   {
7322     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7323              __func__, __FILE__, __LINE__);
7324 #if defined(USE_BACKTRACE)
7325 # ifdef SIGUSR2
7326     (void)raise(SIGUSR2);
7327     /*NOTREACHED*/ /* unreachable */
7328 # endif /* ifdef SIGUSR2 */
7329 #endif /* if defined(USE_BACKTRACE) */
7330     abort();
7331   }
7332 sim_internal_devices[sim_internal_device_count-1] = dptr;
7333 sim_internal_devices[sim_internal_device_count] = NULL;
7334 return SCPE_OK;
7335 }
7336 
7337 /* Find_dev_from_unit   find device for unit
7338 
7339    Inputs:
7340         uptr    =       pointer to unit
7341    Outputs:
7342         result  =       pointer to device
7343 */
7344 
7345 DEVICE *find_dev_from_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7346 {
7347 DEVICE *dptr;
7348 uint32 i, j;
7349 
7350 if (uptr == NULL)
7351     return NULL;
7352 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7353     for (j = 0; j < dptr->numunits; j++) {
7354         if (uptr == (dptr->units + j))
7355             return dptr;
7356         }
7357     }
7358 for (i = 0; i<sim_internal_device_count; i++) {
7359     dptr = sim_internal_devices[i];
7360     for (j = 0; j < dptr->numunits; j++) {
7361         if (uptr == (dptr->units + j))
7362             return dptr;
7363         }
7364     }
7365 return NULL;
7366 }
7367 
7368 /* Test for disabled device */
7369 
7370 t_bool qdisable (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7371 {
7372 return (dptr->flags & DEV_DIS? TRUE: FALSE);
7373 }
7374 
7375 /* find_reg_glob        find globally unique register
7376 
7377    Inputs:
7378         cptr    =       pointer to input string
7379         optr    =       pointer to output pointer (can be null)
7380         gdptr   =       pointer to global device
7381    Outputs:
7382         result  =       pointer to register, NULL if error
7383         *optr   =       pointer to next character in input string
7384         *gdptr  =       pointer to device where found
7385 */
7386 
7387 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7388 {
7389 int32 i;
7390 DEVICE *dptr;
7391 REG *rptr, *srptr = NULL;
7392 
7393 *gdptr = NULL;
7394 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
7395     if (dptr->flags & DEV_DIS)                          /* skip disabled */
7396         continue;
7397     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
7398         if (srptr)                                      /* ambig? err */
7399             return NULL;
7400         srptr = rptr;                                   /* save reg */
7401         *gdptr = dptr;                                  /* save unit */
7402         }
7403     }
7404 return srptr;
7405 }
7406 
7407 /* find_reg             find register matching input string
7408 
7409    Inputs:
7410         cptr    =       pointer to input string
7411         optr    =       pointer to output pointer (can be null)
7412         dptr    =       pointer to device
7413    Outputs:
7414         result  =       pointer to register, NULL if error
7415         *optr   =       pointer to next character in input string
7416 */
7417 
7418 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7419 {
7420 CONST char *tptr;
7421 REG *rptr;
7422 size_t slnt;
7423 
7424 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
7425     return NULL;
7426 tptr = cptr;
7427 do {
7428     tptr++;
7429     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
7430 slnt = tptr - cptr;
7431 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
7432     if ((slnt == strlen (rptr->name)) &&
7433         (strncmp (cptr, rptr->name, slnt) == 0)) {
7434         if (optr != NULL)
7435             *optr = tptr;
7436         return rptr;
7437         }
7438     }
7439 return NULL;
7440 }
7441 
7442 /* get_switches         get switches from input string
7443 
7444    Inputs:
7445         cptr    =       pointer to input string
7446    Outputs:
7447         sw      =       switch bit mask
7448                         0 if no switches, -1 if error
7449 */
7450 
7451 int32 get_switches (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7452 {
7453 int32 sw;
7454 
7455 if (*cptr != '-')
7456     return 0;
7457 sw = 0;
7458 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
7459     if (sim_isalpha (*cptr) == 0)
7460         return -1;
7461     sw = sw | SWMASK (toupper (*cptr));
7462     }
7463 return sw;
7464 }
7465 
7466 /* get_sim_sw           accumulate sim_switches
7467 
7468    Inputs:
7469         cptr    =       pointer to input string
7470    Outputs:
7471         ptr     =       pointer to first non-string glyph
7472                         NULL if error
7473 */
7474 
7475 CONST char *get_sim_sw (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7476 {
7477 int32 lsw;
7478 char gbuf[CBUFSIZE];
7479 
7480 while (*cptr == '-') {                                  /* while switches */
7481     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
7482     lsw = get_switches (gbuf);                          /* parse */
7483     if (lsw <= 0)                                       /* invalid? */
7484         return NULL;
7485     sim_switches = sim_switches | lsw;                  /* accumulate */
7486     }
7487 return cptr;
7488 }
7489 
7490 /* get_sim_opt          get simulator command options
7491 
7492    Inputs:
7493         opt     =       command options
7494         cptr    =       pointer to input string
7495    Outputs:
7496         ptr     =       pointer to next glyph, NULL if error
7497         *stat   =       error status
7498 */
7499 
7500 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
     /* [previous][next][first][last][top][bottom][index][help] */
7501 {
7502 int32 t;
7503 char gbuf[CBUFSIZE];
7504 CONST char *svptr;
7505 DEVICE *tdptr;
7506 UNIT *tuptr;
7507 
7508 sim_switches = 0;                                       /* no switches */
7509 sim_ofile = NULL;                                       /* no output file */
7510 sim_schrptr = NULL;                                     /* no search */
7511 sim_schaptr = NULL;                                     /* no search */
7512 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
7513 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
7514 sim_stabr.count = 1;
7515 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
7516 if (!sim_stabr.mask)
7517   {
7518     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7519              __func__, __FILE__, __LINE__);
7520 #if defined(USE_BACKTRACE)
7521 # ifdef SIGUSR2
7522     (void)raise(SIGUSR2);
7523     /*NOTREACHED*/ /* unreachable */
7524 # endif /* ifdef SIGUSR2 */
7525 #endif /* if defined(USE_BACKTRACE) */
7526     abort();
7527   }
7528 memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
7529 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
7530 if (!sim_stabr.comp)
7531   {
7532     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7533              __func__, __FILE__, __LINE__);
7534 #if defined(USE_BACKTRACE)
7535 # ifdef SIGUSR2
7536     (void)raise(SIGUSR2);
7537     /*NOTREACHED*/ /* unreachable */
7538 # endif /* ifdef SIGUSR2 */
7539 #endif /* if defined(USE_BACKTRACE) */
7540     abort();
7541   }
7542 memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
7543 sim_staba.count = sim_emax;
7544 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
7545 if (!sim_staba.mask)
7546   {
7547     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7548              __func__, __FILE__, __LINE__);
7549 #if defined(USE_BACKTRACE)
7550 # ifdef SIGUSR2
7551     (void)raise(SIGUSR2);
7552     /*NOTREACHED*/ /* unreachable */
7553 # endif /* ifdef SIGUSR2 */
7554 #endif /* if defined(USE_BACKTRACE) */
7555     abort();
7556   }
7557 memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
7558 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
7559 if (!sim_staba.comp)
7560   {
7561     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7562              __func__, __FILE__, __LINE__);
7563 #if defined(USE_BACKTRACE)
7564 # ifdef SIGUSR2
7565     (void)raise(SIGUSR2);
7566     /*NOTREACHED*/ /* unreachable */
7567 # endif /* ifdef SIGUSR2 */
7568 #endif /* if defined(USE_BACKTRACE) */
7569     abort();
7570   }
7571 memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
7572 if (! sim_dflt_dev)
7573   return NULL;
7574 sim_dfdev = sim_dflt_dev;
7575 sim_dfunit = sim_dfdev->units;
7576 sim_opt_out = 0;                                        /* no options yet */
7577 *st = SCPE_OK;
7578 while (*cptr) {                                         /* loop through modifiers */
7579     svptr = cptr;                                       /* save current position */
7580     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
7581         if (sim_ofile) {                                /* already got one? */
7582             fclose (sim_ofile);                         /* one per customer */
7583             *st = SCPE_ARG;
7584             return NULL;
7585             }
7586         cptr = get_glyph (cptr + 1, gbuf, 0);
7587         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
7588         if (sim_ofile == NULL) {                        /* open failed? */
7589             *st = SCPE_OPENERR;
7590             return NULL;
7591             }
7592         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
7593         continue;
7594         }
7595     cptr = get_glyph (cptr, gbuf, 0);
7596     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
7597         if (t < 0) {                                    /* err if bad switch */
7598             *st = SCPE_INVSW;
7599             return NULL;
7600             }
7601         sim_switches = sim_switches | t;                /* or in new switches */
7602         }
7603     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
7604         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
7605         sim_schrptr = &sim_stabr;                       /* set search */
7606         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
7607         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
7608         }
7609     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
7610         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
7611         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
7612         (tuptr != NULL)) {
7613         sim_dfdev = tdptr;                              /* set as default */
7614         sim_dfunit = tuptr;
7615         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
7616         }
7617     else return svptr;                                  /* not rec, break out */
7618     }
7619 return cptr;
7620 }
7621 
7622 /* put_switches         put switches into string
7623 
7624    Inputs:
7625         buf     =       pointer to string buffer
7626         bufsize =       size of string buffer
7627         sw      =       switch bit mask
7628    Outputs:
7629         buf     =       buffer with switches converted to text
7630 */
7631 
7632 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
7633 {
7634 char *optr = buf;
7635 int32 bit;
7636 
7637 memset (buf, 0, bufsize);
7638 if ((sw == 0) || (bufsize < 3))
7639     return buf;
7640 --bufsize;                          /* leave room for terminating NUL */
7641 *optr++ = '-';
7642 for (bit=0; bit <= ('Z'-'A'); bit++)
7643     if (sw & (1 << bit))
7644         if ((size_t)(optr - buf) < bufsize)
7645             *optr++ = 'A' + bit;
7646 return buf;
7647 }
7648 
7649 /* Get register search specification
7650 
7651    Inputs:
7652         cptr    =       pointer to input string
7653         radix   =       radix for numbers
7654         schptr =        pointer to search table
7655    Outputs:
7656         return =        NULL if error
7657                         schptr if valid search specification
7658 */
7659 
7660 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7661 {
7662 int32 c, logop, cmpop;
7663 t_value logval, cmpval;
7664 const char *sptr;
7665 CONST char *tptr;
7666 const char logstr[] = "|&^", cmpstr[] = "=!><";
7667 
7668 logval = cmpval = 0;
7669 if (*cptr == 0)                                         /* check for clause */
7670     return NULL;
7671 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7672 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7673     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7674         logop = (int32)(sptr - logstr);
7675         logval = strtotv (cptr, &tptr, radix);
7676         if (cptr == tptr)
7677             return NULL;
7678         cptr = tptr;
7679         }
7680     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7681         cmpop = (int32)(sptr - cmpstr);
7682         if (*cptr == '=') {
7683             cmpop = cmpop + strlen (cmpstr);
7684             cptr++;
7685             }
7686         cmpval = strtotv (cptr, &tptr, radix);
7687         if (cptr == tptr)
7688             return NULL;
7689         cptr = tptr;
7690         }
7691     else return NULL;
7692     }                                                   /* end for */
7693 if (schptr->count != 1) {
7694     FREE (schptr->mask);
7695     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7696     FREE (schptr->comp);
7697     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7698     }
7699 if (logop >= 0) {
7700     schptr->logic = logop;
7701     schptr->mask[0] = logval;
7702     }
7703 if (cmpop >= 0) {
7704     schptr->boolop = cmpop;
7705     schptr->comp[0] = cmpval;
7706     }
7707 schptr->count = 1;
7708 return schptr;
7709 }
7710 
7711 /* Get memory search specification
7712 
7713    Inputs:
7714         cptr    =       pointer to input string
7715         radix   =       radix for numbers
7716         schptr =        pointer to search table
7717    Outputs:
7718         return =        NULL if error
7719                         schptr if valid search specification
7720 */
7721 
7722 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7723 {
7724 int32 c, logop, cmpop;
7725 t_value *logval, *cmpval;
7726 t_stat reason = SCPE_OK;
7727 CONST char *ocptr = cptr;
7728 const char *sptr;
7729 char gbuf[CBUFSIZE];
7730 const char logstr[] = "|&^", cmpstr[] = "=!><";
7731 
7732 if (*cptr == 0)                                         /* check for clause */
7733     return NULL;
7734 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
7735 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
7736 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7737 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7738     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7739         logop = (int32)(sptr - logstr);
7740         cptr = get_glyph (cptr, gbuf, 0);
7741         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
7742         if (reason > 0) {
7743             FREE (logval);
7744             FREE (cmpval);
7745             return get_rsearch (ocptr, radix, schptr);
7746             }
7747         }
7748     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7749         cmpop = (int32)(sptr - cmpstr);
7750         if (*cptr == '=') {
7751             cmpop = cmpop + strlen (cmpstr);
7752             cptr++;
7753             }
7754         cptr = get_glyph (cptr, gbuf, 0);
7755         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
7756         if (reason > 0) {
7757             FREE (logval);
7758             FREE (cmpval);
7759             return get_rsearch (ocptr, radix, schptr);
7760             }
7761         }
7762     else {
7763         FREE (logval);
7764         FREE (cmpval);
7765         return NULL;
7766         }
7767     }                                                   /* end for */
7768 if (schptr->count != (uint32)(1 - reason)) {
7769     schptr->count = 1 - reason;
7770     FREE (schptr->mask);
7771     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7772     FREE (schptr->comp);
7773     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7774     }
7775 if (logop >= 0) {
7776     schptr->logic = logop;
7777     FREE (schptr->mask);
7778     schptr->mask = logval;
7779     }
7780 else {
7781     FREE (logval);
7782     }
7783 if (cmpop >= 0) {
7784     schptr->boolop = cmpop;
7785     FREE (schptr->comp);
7786     schptr->comp = cmpval;
7787     }
7788 else {
7789     FREE (cmpval);
7790     }
7791 return schptr;
7792 }
7793 
7794 /* Test value against search specification
7795 
7796    Inputs:
7797         val    =        value list to test
7798         schptr =        pointer to search table
7799    Outputs:
7800         return =        1 if value passes search criteria, 0 if not
7801 */
7802 
7803 int32 test_search (t_value *values, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7804 {
7805 t_value *val = NULL;
7806 int32 i, updown;
7807 int32 ret = 0;
7808 
7809 if (schptr == NULL)
7810     return ret;
7811 
7812 val = (t_value *)malloc (schptr->count * sizeof (*values));
7813 if (!val)
7814   {
7815     fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7816             __func__, __FILE__, __LINE__);
7817 #if defined(USE_BACKTRACE)
7818 # ifdef SIGUSR2
7819     (void)raise(SIGUSR2);
7820     /*NOTREACHED*/ /* unreachable */
7821 # endif /* ifdef SIGUSR2 */
7822 #endif /* if defined(USE_BACKTRACE) */
7823     abort();
7824   }
7825 for (i=0; i<(int32)schptr->count; i++) {
7826     val[i] = values[i];
7827     switch (schptr->logic) {                            /* case on logical */
7828 
7829         case SCH_OR:
7830             val[i] = val[i] | schptr->mask[i];
7831             break;
7832 
7833         case SCH_AND:
7834             val[i] = val[i] & schptr->mask[i];
7835             break;
7836 
7837         case SCH_XOR:
7838             val[i] = val[i] ^ schptr->mask[i];
7839             break;
7840             }
7841     }
7842 
7843 ret = 1;
7844 if (1) {    /* Little Endian VM */
7845     updown = -1;
7846     i=schptr->count-1;
7847     }
7848 else {      /* Big Endian VM */
7849     updown = 1;
7850     i=0;
7851     }
7852 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
7853     switch (schptr->boolop) {                           /* case on comparison */
7854 
7855         case SCH_E: case SCH_EE:
7856             if (val[i] != schptr->comp[i])
7857                 ret = 0;
7858             break;
7859 
7860         case SCH_N: case SCH_NE:
7861             if (val[i] == schptr->comp[i])
7862                 ret = 0;
7863             break;
7864 
7865         case SCH_G:
7866             if (val[i] <= schptr->comp[i])
7867                 ret = 0;
7868             break;
7869 
7870         case SCH_GE:
7871             if (val[i] < schptr->comp[i])
7872                 ret = 0;
7873             break;
7874 
7875         case SCH_L:
7876             if (val[i] >= schptr->comp[i])
7877                 ret = 0;
7878             break;
7879 
7880         case SCH_LE:
7881             if (val[i] > schptr->comp[i])
7882                 ret = 0;
7883             break;
7884         }
7885     }
7886 FREE (val);
7887 return ret;
7888 }
7889 
7890 /* Radix independent input/output package
7891 
7892    strtotv - general radix input routine
7893 
7894    Inputs:
7895         inptr   =       string to convert
7896         endptr  =       pointer to first unconverted character
7897         radix   =       radix for input
7898    Outputs:
7899         value   =       converted value
7900 
7901    On an error, the endptr will equal the inptr.
7902 */
7903 
7904 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
     /* [previous][next][first][last][top][bottom][index][help] */
7905 {
7906 int32 nodigit;
7907 t_value val;
7908 uint32 c, digit;
7909 
7910 *endptr = inptr;                                        /* assume fails */
7911 if ((radix < 2) || (radix > 36))
7912     return 0;
7913 while (sim_isspace (*inptr))                            /* bypass white space */
7914     inptr++;
7915 val = 0;
7916 nodigit = 1;
7917 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
7918     if (sim_islower (c))
7919         c = toupper (c);
7920     if (sim_isdigit (c))                                /* digit? */
7921         digit = c - (uint32) '0';
7922     else if (radix <= 10)                               /* stop if not expected */
7923         break;
7924     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
7925     if (digit >= radix)                                 /* valid in radix? */
7926         return 0;
7927     val = (val * radix) + digit;                        /* add to value */
7928     nodigit = 0;
7929     }
7930 if (nodigit)                                            /* no digits? */
7931     return 0;
7932 *endptr = inptr;                                        /* result pointer */
7933 return val;
7934 }
7935 
7936 /* fprint_val - general radix printing routine
7937 
7938    Inputs:
7939         stream  =       stream designator
7940         val     =       value to print
7941         radix   =       radix to print
7942         width   =       width to print
7943         format  =       leading zeroes format
7944    Outputs:
7945         status  =       error status
7946         if stream is NULL, returns length of output that would
7947         have been generated.
7948 */
7949 
7950 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
7951     uint32 width, uint32 format)
7952 {
7953 #define MAX_WIDTH ((int) ((CHAR_BIT * sizeof (t_value) * 4 + 3)/3))
7954 t_value owtest, wtest;
7955 int32 d, digit, ndigits, commas = 0;
7956 char dbuf[MAX_WIDTH + 1];
7957 
7958 for (d = 0; d < MAX_WIDTH; d++)
7959     dbuf[d] = (format == PV_RZRO)? '0': ' ';
7960 dbuf[MAX_WIDTH] = 0;
7961 d = MAX_WIDTH;
7962 do {
7963     d = d - 1;
7964     digit = (int32) (val % radix);
7965     val = val / radix;
7966     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
7967     } while ((d > 0) && (val != 0));
7968 
7969 switch (format) {
7970     case PV_LEFT:
7971         break;
7972     case PV_RCOMMA:
7973         for (digit = 0; digit < MAX_WIDTH; digit++)
7974             if (dbuf[digit] != ' ')
7975                 break;
7976         ndigits = MAX_WIDTH - digit;
7977         commas = (ndigits - 1)/3;
7978         for (digit=0; digit<ndigits-3; digit++)
7979             dbuf[MAX_WIDTH + (digit - ndigits) - (ndigits - digit - 1)/3] = dbuf[MAX_WIDTH + (digit - ndigits)];
7980         for (digit=1; digit<=commas; digit++)
7981             dbuf[MAX_WIDTH - (digit * 4)] = ',';
7982         d = d - commas;
7983         if (width > MAX_WIDTH) {
7984             if (!buffer)
7985                 return width;
7986             sprintf (buffer, "%*s", -((int)width), dbuf);
7987             return SCPE_OK;
7988             }
7989         else
7990             if (width > 0)
7991                 d = MAX_WIDTH - width;
7992         break;
7993     case PV_RZRO:
7994     case PV_RSPC:
7995         wtest = owtest = radix;
7996         ndigits = 1;
7997         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
7998             owtest = wtest;
7999             wtest = wtest * radix;
8000             ndigits = ndigits + 1;
8001             }
8002         if ((MAX_WIDTH - (ndigits + commas)) < d)
8003             d = MAX_WIDTH - (ndigits + commas);
8004         break;
8005     }
8006 if (!buffer)
8007     return strlen(dbuf+d);
8008 *buffer = '\0';
8009 if (width < strlen(dbuf+d))
8010     return SCPE_IOERR;
8011 strcpy(buffer, dbuf+d);
8012 return SCPE_OK;
8013 }
8014 
8015 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8016     uint32 width, uint32 format)
8017 {
8018 char dbuf[MAX_WIDTH + 1];
8019 
8020 if (!stream)
8021     return sprint_val (NULL, val, radix, width, format);
8022 if (width > MAX_WIDTH)
8023     width = MAX_WIDTH;
8024 sprint_val (dbuf, val, radix, width, format);
8025 if (Fprintf (stream, "%s", dbuf) < 0)
8026     return SCPE_IOERR;
8027 return SCPE_OK;
8028 }
8029 
8030 const char *sim_fmt_secs (double seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
8031 {
8032 static char buf[60];
8033 char frac[16] = "";
8034 const char *sign = "";
8035 double val = seconds;
8036 double days, hours, mins, secs, msecs, usecs;
8037 
8038 if (val == 0.0)
8039     return "";
8040 if (val < 0.0) {
8041     sign = "-";
8042     val = -val;
8043     }
8044 days = floor (val / (24.0*60.0*60.0));
8045 val -= (days * 24.0*60.0*60.0);
8046 hours = floor (val / (60.0*60.0));
8047 val -= (hours * 60.0 * 60.0);
8048 mins = floor (val / 60.0);
8049 val -= (mins * 60.0);
8050 secs = floor (val);
8051 val -= secs;
8052 val *= 1000.0;
8053 msecs = floor (val);
8054 val -= msecs;
8055 val *= 1000.0;
8056 usecs = floor (val+0.5);
8057 if (usecs == 1000.0) {
8058     usecs = 0.0;
8059     msecs += 1;
8060     }
8061 if ((msecs > 0.0) || (usecs > 0.0)) {
8062     sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8063     while (frac[strlen (frac) - 1] == '0') //-V557
8064         frac[strlen (frac) - 1] = '\0'; //-V557
8065     if (strlen (frac) == 1)
8066         frac[0] = '\0';
8067     }
8068 if (days > 0)
8069     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" : "");
8070 else
8071     if (hours > 0)
8072         sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour", sign, hours, mins, secs, frac);
8073     else
8074         if (mins > 0)
8075             sprintf (buf, "%s%.0f:%02.0f%s minute", sign, mins, secs, frac);
8076         else
8077             if (secs > 0)
8078                 sprintf (buf, "%s%.0f%s second", sign, secs, frac);
8079             else
8080                 if (msecs > 0) {
8081                     if (usecs > 0)
8082                         sprintf (buf, "%s%.0f.%s msec", sign, msecs, frac+4);
8083                     else
8084                         sprintf (buf, "%s%.0f msec", sign, msecs);
8085                     }
8086                 else
8087                     sprintf (buf, "%s%.0f usec", sign, usecs);
8088 if (0 != strncmp ("1 ", buf, 2))
8089     strcpy (&buf[strlen (buf)], "s");
8090 return buf;
8091 }
8092 
8093 /*
8094  * Event queue package
8095  *
8096  *      sim_activate            add entry to event queue
8097  *      sim_activate_abs        add entry to event queue even if event already scheduled
8098  *      sim_activate_after      add entry to event queue after a specified amount of wall time
8099  *      sim_cancel              remove entry from event queue
8100  *      sim_process_event       process entries on event queue
8101  *      sim_is_active           see if entry is on event queue
8102  *      sim_activate_time       return time until activation
8103  *      sim_atime               return absolute time for an entry
8104  *      sim_gtime               return global time
8105  *      sim_qcount              return event queue entry count
8106  */
8107 
8108 t_stat sim_process_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8109 {
8110 UNIT *uptr;
8111 t_stat reason;
8112 
8113 if (stop_cpu)                                           /* stop CPU? */
8114   {
8115 #ifdef WIN_STDIO
8116     (void)fflush(stdout);
8117     (void)fflush(stderr);
8118 #endif /* ifdef WIN_STDIO */
8119     return SCPE_STOP;
8120   }
8121 UPDATE_SIM_TIME;                                        /* update sim time */
8122 
8123 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8124     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8125     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Empty New Interval = %d\n", sim_interval);
8126     return SCPE_OK;
8127     }
8128 sim_processing_event = TRUE;
8129 do {
8130     uptr = sim_clock_queue;                             /* get first */
8131     sim_clock_queue = uptr->next;                       /* remove first */
8132     uptr->next = NULL;                                  /* hygiene */
8133     sim_interval -= uptr->time;
8134     uptr->time = 0;
8135     if (sim_clock_queue != QUEUE_LIST_END)
8136         sim_interval = sim_clock_queue->time;
8137     else
8138         sim_interval = noqueue_time = NOQUEUE_WAIT;
8139     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr));
8140     if (uptr->action != NULL)
8141         reason = uptr->action (uptr);
8142     else
8143         reason = SCPE_OK;
8144     } while ((reason == SCPE_OK) &&
8145              (sim_interval <= 0) &&
8146              (sim_clock_queue != QUEUE_LIST_END) &&
8147              (!stop_cpu));
8148 
8149 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8150     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8151     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval);
8152     }
8153 else
8154     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue));
8155 
8156 if ((reason == SCPE_OK) && stop_cpu)
8157   {
8158 #ifdef WIN_STDIO
8159     (void)fflush(stdout);
8160     (void)fflush(stderr);
8161 #endif /* ifdef WIN_STDIO */
8162     reason = SCPE_STOP;
8163   }
8164 sim_processing_event = FALSE;
8165 return reason;
8166 }
8167 
8168 /* sim_activate - activate (queue) event
8169 
8170    Inputs:
8171         uptr    =       pointer to unit
8172         event_time =    relative timeout
8173    Outputs:
8174         reason  =       result (SCPE_OK if ok)
8175 */
8176 
8177 t_stat sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8178 {
8179 if (uptr->dynflags & UNIT_TMR_UNIT)
8180     return sim_timer_activate (uptr, event_time);
8181 return _sim_activate (uptr, event_time);
8182 }
8183 
8184 t_stat _sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8185 {
8186 UNIT *cptr, *prvptr;
8187 int32 accum;
8188 
8189 if (sim_is_active (uptr))                               /* already active? */
8190     return SCPE_OK;
8191 UPDATE_SIM_TIME;                                        /* update sim time */
8192 
8193 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8194 
8195 prvptr = NULL;
8196 accum = 0;
8197 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8198     if (event_time < (accum + cptr->time))
8199         break;
8200     accum = accum + cptr->time;
8201     prvptr = cptr;
8202     }
8203 if (prvptr == NULL) {                                   /* insert at head */
8204     cptr = uptr->next = sim_clock_queue;
8205     sim_clock_queue = uptr;
8206     }
8207 else {
8208     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
8209     prvptr->next = uptr;
8210     }
8211 uptr->time = event_time - accum;
8212 if (cptr != QUEUE_LIST_END)
8213     cptr->time = cptr->time - uptr->time;
8214 sim_interval = sim_clock_queue->time;
8215 return SCPE_OK;
8216 }
8217 
8218 /* sim_activate_abs - activate (queue) event even if event already scheduled
8219 
8220    Inputs:
8221         uptr    =       pointer to unit
8222         event_time =    relative timeout
8223    Outputs:
8224         reason  =       result (SCPE_OK if ok)
8225 */
8226 
8227 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8228 {
8229 sim_cancel (uptr);
8230 return _sim_activate (uptr, event_time);
8231 }
8232 
8233 /* sim_activate_after - activate (queue) event
8234 
8235    Inputs:
8236         uptr    =       pointer to unit
8237         usec_delay =    relative timeout (in microseconds)
8238    Outputs:
8239         reason  =       result (SCPE_OK if ok)
8240 */
8241 
8242 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8243 {
8244 return _sim_activate_after (uptr, usec_delay);
8245 }
8246 
8247 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8248 {
8249 if (sim_is_active (uptr))                               /* already active? */
8250     return SCPE_OK;
8251 return sim_timer_activate_after (uptr, usec_delay);
8252 }
8253 
8254 /* sim_cancel - cancel (dequeue) event
8255 
8256    Inputs:
8257         uptr    =       pointer to unit
8258    Outputs:
8259         reason  =       result (SCPE_OK if ok)
8260 
8261 */
8262 
8263 t_stat sim_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8264 {
8265 UNIT *cptr, *nptr;
8266 
8267 if (sim_clock_queue == QUEUE_LIST_END)
8268     return SCPE_OK;
8269 if (!sim_is_active (uptr))
8270     return SCPE_OK;
8271 UPDATE_SIM_TIME;                                        /* update sim time */
8272 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8273 nptr = QUEUE_LIST_END;
8274 
8275 if (sim_clock_queue == uptr) {
8276     nptr = sim_clock_queue = uptr->next;
8277     uptr->next = NULL;                                  /* hygiene */
8278     }
8279 else {
8280     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8281         if (cptr->next == uptr) {
8282             nptr = cptr->next = uptr->next;
8283             uptr->next = NULL;                          /* hygiene */
8284             break;                                      /* end queue scan */
8285             }
8286         }
8287     }
8288 if (nptr != QUEUE_LIST_END)
8289     nptr->time += (uptr->next) ? 0 : uptr->time;
8290 if (!uptr->next)
8291     uptr->time = 0;
8292 if (sim_clock_queue != QUEUE_LIST_END)
8293     sim_interval = sim_clock_queue->time;
8294 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8295 if (uptr->next) {
8296     sim_printf ("\rCancel failed for '%s'!\r\n", sim_uname(uptr));
8297     if (sim_deb)
8298         fclose(sim_deb);
8299     fprintf (stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
8300              __func__, __FILE__, __LINE__);
8301     abort ();
8302     }
8303 return SCPE_OK;
8304 }
8305 
8306 /* sim_is_active - test for entry in queue
8307 
8308    Inputs:
8309         uptr    =       pointer to unit
8310    Outputs:
8311         result =        TRUE if unit is busy, FALSE inactive
8312 */
8313 
8314 t_bool sim_is_active (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8315 {
8316 if (uptr->next == NULL)
8317   return FALSE;
8318 else
8319 return TRUE;
8320 }
8321 
8322 /* sim_activate_time - return activation time
8323 
8324    Inputs:
8325         uptr    =       pointer to unit
8326    Outputs:
8327         result =        absolute activation time + 1, 0 if inactive
8328 */
8329 
8330 int32 sim_activate_time (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8331 {
8332 UNIT *cptr;
8333 int32 accum = 0;
8334 
8335 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8336     if (cptr == sim_clock_queue) {
8337         if (sim_interval > 0)
8338             accum = accum + sim_interval;
8339         }
8340     else
8341         accum = accum + cptr->time;
8342     if (cptr == uptr)
8343         return accum + 1;
8344     }
8345 return 0;
8346 }
8347 
8348 /* sim_gtime - return global time
8349 
8350    Inputs: none
8351    Outputs:
8352         time    =       global time
8353 */
8354 
8355 double sim_gtime (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8356 {
8357 return sim_time;
8358 }
8359 
8360 /* sim_qcount - return queue entry count
8361 
8362    Inputs: none
8363    Outputs:
8364         count   =       number of entries on the queue
8365 */
8366 
8367 int32 sim_qcount (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8368 {
8369 int32 cnt;
8370 UNIT *uptr;
8371 
8372 cnt = 0;
8373 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
8374     cnt++;
8375 return cnt;
8376 }
8377 
8378 /* Breakpoint package.  This module replaces the VM-implemented one
8379    instruction breakpoint capability.
8380 
8381    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
8382    efficient binary searching.  A breakpoint consists of a six entry structure:
8383 
8384         addr                    address of the breakpoint
8385         type                    types of breakpoints set on the address
8386                                 a bit mask representing letters A-Z
8387         cnt                     number of iterations before breakp is taken
8388         action                  pointer command string to be executed
8389                                 when break is taken
8390         next                    list of other breakpoints with the same addr specifier
8391         time_fired              array of when this breakpoint was fired for each class
8392 
8393    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
8394    is the bitwise OR of all the type fields).  A simulator need only check for
8395    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
8396 
8397    The package contains the following public routines:
8398 
8399         sim_brk_init            initialize
8400         sim_brk_set             set breakpoint
8401         sim_brk_clr             clear breakpoint
8402         sim_brk_clrall          clear all breakpoints
8403         sim_brk_show            show breakpoint
8404         sim_brk_showall         show all breakpoints
8405         sim_brk_test            test for breakpoint
8406         sim_brk_npc             PC has been changed
8407         sim_brk_getact          get next action
8408         sim_brk_clract          clear pending actions
8409 
8410    Initialize breakpoint system.
8411 */
8412 
8413 t_stat sim_brk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8414 {
8415 int32 i;
8416 
8417 for (i=0; i<sim_brk_lnt; i++) {
8418     BRKTAB *bp;
8419     if (sim_brk_tab)
8420       {
8421         bp = sim_brk_tab[i];
8422 
8423         while (bp)
8424           {
8425             BRKTAB *bpt = bp->next;
8426 
8427             FREE (bp->act);
8428             FREE (bp);
8429             bp = bpt;
8430           }
8431       }
8432 }
8433 if (sim_brk_tab != NULL)
8434     memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8435 sim_brk_lnt = SIM_BRK_INILNT;
8436 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
8437 if (sim_brk_tab == NULL)
8438     return SCPE_MEM;
8439 memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8440 sim_brk_ent = sim_brk_ins = 0;
8441 sim_brk_clract ();
8442 sim_brk_npc (0);
8443 return SCPE_OK;
8444 }
8445 
8446 /* Search for a breakpoint in the sorted breakpoint table */
8447 
8448 BRKTAB *sim_brk_fnd (t_addr loc)
     /* [previous][next][first][last][top][bottom][index][help] */
8449 {
8450 int32 lo, hi, p;
8451 BRKTAB *bp;
8452 
8453 if (sim_brk_ent == 0) {                                 /* table empty? */
8454     sim_brk_ins = 0;                                    /* insrt at head */
8455     return NULL;                                        /* sch fails */
8456     }
8457 lo = 0;                                                 /* initial bounds */
8458 hi = sim_brk_ent - 1;
8459 do {
8460     p = (lo + hi) >> 1;                                 /* probe */
8461     bp = sim_brk_tab[p];                                /* table addr */
8462     if (loc == bp->addr) {                              /* match? */
8463         sim_brk_ins = p;
8464         return bp;
8465         }
8466     else if (loc < bp->addr)                            /* go down? p is upper */
8467         hi = p - 1;
8468     else lo = p + 1;                                    /* go up? p is lower */
8469     } while (lo <= hi);
8470 if (loc < bp->addr)                                     /* insrt before or */
8471     sim_brk_ins = p;
8472 else sim_brk_ins = p + 1;                               /* after last sch */
8473 return NULL;
8474 }
8475 
8476 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
     /* [previous][next][first][last][top][bottom][index][help] */
8477 {
8478 BRKTAB *bp = sim_brk_fnd (loc);
8479 
8480 while (bp) {
8481     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
8482                   (bp->typ == btyp))
8483         return bp;
8484     bp = bp->next;
8485     }
8486 return bp;
8487 }
8488 
8489 /* Insert a breakpoint */
8490 
8491 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
8492 {
8493 int32 i, t;
8494 BRKTAB *bp, **newp;
8495 
8496 if (sim_brk_ins < 0)
8497     return NULL;
8498 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
8499     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
8500     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
8501     if (newp == NULL)                                   /* can't extend */
8502         return NULL;
8503     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
8504     memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
8505     FREE (sim_brk_tab);                                 /* free old table */
8506     sim_brk_tab = newp;                                 /* new base, lnt */
8507     sim_brk_lnt = t;
8508     }
8509 if ((sim_brk_ins == sim_brk_ent) ||
8510     ((sim_brk_ins != sim_brk_ent) && //-V728
8511      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
8512     for (i = sim_brk_ent; i > sim_brk_ins; --i)
8513         sim_brk_tab[i] = sim_brk_tab[i - 1];
8514     sim_brk_tab[sim_brk_ins] = NULL;
8515     }
8516 bp = (BRKTAB *)calloc (1, sizeof (*bp));
8517 if (!bp)
8518   {
8519     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8520              __func__, __FILE__, __LINE__);
8521 #if defined(USE_BACKTRACE)
8522 # ifdef SIGUSR2
8523     (void)raise(SIGUSR2);
8524     /*NOTREACHED*/ /* unreachable */
8525 # endif /* ifdef SIGUSR2 */
8526 #endif /* if defined(USE_BACKTRACE) */
8527     abort();
8528   }
8529 bp->next = sim_brk_tab[sim_brk_ins];
8530 sim_brk_tab[sim_brk_ins] = bp;
8531 if (bp->next == NULL)
8532     sim_brk_ent += 1;
8533 bp->addr = loc;
8534 bp->typ = btyp;
8535 bp->cnt = 0;
8536 bp->act = NULL;
8537 for (i = 0; i < SIM_BKPT_N_SPC; i++)
8538     bp->time_fired[i] = -1.0;
8539 return bp;
8540 }
8541 
8542 /* Set a breakpoint of type sw */
8543 
8544 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
8545 {
8546 BRKTAB *bp;
8547 
8548 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
8549     sw |= sim_brk_dflt;
8550 if (~sim_brk_types & sw) {
8551     char gbuf[CBUFSIZE];
8552 
8553     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
8554     }
8555 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
8556     return SCPE_ARG;
8557 bp = sim_brk_fnd (loc);                                 /* loc present? */
8558 if (!bp)                                                /* no, allocate */
8559     bp = sim_brk_new (loc, sw);
8560 else {
8561     while (bp && (bp->typ != (uint32)sw))
8562         bp = bp->next;
8563     if (!bp)
8564         bp = sim_brk_new (loc, sw);
8565     }
8566 if (!bp)                                                /* still no? mem err */
8567     return SCPE_MEM;
8568 bp->cnt = ncnt;                                         /* set count */
8569 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
8570     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
8571     FREE (bp->act);                                     /* deallocate */
8572     bp->act = NULL;                                     /* now no action */
8573     }
8574 if ((act != NULL) && (*act != 0)) {                     /* new action? */
8575     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
8576     if (newp == NULL)                                   /* mem err? */
8577         return SCPE_MEM;
8578     strncpy (newp, act, CBUFSIZE);                      /* copy action */
8579     bp->act = newp;                                     /* set pointer */
8580     }
8581 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
8582 return SCPE_OK;
8583 }
8584 
8585 /* Clear a breakpoint */
8586 
8587 t_stat sim_brk_clr (t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8588 {
8589 BRKTAB *bpl = NULL;
8590 BRKTAB *bp = sim_brk_fnd (loc);
8591 int32 i;
8592 
8593 if (!bp)                                                /* not there? ok */
8594     return SCPE_OK;
8595 if (sw == 0)
8596     sw = SIM_BRK_ALLTYP;
8597 
8598 #ifndef __clang_analyzer__
8599 while (bp) {
8600     if (bp->typ == (bp->typ & sw)) {
8601         FREE (bp->act);                                 /* deallocate action */
8602         if (bp == sim_brk_tab[sim_brk_ins])
8603             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
8604         else
8605           {
8606             if (bpl)
8607               bpl->next = bp->next;
8608           }
8609         FREE (bp);
8610         bp = bpl;
8611         }
8612     else {
8613         bpl = bp;
8614         bp = bp->next;
8615         }
8616     }
8617 #endif /* ifndef __clang_analyzer__ */
8618 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
8619     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
8620     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
8621         sim_brk_tab[i] = sim_brk_tab[i+1];
8622     }
8623 sim_brk_summ = 0;                                       /* recalc summary */
8624 for (i = 0; i < sim_brk_ent; i++) {
8625     bp = sim_brk_tab[i];
8626     while (bp) {
8627         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
8628         bp = bp->next;
8629         }
8630     }
8631 return SCPE_OK;
8632 }
8633 
8634 /* Clear all breakpoints */
8635 
8636 t_stat sim_brk_clrall (int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8637 {
8638 int32 i;
8639 
8640 if (sw == 0)
8641     sw = SIM_BRK_ALLTYP;
8642 for (i = 0; i < sim_brk_ent;) {
8643     t_addr loc = sim_brk_tab[i]->addr;
8644     sim_brk_clr (loc, sw);
8645     if ((i < sim_brk_ent) &&
8646         (loc == sim_brk_tab[i]->addr))
8647         ++i;
8648     }
8649 return SCPE_OK;
8650 }
8651 
8652 /* Show a breakpoint */
8653 
8654 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8655 {
8656 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
8657 DEVICE *dptr;
8658 int32 i, any;
8659 
8660 if ((sw == 0) || (sw == SWMASK ('C')))
8661     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8662 if (!bp || (!(bp->typ & sw)))
8663     return SCPE_OK;
8664 dptr = sim_dflt_dev;
8665 if (dptr == NULL)
8666     return SCPE_OK;
8667 if (sw & SWMASK ('C'))
8668     fprintf (st, "SET BREAK ");
8669 else {
8670     if (sim_vm_fprint_addr)
8671         sim_vm_fprint_addr (st, dptr, loc);
8672     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8673     fprintf (st, ":\t");
8674     }
8675 for (i = any = 0; i < 26; i++) {
8676     if ((bp->typ >> i) & 1) {
8677         if ((sw & SWMASK ('C')) == 0) {
8678             if (any)
8679                 fprintf (st, ", ");
8680             fputc (i + 'A', st);
8681             }
8682         else
8683             fprintf (st, "-%c", i + 'A');
8684         any = 1;
8685         }
8686     }
8687 if (sw & SWMASK ('C')) {
8688     fprintf (st, " ");
8689     if (sim_vm_fprint_addr)
8690         sim_vm_fprint_addr (st, dptr, loc);
8691     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8692     }
8693 if (bp->cnt > 0)
8694     fprintf (st, "[%d]", bp->cnt);
8695 if (bp->act != NULL)
8696     fprintf (st, "; %s", bp->act);
8697 fprintf (st, "\n");
8698 return SCPE_OK;
8699 }
8700 
8701 /* Show all breakpoints */
8702 
8703 t_stat sim_brk_showall (FILE *st, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8704 {
8705 int32 bit, mask, types;
8706 BRKTAB **bpt;
8707 
8708 if ((sw == 0) || (sw == SWMASK ('C')))
8709     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8710 for (types=bit=0; bit <= ('Z'-'A'); bit++)
8711     if (sim_brk_types & (1 << bit))
8712         ++types;
8713 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
8714     fprintf (st, "Supported Breakpoint Types:");
8715     for (bit=0; bit <= ('Z'-'A'); bit++)
8716         if (sim_brk_types & (1 << bit))
8717             fprintf (st, " -%c", 'A' + bit);
8718     fprintf (st, "\n");
8719     }
8720 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
8721     mask = (sw & sim_brk_types);
8722     fprintf (st, "Displaying Breakpoint Types:");
8723     for (bit=0; bit <= ('Z'-'A'); bit++)
8724         if (mask & (1 << bit))
8725             fprintf (st, " -%c", 'A' + bit);
8726     fprintf (st, "\n");
8727     }
8728 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8729     BRKTAB *prev = NULL;
8730     BRKTAB *cur = *bpt;
8731     BRKTAB *next;
8732     /* First reverse the list */
8733     while (cur) {
8734         next = cur->next;
8735         cur->next = prev;
8736         prev = cur;
8737         cur = next;
8738         }
8739     /* save reversed list in the head pointer so lookups work */
8740     *bpt = prev;
8741     /* Walk the reversed list and print it in the order it was defined in */
8742     cur = prev;
8743     while (cur) {
8744         if (cur->typ & sw)
8745             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
8746         cur = cur->next;
8747         }
8748     /* reversing the list again */
8749     cur = prev;
8750     prev = NULL;
8751     while (cur) {
8752         next = cur->next;
8753         cur->next = prev;
8754         prev = cur;
8755         cur = next;
8756         }
8757     /* restore original list */
8758     *bpt = prev;
8759     }
8760 return SCPE_OK;
8761 }
8762 
8763 /* Test for breakpoint */
8764 
8765 uint32 sim_brk_test (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
8766 {
8767 BRKTAB *bp;
8768 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
8769 
8770 if (sim_brk_summ & BRK_TYP_DYN_ALL)
8771     btyp |= BRK_TYP_DYN_ALL;
8772 
8773 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
8774     double s_gtime = sim_gtime ();                      /* get time now */
8775 
8776     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
8777         return 0;
8778     bp->time_fired[spc] = s_gtime;                      /* remember match time */
8779     if (--bp->cnt > 0)                                  /* count > 0? */
8780         return 0;
8781     bp->cnt = 0;                                        /* reset count */
8782     sim_brk_setact (bp->act);                           /* set up actions */
8783     sim_brk_match_type = btyp & bp->typ;                /* set return value */
8784     if (bp->typ & BRK_TYP_TEMP)
8785         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
8786     sim_brk_match_addr = loc;
8787     return sim_brk_match_type;
8788     }
8789 return 0;
8790 }
8791 
8792 /* Get next pending action, if any */
8793 
8794 CONST char *sim_brk_getact (char *buf, int32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
8795 {
8796 char *ep;
8797 size_t lnt;
8798 
8799 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
8800     return NULL;
8801 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
8802     sim_brk_act[sim_do_depth]++;
8803 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
8804     return sim_brk_clract ();
8805     }
8806 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
8807     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
8808     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
8809     buf[lnt] = 0;                                       /* erase ; */
8810     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
8811     }
8812 else {
8813     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
8814     sim_brk_clract ();                                  /* no more */
8815     }
8816 return buf;
8817 }
8818 
8819 /* Clear pending actions */
8820 
8821 char *sim_brk_clract (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8822 {
8823 FREE (sim_brk_act_buf[sim_do_depth]);
8824 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
8825 }
8826 
8827 /* Set up pending actions */
8828 
8829 void sim_brk_setact (const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
8830 {
8831 if (action) {
8832     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
8833     if (!sim_brk_act_buf[sim_do_depth])
8834       {
8835         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8836                  __func__, __FILE__, __LINE__);
8837 #if defined(USE_BACKTRACE)
8838 # ifdef SIGUSR2
8839         (void)raise(SIGUSR2);
8840         /*NOTREACHED*/ /* unreachable */
8841 # endif /* ifdef SIGUSR2 */
8842 #endif /* if defined(USE_BACKTRACE) */
8843         abort();
8844       }
8845     strcpy (sim_brk_act_buf[sim_do_depth], action);
8846     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
8847     }
8848 else
8849     sim_brk_clract ();
8850 }
8851 
8852 /* New PC */
8853 
8854 void sim_brk_npc (uint32 cnt)
     /* [previous][next][first][last][top][bottom][index][help] */
8855 {
8856 uint32 spc;
8857 BRKTAB **bpt, *bp;
8858 
8859 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
8860     cnt = SIM_BKPT_N_SPC;
8861 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8862     for (bp = *bpt; bp; bp = bp->next) {
8863         for (spc = 0; spc < cnt; spc++)
8864             bp->time_fired[spc] = -1.0;
8865         }
8866     }
8867 }
8868 
8869 /* Clear breakpoint space */
8870 
8871 void sim_brk_clrspc (uint32 spc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
8872 {
8873 BRKTAB **bpt, *bp;
8874 
8875 if (spc < SIM_BKPT_N_SPC) {
8876     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8877         for (bp = *bpt; bp; bp = bp->next) {
8878             if (bp->typ & btyp)
8879                 bp->time_fired[spc] = -1.0;
8880             }
8881         }
8882     }
8883 }
8884 
8885 const char *sim_brk_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
8886 {
8887 static char msg[256];
8888 char addr[65];
8889 char buf[32];
8890 
8891 msg[0] = '\0';
8892 if (sim_dflt_dev) {
8893   if (sim_vm_sprint_addr)
8894     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
8895   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
8896 }
8897 if (sim_brk_type_desc) {
8898     BRKTYPTAB *brk = sim_brk_type_desc;
8899 
8900     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
8901         if (brk->btyp == sim_brk_match_type) {
8902             sprintf (msg, "%s: %s", brk->desc, addr);
8903             break;
8904             }
8905         brk++;
8906         }
8907     }
8908 if (!msg[0])
8909     sprintf (msg, "%s Breakpoint at: %s\n", put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
8910 
8911 return msg;
8912 }
8913 
8914 /* Expect package.  This code provides a mechanism to stop and control simulator
8915    execution based on traffic coming out of simulated ports and as well as a means
8916    to inject data into those ports.  It can conceptually viewed as a string
8917    breakpoint package.
8918 
8919    Expect rules are stored in tables associated with each port which can use this
8920    facility.  An expect rule consists of a five entry structure:
8921 
8922         match                   the expect match string
8923         size                    the number of bytes in the match string
8924         match_pattern           the expect match string in display format
8925         cnt                     number of iterations before match is declared
8926         action                  command string to be executed when match occurs
8927 
8928    All active expect rules are contained in an expect match context structure.
8929 
8930         rules                   the match rules
8931         size                    the count of match rules
8932         buf                     the buffer of output data which has been produced
8933         buf_ins                 the buffer insertion point for the next output data
8934         buf_size                the buffer size
8935 
8936    The package contains the following public routines:
8937 
8938         sim_set_expect          expect command parser and intializer
8939         sim_set_noexpect        noexpect command parser
8940         sim_exp_set             set or add an expect rule
8941         sim_exp_clr             clear or delete an expect rule
8942         sim_exp_clrall          clear all expect rules
8943         sim_exp_show            show an expect rule
8944         sim_exp_showall         show all expect rules
8945         sim_exp_check           test for rule match
8946 */
8947 
8948 /* Set expect */
8949 
8950 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8951 {
8952 char gbuf[CBUFSIZE];
8953 CONST char *tptr;
8954 CONST char *c1ptr;
8955 t_bool after_set = FALSE;
8956 uint32 after;
8957 int32 cnt = 0;
8958 t_stat r;
8959 
8960 if (exp == NULL)
8961     return sim_messagef (SCPE_ARG, "Null exp!\n");
8962 after = exp->after;
8963 
8964 if ((cptr == NULL) || (*cptr == 0))
8965     return SCPE_2FARG;
8966 if (*cptr == '[') {
8967     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
8968     if ((cptr == c1ptr) || (*c1ptr != ']'))
8969         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
8970     cptr = c1ptr + 1;
8971     while (sim_isspace(*cptr))
8972         ++cptr;
8973     }
8974 tptr = get_glyph (cptr, gbuf, ',');
8975 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
8976     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
8977     if (r != SCPE_OK)
8978         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
8979     after_set = TRUE;
8980     cptr = tptr;
8981     }
8982 if ((*cptr != '"') && (*cptr != '\''))
8983     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
8984 cptr = get_glyph_quoted (cptr, gbuf, 0);
8985 
8986 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
8987 }
8988 
8989 /* Clear expect */
8990 
8991 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8992 {
8993 char gbuf[CBUFSIZE];
8994 
8995 if (!cptr || !*cptr)
8996     return sim_exp_clrall (exp);                    /* clear all rules */
8997 if ((*cptr != '"') && (*cptr != '\''))
8998     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
8999 cptr = get_glyph_quoted (cptr, gbuf, 0);
9000 if (*cptr != '\0')
9001     return SCPE_2MARG;                              /* No more arguments */
9002 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9003 }
9004 
9005 /* Search for an expect rule in an expect context */
9006 
9007 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, int32 start_rule)
     /* [previous][next][first][last][top][bottom][index][help] */
9008 {
9009 int32 i;
9010 
9011 if (!exp->rules)
9012     return NULL;
9013 for (i=start_rule; i<exp->size; i++)
9014     if (!strcmp (exp->rules[i].match_pattern, match))
9015         return &exp->rules[i];
9016 return NULL;
9017 }
9018 
9019 /* Clear (delete) an expect rule */
9020 
9021 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9022 {
9023 int32 i;
9024 
9025 if (!ep)                                                /* not there? ok */
9026     return SCPE_OK;
9027 FREE (ep->match);                                       /* deallocate match string */
9028 FREE (ep->match_pattern);                               /* deallocate the display format match string */
9029 FREE (ep->act);                                         /* deallocate action */
9030 exp->size -= 1;                                         /* decrement count */
9031 #ifndef __clang_analyzer__
9032 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9033     exp->rules[i] = exp->rules[i+1];
9034 if (exp->size == 0) {                                   /* No rules left? */
9035     FREE (exp->rules);
9036     exp->rules = NULL;
9037     }
9038 #endif /* ifndef __clang_analyzer__ */
9039 return SCPE_OK;
9040 }
9041 
9042 t_stat sim_exp_clr (EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9043 {
9044 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9045 
9046 while (ep) {
9047     sim_exp_clr_tab (exp, ep);
9048     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9049     }
9050 return SCPE_OK;
9051 }
9052 
9053 /* Clear all expect rules */
9054 
9055 t_stat sim_exp_clrall (EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9056 {
9057 int32 i;
9058 
9059 for (i=0; i<exp->size; i++) {
9060     FREE (exp->rules[i].match);                         /* deallocate match string */
9061     FREE (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9062     FREE (exp->rules[i].act);                           /* deallocate action */
9063     }
9064 FREE (exp->rules);
9065 exp->rules = NULL;
9066 exp->size = 0;
9067 FREE (exp->buf);
9068 exp->buf = NULL;
9069 exp->buf_size = 0;
9070 exp->buf_ins = 0;
9071 return SCPE_OK;
9072 }
9073 
9074 /* Set/Add an expect rule */
9075 
9076 t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
9077 {
9078 EXPTAB *ep;
9079 uint8 *match_buf;
9080 uint32 match_size;
9081 int i;
9082 
9083 /* Validate the match string */
9084 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9085 if (!match_buf)
9086     return SCPE_MEM;
9087 if (switches & EXP_TYP_REGEX) {
9088     FREE (match_buf);
9089     return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9090     }
9091 else {
9092     if (switches & EXP_TYP_REGEX_I) {
9093         FREE (match_buf);
9094         return sim_messagef (SCPE_ARG, "Case independent matching is only valid for RegEx expect rules\n");
9095         }
9096     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9097     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9098         FREE (match_buf);
9099         return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9100         }
9101     }
9102 FREE (match_buf);
9103 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9104     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9105         (exp->rules[i].switches & EXP_TYP_PERSIST))
9106         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9107     }
9108 if (after && exp->size)
9109     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9110 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9111 if (!exp->rules)
9112   {
9113     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9114              __func__, __FILE__, __LINE__);
9115 #if defined(USE_BACKTRACE)
9116 # ifdef SIGUSR2
9117     (void)raise(SIGUSR2);
9118     /*NOTREACHED*/ /* unreachable */
9119 # endif /* ifdef SIGUSR2 */
9120 #endif /* if defined(USE_BACKTRACE) */
9121     abort();
9122   }
9123 ep = &exp->rules[exp->size];
9124 exp->size += 1;
9125 exp->after = after;                                     /* set halt after value */
9126 memset (ep, 0, sizeof(*ep));
9127 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9128 if (ep->match_pattern)
9129     strcpy (ep->match_pattern, match);
9130 ep->cnt = cnt;                                          /* set proceed count */
9131 ep->switches = switches;                                /* set switches */
9132 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9133 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9134     sim_exp_clr_tab (exp, ep);                          /* clear it */
9135     FREE (match_buf);                                   /* release allocation */
9136     return SCPE_MEM;
9137     }
9138 if (switches & EXP_TYP_REGEX) {
9139     FREE (match_buf);
9140     match_buf = NULL;
9141     }
9142 else {
9143     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9144     sim_decode_quoted_string (match, match_buf, &match_size);
9145     ep->match = match_buf;
9146     ep->size = match_size;
9147     }
9148 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9149 if (!ep->match_pattern)
9150   {
9151     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9152              __func__, __FILE__, __LINE__);
9153 #if defined(USE_BACKTRACE)
9154 # ifdef SIGUSR2
9155     (void)raise(SIGUSR2);
9156     /*NOTREACHED*/ /* unreachable */
9157 # endif /* ifdef SIGUSR2 */
9158 #endif /* if defined(USE_BACKTRACE) */
9159     abort();
9160   }
9161 strcpy (ep->match_pattern, match);
9162 if (ep->act) {                                          /* replace old action? */
9163     FREE (ep->act);                                     /* deallocate */
9164     ep->act = NULL;                                     /* now no action */
9165     }
9166 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
9167 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9168     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9169     if (newp == NULL)                                   /* mem err? */
9170         return SCPE_MEM;
9171     strcpy (newp, act);                                 /* copy action */
9172     ep->act = newp;                                     /* set pointer */
9173     }
9174 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9175 for (i=0; i<exp->size; i++) {
9176     uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9177     if (compare_size >= exp->buf_size) {
9178         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9179         exp->buf_size = compare_size + 1;
9180         }
9181     }
9182 return SCPE_OK;
9183 }
9184 
9185 /* Show an expect rule */
9186 
9187 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9188 {
9189 if (!ep)
9190     return SCPE_OK;
9191 fprintf (st, "EXPECT");
9192 if (ep->switches & EXP_TYP_PERSIST)
9193     fprintf (st, " -p");
9194 if (ep->switches & EXP_TYP_CLEARALL)
9195     fprintf (st, " -c");
9196 if (ep->switches & EXP_TYP_REGEX)
9197     fprintf (st, " -r");
9198 if (ep->switches & EXP_TYP_REGEX_I)
9199     fprintf (st, " -i");
9200 fprintf (st, " %s", ep->match_pattern);
9201 if (ep->cnt > 0)
9202     fprintf (st, " [%d]", ep->cnt);
9203 if (ep->act)
9204     fprintf (st, " %s", ep->act);
9205 fprintf (st, "\n");
9206 return SCPE_OK;
9207 }
9208 
9209 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9210 {
9211 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9212 
9213 if (exp->buf_size) {
9214     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9215 
9216     fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
9217     fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
9218     fprintf (st, "Buffer Contents: %s\n", bstr);
9219     FREE (bstr);
9220     }
9221 if (exp->after)
9222     fprintf (st, "Halt After: %d instructions\n", exp->after);
9223 if (exp->dptr && exp->dbit)
9224     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) : "");
9225 fprintf (st, "Match Rules:\n");
9226 if (!*match)
9227     return sim_exp_showall (st, exp);
9228 if (!ep) {
9229     fprintf (st, "No Rules match '%s'\n", match);
9230     return SCPE_ARG;
9231     }
9232 do {
9233     sim_exp_show_tab (st, exp, ep);
9234     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9235     } while (ep);
9236 return SCPE_OK;
9237 }
9238 
9239 /* Show all expect rules */
9240 
9241 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9242 {
9243 int32 i;
9244 
9245 for (i=0; i < exp->size; i++)
9246     sim_exp_show_tab (st, exp, &exp->rules[i]);
9247 return SCPE_OK;
9248 }
9249 
9250 /* Test for expect match */
9251 
9252 t_stat sim_exp_check (EXPECT *exp, uint8 data)
     /* [previous][next][first][last][top][bottom][index][help] */
9253 {
9254 int32 i;
9255 EXPTAB *ep = NULL;
9256 char *tstr = NULL;
9257 
9258 if ((!exp) || (!exp->rules))                            /* Anything to check? */
9259     return SCPE_OK;
9260 
9261 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
9262 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
9263 
9264 for (i=0; i < exp->size; i++) {
9265     ep = &exp->rules[i];
9266     if (ep == NULL)
9267         break;
9268     if (ep->switches & EXP_TYP_REGEX) {
9269         }
9270     else {
9271         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
9272             /*
9273              * First compare the newly deposited data at the beginning
9274              * of buffer with the end of the match string
9275              */
9276             if (exp->buf_ins) {
9277                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9278                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9279                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9280 
9281                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%d]: %s\n", exp->buf_ins, estr);
9282                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9283                     FREE (estr);
9284                     FREE (mstr);
9285                     }
9286                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9287                     continue;
9288                 }
9289             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9290                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9291                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9292 
9293                 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);
9294                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9295                 FREE (estr);
9296                 FREE (mstr);
9297                 }
9298             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9299                 continue;
9300             break;
9301             }
9302         else {
9303             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9304                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9305                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9306 
9307                 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_ins-ep->size, ep->size, estr);
9308                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9309                 FREE (estr);
9310                 FREE (mstr);
9311                 }
9312             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9313                 continue;
9314             break;
9315             }
9316         }
9317     }
9318 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
9319         exp->buf_ins = 0;                               /* wrap around to beginning */
9320         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9321     }
9322 if ((ep != NULL) && (i != exp->size)) {                 /* Found? */
9323     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern!\n");
9324     if (ep->cnt > 0) {
9325         ep->cnt -= 1;
9326         sim_debug (exp->dbit, exp->dptr, "Waiting for %d more match%s before stopping\n",
9327                                          ep->cnt, (ep->cnt == 1) ? "" : "es");
9328         }
9329     else {
9330         uint32 after   = exp->after;
9331         int32 switches = ep->switches;
9332         if (ep->act && *ep->act) {
9333             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
9334             }
9335         else {
9336             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
9337             }
9338         sim_brk_setact (ep->act);                       /* set up actions */
9339         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
9340             sim_exp_clrall (exp);                       /* delete all rules */
9341         else {
9342             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
9343                 sim_exp_clr_tab (exp, ep);              /* delete it */
9344             }
9345         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
9346                       (switches & EXP_TYP_TIME) ?
9347                             (uint32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
9348                              after);
9349         }
9350     /* Matched data is no longer available for future matching */
9351     exp->buf_ins = 0;
9352     }
9353 if (tstr)  //-V547
9354   FREE (tstr);
9355 return SCPE_OK;
9356 }
9357 
9358 /* Queue input data for sending */
9359 
9360 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
     /* [previous][next][first][last][top][bottom][index][help] */
9361 {
9362 if (snd->extoff != 0) {
9363     if (snd->insoff-snd->extoff > 0)
9364         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9365     snd->insoff -= snd->extoff;
9366     snd->extoff -= snd->extoff;
9367     }
9368 if (snd->insoff+size > snd->bufsize) {
9369     snd->bufsize = snd->insoff+size;
9370     snd->buffer  = (uint8 *)realloc(snd->buffer, snd->bufsize);
9371     if (!snd->buffer)
9372       {
9373         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9374                  __func__, __FILE__, __LINE__);
9375 #if defined(USE_BACKTRACE)
9376 # ifdef SIGUSR2
9377         (void)raise(SIGUSR2);
9378         /*NOTREACHED*/ /* unreachable */
9379 # endif /* ifdef SIGUSR2 */
9380 #endif /* if defined(USE_BACKTRACE) */
9381         abort();
9382       }
9383     }
9384 memcpy(snd->buffer+snd->insoff, data, size);
9385 snd->insoff += size;
9386 if (delay)
9387     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
9388 if (after)
9389     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
9390 if (snd->after == 0)
9391     snd->after = snd->delay;
9392 snd->next_time = sim_gtime() + snd->after;
9393 return SCPE_OK;
9394 }
9395 
9396 /* Cancel Queued input data */
9397 t_stat sim_send_clear (SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
9398 {
9399 snd->insoff = 0;
9400 snd->extoff = 0;
9401 return SCPE_OK;
9402 }
9403 
9404 /* Display console Queued input data status */
9405 
9406 t_stat sim_show_send_input (FILE *st, const SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
9407 {
9408 if (snd->extoff < snd->insoff) {
9409     fprintf (st, "%d bytes of pending input Data:\n    ", snd->insoff-snd->extoff);
9410     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9411     fprintf (st, "\n");
9412     }
9413 else
9414     fprintf (st, "No Pending Input Data\n");
9415 if ((snd->next_time - sim_gtime()) > 0) {
9416     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
9417         fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
9418         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
9419     else
9420         fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
9421     }
9422 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
9423     fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
9424 else
9425     fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay);
9426 if (snd->dptr && snd->dbit)
9427     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) : "");
9428 return SCPE_OK;
9429 }
9430 
9431 /* Poll for Queued input data */
9432 
9433 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9434 {
9435 if (snd && (snd->extoff < snd->insoff)) {               /* pending input characters available? */
9436     if (sim_gtime() < snd->next_time) {                 /* too soon? */
9437         *stat = SCPE_OK;
9438         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
9439         }
9440     else {
9441         char dstr[8] = "";
9442         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
9443         snd->next_time = sim_gtime() + snd->delay;
9444         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
9445             sprintf (dstr, " '%c'", *stat & 0xFF);
9446         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
9447         }
9448     return TRUE;
9449     }
9450 return FALSE;
9451 }
9452 
9453 /* Message Text */
9454 
9455 const char *sim_error_text (t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9456 {
9457 static char msgbuf[64];
9458 
9459 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
9460 if (stat == SCPE_OK)
9461     return "No Error";
9462 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
9463     return scp_errors[stat-SCPE_BASE].message;
9464 sprintf(msgbuf, "Error %d", stat);
9465 return msgbuf;
9466 }
9467 
9468 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9469 {
9470 char gbuf[CBUFSIZE];
9471 int32 cond;
9472 
9473 *stat = SCPE_ARG;
9474 cptr = get_glyph (cptr, gbuf, 0);
9475 if (0 == memcmp("SCPE_", gbuf, 5))
9476     memmove (gbuf, gbuf + 5, 1 + strlen (gbuf + 5));  /* skip leading SCPE_ */
9477 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
9478     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
9479         cond += SCPE_BASE;
9480         break;
9481         }
9482 if (0 == strcmp(gbuf, "OK"))
9483     cond = SCPE_OK;
9484 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
9485     if (0 == (cond = strtol(gbuf, NULL, 0)))  /* try explicit number */
9486         return SCPE_ARG;
9487     }
9488 if (cond > SCPE_MAX_ERR)
9489     return SCPE_ARG;
9490 *stat = cond;
9491 return SCPE_OK;
9492 }
9493 
9494 /* Debug printout routines, from Dave Hittner */
9495 
9496 const char* debug_bstates = "01_^";
9497 char debug_line_prefix[256];
9498 int32 debug_unterm  = 0;
9499 
9500 /* Finds debug phrase matching bitmask from from device DEBTAB table */
9501 
9502 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9503 {
9504 static const char *debtab_none    = "DEBTAB_ISNULL";
9505 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
9506 const char *some_match = NULL;
9507 int32 offset = 0;
9508 
9509 if (dptr->debflags == 0)
9510     return debtab_none;
9511 
9512 dbits &= dptr->dctrl;                           /* Look for just the bits tha matched */
9513 
9514 /* Find matching words for bitmask */
9515 
9516 while ((offset < 32) && dptr->debflags[offset].name) {
9517     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
9518         return dptr->debflags[offset].name;
9519     if (dptr->debflags[offset].mask & dbits)
9520         some_match = dptr->debflags[offset].name;
9521     offset++;
9522     }
9523 return some_match ? some_match : debtab_nomatch;
9524 }
9525 
9526 /* Prints standard debug prefix unless previous call unterminated */
9527 
9528 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9529 {
9530 const char* debug_type = get_dbg_verb (dbits, dptr);
9531 char tim_t[32] = "";
9532 char tim_a[32] = "";
9533 char  pc_s[64] = "";
9534 struct timespec time_now;
9535 
9536 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
9537     clock_gettime(CLOCK_REALTIME, &time_now);
9538     if (sim_deb_switches & SWMASK ('R'))
9539         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
9540     if (sim_deb_switches & SWMASK ('T')) {
9541         time_t tnow = (time_t)time_now.tv_sec;
9542         struct tm *now = gmtime(&tnow);
9543         sprintf(tim_t, "%02d:%02d:%02d.%03ld ",
9544                 (int)now->tm_hour,
9545                 (int)now->tm_min,
9546                 (int)now->tm_sec,
9547                 (long)(time_now.tv_nsec/1000000));
9548         }
9549     if (sim_deb_switches & SWMASK ('A')) {
9550         sprintf(tim_t, "%d.%03ld ",
9551                 (int)(time_now.tv_sec),
9552                 (long)(time_now.tv_nsec/1000000));
9553         }
9554     }
9555 if (sim_deb_switches & SWMASK ('P')) {
9556     t_value val;
9557 
9558     if (sim_vm_pc_value)
9559         val = (*sim_vm_pc_value)();
9560     else
9561         val = get_rval (sim_PC, 0);
9562     sprintf(pc_s, "-%s:", sim_PC->name);
9563     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
9564     }
9565 sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s,  "", dptr->name, debug_type);
9566 return debug_line_prefix;
9567 }
9568 
9569 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
     /* [previous][next][first][last][top][bottom][index][help] */
9570 {
9571 int32 i, fields, offset;
9572 uint32 value, beforevalue, mask;
9573 
9574 for (fields=offset=0; bitdefs[fields].name; ++fields) {
9575     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
9576         bitdefs[fields].offset = offset;
9577     offset += bitdefs[fields].width;
9578     }
9579 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
9580     if (bitdefs[i].name[0] == '\0')
9581         continue;
9582     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
9583         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
9584         Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
9585         }
9586     else {
9587         const char *delta = "";
9588         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
9589         value = (uint32)((after >> bitdefs[i].offset) & mask);
9590         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
9591         if (value < beforevalue)
9592             delta = "_";
9593         if (value > beforevalue)
9594             delta = "^";
9595         if (bitdefs[i].valuenames)
9596             Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
9597         else
9598             if (bitdefs[i].format) {
9599                 Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
9600                 Fprintf(stream, bitdefs[i].format, value);
9601                 Fprintf(stream, " ");
9602                 }
9603             else
9604                 Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
9605         }
9606     }
9607 }
9608 
9609 /* Prints state of a register: bit translation + state (0,1,_,^)
9610    indicating the state and transition of the bit and bitfields. States:
9611    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
9612 
9613 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
     /* [previous][next][first][last][top][bottom][index][help] */
9614     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
9615 {
9616 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
9617     if (!debug_unterm)
9618         fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
9619     if (header)
9620         fprintf(sim_deb, "%s: ", header);
9621     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
9622     if (terminate)
9623         fprintf(sim_deb, "\r\n");
9624     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
9625     }
9626 }
9627 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
     /* [previous][next][first][last][top][bottom][index][help] */
9628     uint32 before, uint32 after, int terminate)
9629 {
9630 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
9631 }
9632 
9633 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9634 void sim_printf (const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9635 {
9636 char stackbuf[STACKBUFSIZE];
9637 int32 bufsize = sizeof(stackbuf);
9638 char *buf = stackbuf;
9639 int32 len;
9640 va_list arglist;
9641 
9642 while (1) {                                         /* format passed string, args */
9643     va_start (arglist, fmt);
9644     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9645     va_end (arglist);
9646 
9647 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9648 
9649     if ((len < 0) || (len >= bufsize-1)) {
9650         if (buf != stackbuf)
9651             FREE (buf);
9652         bufsize = bufsize * 2;
9653         if (bufsize < len + 2)
9654             bufsize = len + 2;
9655         buf = (char *) malloc (bufsize);
9656         if (buf == NULL)                            /* out of memory */
9657             return;
9658         buf[bufsize-1] = '\0';
9659         continue;
9660         }
9661     break;
9662     }
9663 
9664 if (sim_is_running) {
9665     char *c, *remnant = buf;
9666     while ((c = strchr(remnant, '\n'))) {
9667         if ((c != buf) && (*(c - 1) != '\r'))
9668             printf("%.*s\r\n", (int)(c-remnant), remnant);
9669         else
9670             printf("%.*s\n", (int)(c-remnant), remnant);
9671         remnant = c + 1;
9672         }
9673     printf("%s", remnant);
9674     }
9675 else
9676     printf("%s", buf);
9677 if (sim_log && (sim_log != stdout))
9678     fprintf (sim_log, "%s", buf);
9679 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
9680     fprintf (sim_deb, "%s", buf);
9681 
9682 if (buf != stackbuf)
9683     FREE (buf);
9684 }
9685 
9686 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9687 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9688 {
9689 char stackbuf[STACKBUFSIZE];
9690 int32 bufsize = sizeof(stackbuf);
9691 char *buf = stackbuf;
9692 int32 len;
9693 va_list arglist;
9694 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
9695 
9696 while (1) {                                         /* format passed string, args */
9697     va_start (arglist, fmt);
9698     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9699     va_end (arglist);
9700 
9701 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9702 
9703     if ((len < 0) || (len >= bufsize-1)) {
9704         if (buf != stackbuf)
9705             FREE (buf);
9706         bufsize = bufsize * 2;
9707         if (bufsize < len + 2)
9708             bufsize = len + 2;
9709         buf = (char *) malloc (bufsize);
9710         if (buf == NULL)                            /* out of memory */
9711             return SCPE_MEM;
9712         buf[bufsize-1] = '\0';
9713         continue;
9714         }
9715     break;
9716     }
9717 
9718 if (sim_do_ocptr[sim_do_depth]) {
9719     if (!sim_do_echo && !sim_quiet && !inhibit_message)
9720         sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9721     else {
9722         if (sim_deb)                        /* Always put context in debug output */
9723             fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9724         }
9725     }
9726 if (sim_is_running && !inhibit_message) {
9727     char *c, *remnant = buf;
9728     while ((c = strchr(remnant, '\n'))) {
9729         if ((c != buf) && (*(c - 1) != '\r'))
9730             printf("%.*s\r\n", (int)(c-remnant), remnant);
9731         else
9732             printf("%.*s\n", (int)(c-remnant), remnant);
9733         remnant = c + 1;
9734         }
9735     printf("%s", remnant);
9736     }
9737 else {
9738     if (!inhibit_message)
9739         printf("%s", buf);
9740     }
9741 if (sim_log && (sim_log != stdout) && !inhibit_message)
9742     fprintf (sim_log, "%s", buf);
9743 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
9744     fprintf (sim_deb, "%s", buf);
9745 
9746 if (buf != stackbuf)
9747     FREE (buf);
9748 return stat | SCPE_NOMESSAGE;
9749 }
9750 
9751 /* Inline debugging - will print debug message if debug file is
9752    set and the bitmask matches the current device debug options.
9753    Extra returns are added for un*x systems, since the output
9754    device is set into 'raw' mode when the cpu is booted,
9755    and the extra returns don't hurt any other systems.
9756    Callers should be calling sim_debug() which is a macro
9757    defined in scp.h which evaluates the action condition before
9758    incurring call overhead. */
9759 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9760 {
9761 DEVICE *dptr = (DEVICE *)vdptr;
9762 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
9763     char stackbuf[STACKBUFSIZE];
9764     int32 bufsize = sizeof(stackbuf);
9765     char *buf = stackbuf;
9766     va_list arglist;
9767     int32 i, j, len;
9768     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
9769 
9770     buf[bufsize-1] = '\0';
9771     while (1) {                                         /* format passed string, args */
9772         va_start (arglist, fmt);
9773         len = vsnprintf (buf, bufsize-1, fmt, arglist);
9774         va_end (arglist);
9775 
9776 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9777 
9778         if ((len < 0) || (len >= bufsize-1)) {
9779             if (buf != stackbuf)
9780                 FREE (buf);
9781             bufsize = bufsize * 2;
9782             if (bufsize < len + 2)
9783                 bufsize = len + 2;
9784             buf = (char *) malloc (bufsize);
9785             if (buf == NULL)                            /* out of memory */
9786                 return;
9787             buf[bufsize-1] = '\0';
9788             continue;
9789             }
9790         break;
9791         }
9792 
9793 /* Output the formatted data expanding newlines where they exist */
9794 
9795     for (i = j = 0; i < len; ++i) {
9796         if ('\n' == buf[i]) {
9797             if (i >= j) {
9798                 if ((i != j) || (i == 0)) {
9799                     if (debug_unterm)
9800                         fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
9801                     else                                /* print prefix when required */
9802                         fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
9803                     }
9804                 debug_unterm = 0;
9805                 }
9806             j = i + 1;
9807             }
9808         }
9809     if (i > j) {
9810         if (debug_unterm)
9811             fprintf (sim_deb, "%.*s", i-j, &buf[j]);
9812         else                                        /* print prefix when required */
9813             fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
9814         }
9815 
9816 /* Set unterminated flag for next time */
9817 
9818     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
9819     if (buf != stackbuf)
9820         FREE (buf);
9821     }
9822 return;
9823 }
9824 
9825 void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason)
     /* [previous][next][first][last][top][bottom][index][help] */
9826 {
9827 
9828 if (sim_deb && (dptr->dctrl & reason)) {
9829     sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
9830     if (data && len) {
9831         unsigned int i, same, group, sidx, oidx, ridx, eidx, soff;
9832         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
9833         static char hex[] = "0123456789ABCDEF";
9834         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
9835         static unsigned char ebcdic2ascii[] = {
9836             0000,0001,0002,0003,0234,0011,0206,0177,
9837             0227,0215,0216,0013,0014,0015,0016,0017,
9838             0020,0021,0022,0023,0235,0205,0010,0207,
9839             0030,0031,0222,0217,0034,0035,0036,0037,
9840             0200,0201,0202,0203,0204,0012,0027,0033,
9841             0210,0211,0212,0213,0214,0005,0006,0007,
9842             0220,0221,0026,0223,0224,0225,0226,0004,
9843             0230,0231,0232,0233,0024,0025,0236,0032,
9844             0040,0240,0241,0242,0243,0244,0245,0246,
9845             0247,0250,0133,0056,0074,0050,0053,0041,
9846             0046,0251,0252,0253,0254,0255,0256,0257,
9847             0260,0261,0135,0044,0052,0051,0073,0136,
9848             0055,0057,0262,0263,0264,0265,0266,0267,
9849             0270,0271,0174,0054,0045,0137,0076,0077,
9850             0272,0273,0274,0275,0276,0277,0300,0301,
9851             0302,0140,0072,0043,0100,0047,0075,0042,
9852             0303,0141,0142,0143,0144,0145,0146,0147,
9853             0150,0151,0304,0305,0306,0307,0310,0311,
9854             0312,0152,0153,0154,0155,0156,0157,0160,
9855             0161,0162,0313,0314,0315,0316,0317,0320,
9856             0321,0176,0163,0164,0165,0166,0167,0170,
9857             0171,0172,0322,0323,0324,0325,0326,0327,
9858             0330,0331,0332,0333,0334,0335,0336,0337,
9859             0340,0341,0342,0343,0344,0345,0346,0347,
9860             0173,0101,0102,0103,0104,0105,0106,0107,
9861             0110,0111,0350,0351,0352,0353,0354,0355,
9862             0175,0112,0113,0114,0115,0116,0117,0120,
9863             0121,0122,0356,0357,0360,0361,0362,0363,
9864             0134,0237,0123,0124,0125,0126,0127,0130,
9865             0131,0132,0364,0365,0366,0367,0370,0371,
9866             0060,0061,0062,0063,0064,0065,0066,0067,
9867             0070,0071,0372,0373,0374,0375,0376,0377,
9868             };
9869 
9870         for (i=same=0; i<len; i += 16) {
9871             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
9872                 ++same;
9873                 continue;
9874                 }
9875             if (same > 0) {
9876                 sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
9877                 same = 0;
9878                 }
9879             group = (((len - i) > 16) ? 16 : (len - i));
9880             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
9881             eidx = strlen(ebcdicbuf);
9882             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
9883             ridx = strlen(rad50buf);
9884             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
9885             soff = strlen(strbuf);
9886             for (sidx=oidx=0; sidx<group; ++sidx) {
9887                 outbuf[oidx++] = ' ';
9888                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
9889                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
9890                 if (sim_isprint (data[i+sidx]))
9891                     strbuf[soff+sidx] = data[i+sidx];
9892                 else
9893                     strbuf[soff+sidx] = '.';
9894                 if (ridx && ((sidx&1) == 0)) {
9895                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
9896 
9897                     if (word >= 64000) {
9898                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
9899                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
9900                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
9901                         }
9902                     else {
9903                         rad50buf[ridx++] = rad50[word/1600];
9904                         rad50buf[ridx++] = rad50[(word/40)%40];
9905                         rad50buf[ridx++] = rad50[word%40];
9906                         }
9907                     }
9908                 if (eidx) {
9909                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
9910                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
9911                     else
9912                         ebcdicbuf[eidx++] = '.';
9913                     }
9914                 }
9915             outbuf[oidx] = '\0';
9916             strbuf[soff+sidx] = '\0';
9917             ebcdicbuf[eidx] = '\0';
9918             rad50buf[ridx] = '\0';
9919             sim_debug (reason, dptr, "%04X%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
9920             }
9921         if (same > 0) {
9922             sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), (unsigned int)(len-1));
9923             }
9924         }
9925     }
9926 }
9927 
9928 int Fprintf (FILE *f, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9929 {
9930 int ret = 0;
9931 va_list args;
9932 
9933 va_start (args, fmt);
9934     ret = vfprintf (f, fmt, args);
9935 va_end (args);
9936 return ret;
9937 }
9938 
9939 /* Hierarchical help presentation
9940  *
9941  * Device help can be presented hierarchically by calling
9942  *
9943  * t_stat scp_help (FILE *st, DEVICE *dptr,
9944  *                  UNIT *uptr, int flag, const char *help, char *cptr)
9945  *
9946  * or one of its three cousins from the device HELP routine.
9947  *
9948  * *help is the pointer to the structured help text to be displayed.
9949  *
9950  * The format and usage, and some helper macros can be found in scp_help.h
9951  * If you don't use the macros, it is not necessary to #include "scp_help.h".
9952  *
9953  * Actually, if you don't specify a DEVICE pointer and don't include
9954  * other device references, it can be used for non-device help.
9955  */
9956 
9957 #define blankch(x) ((x) == ' ' || (x) == '\t')
9958 
9959 typedef struct topic {
9960     uint32         level;
9961     char          *title;
9962     char          *label;
9963     struct topic  *parent;
9964     struct topic **children;
9965     uint32         kids;
9966     char          *text;
9967     size_t         len;
9968     uint32         flags;
9969     uint32         kidwid;
9970 #define HLP_MAGIC_TOPIC  1
9971     } TOPIC;
9972 
9973 static volatile struct {
9974     const char *error;
9975     const char *prox;
9976     size_t block;
9977     size_t line;
9978     } help_where = { "", NULL, 0, 0 };
9979 jmp_buf help_env;
9980 
9981 #define FAIL(why,text,here)        \
9982   {                                \
9983     help_where.error = #text;      \
9984     help_where.prox = here;        \
9985     longjmp ( help_env, (why) );   \
9986     /*LINTED E_STMT_NOT_REACHED*/  \
9987   }
9988 
9989 /*
9990  * Add to topic text.
9991  * Expands text buffer as necessary.
9992  */
9993 
9994 static void appendText (TOPIC *topic, const char *text, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
9995 {
9996 char *newt;
9997 
9998 if (!len)
9999     return;
10000 
10001 newt = (char *)realloc (topic->text, topic->len + len +1);
10002 if (!newt) {
10003 #ifndef SUNLINT
10004     FAIL (SCPE_MEM, No memory, NULL);
10005 #endif /* ifndef SUNLINT */
10006     }
10007 topic->text = newt;
10008 memcpy (newt + topic->len, text, len);
10009 topic->len +=len;
10010 newt[topic->len] = '\0';
10011 return;
10012 }
10013 
10014 /* Release memory held by a topic and its children.
10015  */
10016 static void cleanHelp (TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10017 {
10018 TOPIC *child;
10019 size_t i;
10020 
10021 FREE (topic->title);
10022 FREE (topic->text);
10023 FREE (topic->label);
10024 for (i = 0; i < topic->kids; i++) {
10025     child = topic->children[i];
10026     cleanHelp (child);
10027     FREE (child);
10028     }
10029 FREE (topic->children);
10030 return;
10031 }
10032 
10033 /* Build a help tree from a string.
10034  * Handles substitutions, formatting.
10035  */
10036 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10037                          UNIT *uptr, const char *htext, va_list ap)
10038 {
10039 char *end;
10040 size_t n, ilvl;
10041 #define VSMAX 100
10042 char *vstrings[VSMAX];
10043 size_t vsnum = 0;
10044 char * astrings[VSMAX+1];
10045 size_t asnum = 0;
10046 char *const *hblock;
10047 const char *ep;
10048 t_bool excluded = FALSE;
10049 
10050 /* variable arguments consumed table.
10051  * The scheme used allows arguments to be accessed in random
10052  * order, but for portability, all arguments must be char *.
10053  * If you try to violate this, there ARE machines that WILL break.
10054  */
10055 
10056 memset (vstrings, 0, sizeof (vstrings));
10057 memset (astrings, 0, sizeof (astrings));
10058 astrings[asnum++] = (char *) htext;
10059 
10060 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10061     help_where.block = hblock - astrings;
10062     help_where.line = 0;
10063     while (*htext) {
10064         const char *start;
10065 
10066         help_where.line++;
10067         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10068             if (excluded) {                     /* Excluded topic text */
10069                 while (*htext && *htext != '\n')
10070                     htext++;
10071                 if (*htext)
10072                     ++htext;
10073                 continue;
10074                 }
10075             ilvl = 1;
10076             appendText (topic, "    ", 4);      /* Basic indentation */
10077             if (*htext == '+') {                /* More for each + */
10078                 while (*htext == '+') {
10079                     ilvl++;
10080                     appendText (topic, "    ", 4);
10081                     htext++;
10082                     }
10083                 }
10084             while (*htext && *htext != '\n' && sim_isspace (*htext))
10085                 htext++;
10086             if (!*htext)                        /* Empty after removing leading spaces */
10087                 break;
10088             start = htext;
10089             while (*htext) {                    /* Process line for substitutions */
10090                 if (*htext == '%') {
10091                     appendText (topic, start, htext - start); /* Flush up to escape */
10092                     switch (*++htext) {         /* Evaluate escape */
10093                         case 'U':
10094                             if (dptr) {
10095                                 char buf[129];
10096                                 n = uptr? uptr - dptr->units: 0;
10097                                 sprintf (buf, "%s%u", dptr->name, (int)n);
10098                                 appendText (topic, buf, strlen (buf));
10099                                 }
10100                             break;
10101                         case 'D':
10102                             if (dptr != NULL)
10103                                 appendText (topic, dptr->name, strlen (dptr->name));
10104                             break;
10105                         case 'S':
10106                             appendText (topic, sim_name, strlen (sim_name));
10107                             break;
10108                         case '%':
10109                             appendText (topic, "%", 1);
10110                             break;
10111                         case '+':
10112                             appendText (topic, "+", 1);
10113                             break;
10114                         default:                    /* Check for vararg # */
10115                             if (sim_isdigit (*htext)) {
10116                                 n = 0;
10117                                 while (sim_isdigit (*htext))
10118                                     n += (n * 10) + (*htext++ - '0');
10119                                 if (( *htext != 'H' && *htext != 's') ||
10120                                     n == 0 || n >= VSMAX) {
10121 #ifndef SUNLINT
10122                                     FAIL (SCPE_ARG, Invalid escape, htext);
10123 #endif /* ifndef SUNLINT */
10124                                     }
10125                                 while (n > vsnum)   /* Get arg pointer if not cached */
10126                                     vstrings[vsnum++] = va_arg (ap, char *);
10127                                 start = vstrings[n-1]; /* Insert selected string */
10128                                 if (*htext == 'H') {   /* Append as more input */
10129                                     if (asnum >= VSMAX) {
10130 #ifndef SUNLINT
10131                                         FAIL (SCPE_ARG, Too many blocks, htext);
10132 #endif /* ifndef SUNLINT */
10133                                         }
10134                                     astrings[asnum++] = (char *)start;
10135                                     break;
10136                                     }
10137                                 ep = start;
10138                                 while (*ep) {
10139                                     if (*ep == '\n') {
10140                                         ep++;       /* Segment to \n */
10141                                         appendText (topic, start, ep - start);
10142                                         if (*ep) {  /* More past \n, indent */
10143                                             size_t i;
10144                                             for (i = 0; i < ilvl; i++)
10145                                                 appendText (topic, "    ", 4);
10146                                             }
10147                                         start = ep;
10148                                         }
10149                                     else
10150                                         ep++;
10151                                     }
10152                                 appendText (topic, start, ep-start);
10153                                 break;
10154                                 }
10155 #ifndef SUNLINT
10156                             FAIL (SCPE_ARG, Invalid escape, htext);
10157 #endif /* ifndef SUNLINT */
10158                         } /* switch (escape) */
10159                     start = ++htext;
10160                     continue;                   /* Current line */
10161                     } /* if (escape) */
10162                 if (*htext == '\n') {           /* End of line, append last segment */
10163                     htext++;
10164                     appendText (topic, start, htext - start);
10165                     break;                      /* To next line */
10166                     }
10167                 htext++;                        /* Regular character */
10168                 }
10169             continue;
10170             } /* topic text line */
10171         if (sim_isdigit (*htext)) {             /* Topic heading */
10172             TOPIC **children;
10173             TOPIC *newt;
10174             char nbuf[100];
10175 
10176             n = 0;
10177             start = htext;
10178             while (sim_isdigit (*htext))
10179                 n += (n * 10) + (*htext++ - '0');
10180             if ((htext == start) || !n) {
10181 #ifndef SUNLINT
10182                 FAIL (SCPE_ARG, Invalid topic heading, htext);
10183 #endif /* ifndef SUNLINT */
10184                 }
10185             if (n <= topic->level) {            /* Find level for new topic */
10186                 while (n <= topic->level)
10187                     topic = topic->parent;
10188                 }
10189             else {
10190                 if (n > topic->level +1) {      /* Skipping down more than 1 */
10191 #ifndef SUNLINT
10192                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10193 #endif /* ifndef SUNLINT */
10194                     }
10195                 }
10196             while (*htext && (*htext != '\n') && sim_isspace (*htext))
10197                 htext++;
10198             if (!*htext || (*htext == '\n')) {  /* Name missing */
10199 #ifndef SUNLINT
10200                 FAIL (SCPE_ARG, Missing topic name, htext);
10201 #endif /* ifndef SUNLINT */
10202                 }
10203             start = htext;
10204             while (*htext && (*htext != '\n'))
10205                 htext++;
10206             if (start == htext) {               /* Name NULL */
10207 #ifndef SUNLINT
10208                 FAIL (SCPE_ARG, Null topic name, htext);
10209 #endif /* ifndef SUNLINT */
10210                 }
10211             excluded = FALSE;
10212             if (*start == '?') {                /* Conditional topic? */
10213                 size_t n = 0;
10214                 start++;
10215                 while (sim_isdigit (*start))    /* Get param # */
10216                     n += (n * 10) + (*start++ - '0');
10217                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX) {
10218 #ifndef SUNLINT
10219                     FAIL (SCPE_ARG, Invalid parameter number, start);
10220 #endif /* ifndef SUNLINT */
10221                     }
10222                 while (n > vsnum)               /* Get arg pointer if not cached */
10223                     vstrings[vsnum++] = va_arg (ap, char *);
10224                 end = vstrings[n-1];            /* Check for True */
10225                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10226                     excluded = TRUE;            /* False, skip topic this time */
10227                     if (*htext)
10228                         htext++;
10229                     continue;
10230                     }
10231                 }
10232             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10233             if (!newt) {
10234 #ifndef SUNLINT
10235                 FAIL (SCPE_MEM, No memory, NULL);
10236 #endif /* ifndef SUNLINT */
10237                 }
10238             newt->title = (char *) malloc ((htext - start)+1);
10239             if (!newt->title) {
10240                 FREE (newt);
10241 #ifndef SUNLINT
10242                 FAIL (SCPE_MEM, No memory, NULL);
10243 #endif /* ifndef SUNLINT */
10244                 }
10245             memcpy (newt->title, start, htext - start);
10246             newt->title[htext - start] = '\0';
10247             if (*htext)
10248                 htext++;
10249 
10250             if (newt->title[0] == '$')
10251                 newt->flags |= HLP_MAGIC_TOPIC;
10252 
10253             children = (TOPIC **) realloc (topic->children,
10254                                            (topic->kids +1) * sizeof (TOPIC *));
10255             if (!children) {
10256                 FREE (newt->title);
10257                 FREE (newt);
10258 #ifndef SUNLINT
10259                 FAIL (SCPE_MEM, No memory, NULL);
10260 #endif /* ifndef SUNLINT */
10261                 }
10262             topic->children = children;
10263             topic->children[topic->kids++] = newt;
10264             newt->level = n;
10265             newt->parent = topic;
10266             n = strlen (newt->title);
10267             if (n > topic->kidwid)
10268                 topic->kidwid = n;
10269             sprintf (nbuf, ".%u", topic->kids);
10270             n = strlen (topic->label) + strlen (nbuf) + 1;
10271             newt->label = (char *) malloc (n);
10272             if (!newt->label) {
10273                 FREE (newt->title);
10274                 topic->children[topic->kids -1] = NULL;
10275                 FREE (newt);
10276 #ifndef SUNLINT
10277                 FAIL (SCPE_MEM, No memory, NULL);
10278 #endif /* ifndef SUNLINT */
10279                 }
10280             sprintf (newt->label, "%s%s", topic->label, nbuf);
10281             topic = newt;
10282             continue;
10283             } /* digits introducing a topic */
10284         if (*htext == ';') {                    /* Comment */
10285             while (*htext && *htext != '\n')
10286                 htext++;
10287             continue;
10288             }
10289 #ifndef SUNLINT
10290         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
10291 #endif /* ifndef SUNLINT */
10292         } /* htext not at end */
10293     memset (vstrings, 0, VSMAX * sizeof (char *));
10294     vsnum = 0;
10295     } /* all strings */
10296 
10297 return topic;
10298 }
10299 
10300 /*
10301  * Create prompt string - top thru current topic
10302  * Add prompt at end.
10303  */
10304 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
     /* [previous][next][first][last][top][bottom][index][help] */
10305 {
10306 char *prefix;
10307 char *newp, *newt;
10308 
10309 if (topic->level == 0) {
10310     prefix = (char *) calloc (2,1);
10311     if (!prefix) {
10312 #ifndef SUNLINT
10313         FAIL (SCPE_MEM, No memory, NULL);
10314 #endif /* ifndef SUNLINT */
10315         }
10316     prefix[0] = '\n';
10317     }
10318 else
10319     prefix = helpPrompt (topic->parent, "", oneword);
10320 
10321 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
10322                         strlen (pstring) +1);
10323 if (!newp) {
10324     FREE (prefix);
10325 #ifndef SUNLINT
10326     FAIL (SCPE_MEM, No memory, NULL);
10327 #endif /* ifndef SUNLINT */
10328     }
10329 strcpy (newp, prefix);
10330 if (topic->children) {
10331     if (topic->level != 0)
10332         strcat (newp, " ");
10333     newt = (topic->flags & HLP_MAGIC_TOPIC)?
10334             topic->title+1: topic->title;
10335     if (oneword) {
10336         char *np = newp + strlen (newp);
10337         while (*newt) {
10338             *np++ = blankch (*newt)? '_' : *newt;
10339             newt++;
10340             }
10341         *np = '\0';
10342         }
10343     else
10344         strcat (newp, newt);
10345     if (*pstring && *pstring != '?')
10346         strcat (newp, " ");
10347     }
10348 strcat (newp, pstring);
10349 FREE (prefix);
10350 return newp;
10351 }
10352 
10353 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10354 {
10355 char tbuf[CBUFSIZE];
10356 size_t i, skiplines;
10357 #ifdef _WIN32
10358 FILE *tmp;
10359 char *tmpnam;
10360 
10361 do {
10362     int fd;
10363     tmpnam = _tempnam (NULL, "simh");
10364     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
10365     if (fd != -1) {
10366         tmp = _fdopen (fd, "w+");
10367         break;
10368         }
10369     } while (1);
10370 #else
10371 FILE *tmp = tmpfile();
10372 #endif
10373 
10374 if (!tmp) {
10375     fprintf (st, "Unable to create temporary file: %s\n", strerror (errno));
10376     return;
10377     }
10378 
10379 if (topic->title)
10380     fprintf (st, "%s\n", topic->title+1);
10381 
10382 skiplines = 0;
10383 if (topic->title) {
10384   if (!strcmp (topic->title+1, "Registers")) {
10385       fprint_reg_help (tmp, dptr) ;
10386       skiplines = 1;
10387       }
10388   else
10389       if (!strcmp (topic->title+1, "Set commands")) {
10390           fprint_set_help (tmp, dptr);
10391           skiplines = 3;
10392           }
10393       else
10394           if (!strcmp (topic->title+1, "Show commands")) {
10395               fprint_show_help (tmp, dptr);
10396               skiplines = 3;
10397               }
10398   }
10399 rewind (tmp);
10400 
10401 /* Discard leading blank lines/redundant titles */
10402 
10403 for (i =0; i < skiplines; i++)
10404     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
10405 
10406 while (fgets (tbuf, sizeof (tbuf), tmp)) {
10407     if (tbuf[0] != '\n')
10408         fputs ("    ", st);
10409     fputs (tbuf, st);
10410     }
10411 fclose (tmp);
10412 #ifdef _WIN32
10413 remove (tmpnam);
10414 FREE (tmpnam);
10415 #endif
10416 return;
10417 }
10418 /* Flatten and display help for those who say they prefer it. */
10419 
10420 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10421                                UNIT *uptr, int32 flag,
10422                                TOPIC *topic, va_list ap )
10423 {
10424 size_t i;
10425 
10426 if (topic->flags & HLP_MAGIC_TOPIC) {
10427     fprintf (st, "\n%s ", topic->label);
10428     displayMagicTopic (st, dptr, topic);
10429     }
10430 else
10431     fprintf (st, "\n%s %s\n", topic->label, topic->title);
10432 
10433 /*
10434  * Topic text (for magic topics, follows for explanations)
10435  * It's possible/reasonable for a magic topic to have no text.
10436  */
10437 
10438 if (topic->text)
10439     fputs (topic->text, st);
10440 
10441 for (i = 0; i < topic->kids; i++)
10442     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
10443 
10444 return SCPE_OK;
10445 }
10446 
10447 #define HLP_MATCH_AMBIGUOUS (~0u)
10448 #define HLP_MATCH_WILDCARD  (~1U)
10449 #define HLP_MATCH_NONE      0
10450 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
     /* [previous][next][first][last][top][bottom][index][help] */
10451 {
10452 size_t i, match;
10453 char cbuf[CBUFSIZE], *cptr;
10454 
10455 if (!strcmp (token, "*"))
10456     return HLP_MATCH_WILDCARD;
10457 
10458 match = 0;
10459 for (i = 0; i < topic->kids; i++) {
10460     strcpy (cbuf,topic->children[i]->title +
10461             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10462     cptr = cbuf;
10463     while (*cptr) {
10464         if (blankch (*cptr)) {
10465             *cptr++ = '_';
10466             }
10467         else {
10468             *cptr = (char)toupper (*cptr);
10469             cptr++;
10470             }
10471         }
10472     if (!strcmp (cbuf, token))      /* Exact Match */
10473         return i+1;
10474     if (!strncmp (cbuf, token, strlen (token))) {
10475         if (match)
10476             return HLP_MATCH_AMBIGUOUS;
10477         match = i+1;
10478         }
10479     }
10480 return match;
10481 }
10482 
10483 /* Main help routine */
10484 
10485 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10486                   UNIT *uptr, int32 flag,
10487                   const char *help, const char *cptr, va_list ap)
10488 {
10489 
10490 TOPIC top;
10491 TOPIC *topic = &top;
10492 int failed;
10493 size_t match;
10494 size_t i;
10495 const char *p;
10496 t_bool flat_help = FALSE;
10497 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
10498 
10499 static const char attach_help[] = { " ATTACH" };
10500 static const char  brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help.\n" };
10501 static const char onecmd_help[] = { "%s help.\n" };
10502 static const char   help_help[] = {
10503 
10504     /****|***********************80 column width guide********************************/
10505     "    To see more HELP information, type the listed subtopic name.  To move\n"
10506     "    up a level, just type <CR>.  To review the current subtopic, type \"?\".\n"
10507     "    To view all subtopics, type \"*\".  To exit type \"EXIT\", \"^C\", or \"^D\".\n\n"
10508     };
10509 
10510 memset (&top, 0, sizeof(top));
10511 top.parent = &top;
10512 if ((failed = setjmp (help_env)) != 0) {
10513     fprintf (stderr, "\nHELP was unable to process HELP for this device.\n"
10514                      "Error in block %u line %u: %s\n"
10515                      "%s%*.*s%s\n",
10516              (int)help_where.block, (int)help_where.line, help_where.error,
10517              help_where.prox ? "Near '" : "",
10518              help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
10519              help_where.prox ? help_where.prox : "",
10520                  help_where.prox ? "'" : "");
10521     cleanHelp (&top);
10522     return failed;
10523     }
10524 
10525 /* Compile string into navigation tree */
10526 
10527 /* Root */
10528 
10529 if (dptr) {
10530     p = dptr->name;
10531     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
10532     }
10533 else
10534     p = sim_name;
10535 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
10536 if (!top.title)
10537   {
10538     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10539              __func__, __FILE__, __LINE__);
10540 #if defined(USE_BACKTRACE)
10541 # ifdef SIGUSR2
10542     (void)raise(SIGUSR2);
10543     /*NOTREACHED*/ /* unreachable */
10544 # endif /* ifdef SIGUSR2 */
10545 #endif /* if defined(USE_BACKTRACE) */
10546     abort();
10547   }
10548 for (i = 0; p[i]; i++ )
10549     top.title[i] = (char)toupper (p[i]);
10550 top.title[i] = '\0';
10551 if (flag & SCP_HELP_ATTACH)
10552     strcpy (top.title+i, attach_help);
10553 
10554 top.label = (char *) malloc (sizeof ("1"));
10555 if (!top.label)
10556   {
10557     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10558              __func__, __FILE__, __LINE__);
10559 #if defined(USE_BACKTRACE)
10560 # ifdef SIGUSR2
10561     (void)raise(SIGUSR2);
10562     /*NOTREACHED*/ /* unreachable */
10563 # endif /* ifdef SIGUSR2 */
10564 #endif /* if defined(USE_BACKTRACE) */
10565     abort();
10566   }
10567 strcpy (top.label, "1");
10568 
10569 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
10570 
10571 if (flat_help) {
10572     flag |= SCP_HELP_FLAT;
10573     if (sim_ttisatty())
10574         fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
10575     else
10576         fprintf (st, "%s help.\n", top.title);
10577     }
10578 else
10579     fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
10580 
10581 /* Add text and subtopics */
10582 
10583 (void) buildHelp (&top, dptr, uptr, help, ap);
10584 
10585 /* Go to initial topic if provided */
10586 
10587 while (cptr && *cptr) {
10588     cptr = get_glyph (cptr, gbuf, 0);
10589     if (!gbuf[0])
10590         break;
10591     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10592         fprintf (st, "\n");
10593         fputs (help_help, st);
10594         break;
10595         }
10596     match =  matchHelpTopicName (topic, gbuf);
10597     if (match == HLP_MATCH_WILDCARD) {
10598         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10599         cleanHelp (&top);
10600         return SCPE_OK;
10601         }
10602     if (match == HLP_MATCH_AMBIGUOUS) {
10603         fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
10604         break;
10605         }
10606     if (match == HLP_MATCH_NONE) {
10607         fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
10608         break;
10609         }
10610     topic = topic->children[match-1];
10611     }
10612 cptr = NULL;
10613 
10614 if (flat_help) {
10615     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10616     cleanHelp (&top);
10617     return SCPE_OK;
10618     }
10619 
10620 /* Interactive loop displaying help */
10621 
10622 while (TRUE) {
10623     char *pstring;
10624     const char *prompt[2] = {"? ", "Subtopic? "};
10625 
10626     /* Some magic topic names for help from data structures */
10627 
10628     if (topic->flags & HLP_MAGIC_TOPIC) {
10629         fputc ('\n', st);
10630         displayMagicTopic (st, dptr, topic);
10631         }
10632     else
10633         fprintf (st, "\n%s\n", topic->title);
10634 
10635     /* Topic text (for magic topics, follows for explanations)
10636      * It's possible/reasonable for a magic topic to have no text.
10637      */
10638 
10639     if (topic->text)
10640         fputs (topic->text, st);
10641 
10642     if (topic->kids) {
10643         size_t w = 0;
10644         char *p;
10645         char tbuf[CBUFSIZE];
10646 
10647         fprintf (st, "\n    Additional information available:\n\n");
10648         for (i = 0; i < topic->kids; i++) {
10649             strcpy (tbuf, topic->children[i]->title +
10650                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10651             for (p = tbuf; *p; p++) {
10652                 if (blankch (*p))
10653                     *p = '_';
10654                 }
10655             w += 4 + topic->kidwid;
10656             if (w > 80) {
10657                 w = 4 + topic->kidwid;
10658                 fputc ('\n', st);
10659                 }
10660             fprintf (st, "    %-*s", topic->kidwid, tbuf);
10661             }
10662         fprintf (st, "\n\n");
10663         if (flag & SCP_HELP_ONECMD) {
10664             pstring = helpPrompt (topic, "", TRUE);
10665             fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
10666             FREE (pstring);
10667             break;
10668             }
10669         }
10670 
10671     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
10672         break;
10673 
10674   reprompt:
10675     if (!cptr || !*cptr) {
10676         if (topic->kids == 0)
10677             topic = topic->parent;
10678         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
10679 
10680         cptr = read_line_p (pstring+1, cbuf, sizeof (cbuf), stdin);
10681         FREE (pstring);
10682         if ((cptr != NULL) &&                   /* Got something? */
10683             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
10684              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
10685             cptr = NULL;                        /* These are EOF synonyms */
10686         }
10687 
10688     if (!cptr)                              /* EOF, exit help */
10689         break;
10690 
10691     cptr = get_glyph (cptr, gbuf, 0);
10692     if (!strcmp (gbuf, "*")) {              /* Wildcard */
10693         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10694         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
10695         }
10696     if (!gbuf[0]) {                         /* Blank, up a level */
10697         if (topic->level == 0)
10698             break;
10699         topic = topic->parent;
10700         continue;
10701         }
10702     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
10703         continue;
10704     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10705         fputs (help_help, st);
10706         goto reprompt;
10707         }
10708     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
10709         break;
10710 
10711     /* String - look for that topic */
10712 
10713     if (!topic->kids) {
10714         fprintf (st, "No additional help at this level.\n");
10715         cptr = NULL;
10716         goto reprompt;
10717         }
10718     match = matchHelpTopicName (topic, gbuf);
10719     if (match == HLP_MATCH_AMBIGUOUS) {
10720         fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
10721         cptr = NULL;
10722         goto reprompt;
10723         }
10724 
10725     if (match == HLP_MATCH_NONE) {
10726         fprintf (st, "Help for %s is not available\n", gbuf);
10727         cptr = NULL;
10728         goto reprompt;
10729         }
10730     /* Found, display subtopic */
10731 
10732     topic = topic->children[match-1];
10733     }
10734 
10735 /* Free structures and return */
10736 
10737 cleanHelp (&top);
10738 
10739 return SCPE_OK;
10740 }
10741 
10742 /* variable argument list shell - most commonly used */
10743 
10744 t_stat scp_help (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10745                  UNIT *uptr, int32 flag,
10746                  const char *help, const char *cptr, ...)
10747 {
10748 t_stat r;
10749 va_list ap;
10750 
10751 va_start (ap, cptr);
10752 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
10753 va_end (ap);
10754 
10755 return r;
10756 }
10757 
10758 #if defined(_MSC_VER)
10759 # pragma warning(pop)
10760 #endif

/* [previous][next][first][last][top][bottom][index][help] */