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

   1 /*
   2  * scp.c: simulator control program
   3  *
   4  * vim: filetype=c:tabstop=4:ai:colorcolumn=84:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: 7cde852c-f62a-11ec-8444-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2022 Robert M. Supnik
  11  * Copyright (c) 2021-2023 Jeffrey H. Johnson
  12  * Copyright (c) 2006-2024 The DPS8M Development Team
  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 #if !defined(__EXTENSIONS__)
  43 # define __EXTENSIONS__
  44 #endif
  45 
  46 #if !defined(__STDC_WANT_LIB_EXT1__)
  47 # define __STDC_WANT_LIB_EXT1__ 1
  48 #endif
  49 
  50 #if !defined(_GNU_SOURCE)
  51 # define _GNU_SOURCE
  52 #endif
  53 
  54 /* Macros and data structures */
  55 
  56 #include "sim_defs.h"
  57 #include "sim_disk.h"
  58 #include "sim_tape.h"
  59 #include "sim_sock.h"
  60 
  61 #include <signal.h>
  62 #include <ctype.h>
  63 #include <time.h>
  64 #include <math.h>
  65 #include <stddef.h>
  66 #if defined(_WIN32)
  67 # if !defined(WIN32_LEAN_AND_MEAN)
  68 #  define WIN32_LEAN_AND_MEAN
  69 # endif /* if !defined(WIN32_LEAN_AND_MEAN) */
  70 # if defined(_MSC_VER)
  71 #  pragma warning(push, 3)
  72 # endif
  73 # include <direct.h>
  74 # include <io.h>
  75 # include <fcntl.h>
  76 #else
  77 # include <unistd.h>
  78 # define HAVE_UNISTD 1
  79 #endif
  80 #include <sys/stat.h>
  81 #include <sys/types.h>
  82 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  83 # include <sys/resource.h>
  84 #endif
  85 #include <setjmp.h>
  86 #include <stdint.h>
  87 #include <limits.h>
  88 #if defined(__APPLE__)
  89 # include <xlocale.h>
  90 #endif
  91 #include <locale.h>
  92 
  93 #include "linehistory.h"
  94 
  95 #if defined(__APPLE__)
  96 # include <sys/sysctl.h>
  97 #endif /* if defined(_APPLE_) */
  98 
  99 #if ( defined(__linux__) || defined(__linux) || defined(_linux) || defined(linux) ) //-V1040
 100 # include <sys/sysinfo.h>
 101 # define LINUX_OS
 102 #endif
 103 
 104 #include <uv.h>
 105 
 106 #if !defined(HAVE_UNISTD)
 107 # undef USE_BACKTRACE
 108 #endif /* if !defined(HAVE_UNISTD) */
 109 
 110 #if defined(USE_BACKTRACE)
 111 # include <string.h>
 112 # include <signal.h>
 113 #endif /* if defined(USE_BACKTRACE) */
 114 
 115 #if defined(__HAIKU__)
 116 # include <OS.h>
 117 #endif /* if defined(__HAIKU__) */
 118 
 119 #if !defined(__CYGWIN__)
 120 # if !defined(__APPLE__)
 121 #  if !defined(_AIX)
 122 #   if !defined(__MINGW32__)
 123 #    if !defined(__MINGW64__)
 124 #     if !defined(CROSS_MINGW32)
 125 #      if !defined(CROSS_MINGW64)
 126 #       if !defined(_WIN32)
 127 #        if !defined(__HAIKU__)
 128 #         include <link.h>
 129 #        endif
 130 #       endif
 131 #      endif
 132 #     endif
 133 #    endif
 134 #   endif
 135 #  endif
 136 # endif
 137 #endif
 138 
 139 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
 140 # include <windows.h>
 141 #endif
 142 
 143 #if defined(__CYGWIN__)
 144 # include <windows.h>
 145 # include <sys/utsname.h>
 146 # include <sys/cygwin.h>
 147 # include <cygwin/version.h>
 148 #endif
 149 
 150 #define DBG_CTR 0
 151 
 152 #include "../dps8/dps8.h"
 153 #include "../dps8/dps8_cpu.h"
 154 #include "../dps8/ver.h"
 155 
 156 #include "../dps8/dps8_iom.h"
 157 #include "../dps8/dps8_fnp2.h"
 158 
 159 #include "../decNumber/decContext.h"
 160 #include "../decNumber/decNumberLocal.h"
 161 
 162 #include "../dps8/dps8_math128.h"
 163 
 164 #include "sir.h"
 165 #include "sir/internal.h"
 166 #include "sir/version.h"
 167 
 168 #if !defined(__CYGWIN__)
 169 # if !defined(__APPLE__)
 170 #  if !defined(_AIX)
 171 #   if !defined(__MINGW32__)
 172 #    if !defined(__MINGW64__)
 173 #     if !defined(CROSS_MINGW32)
 174 #      if !defined(CROSS_MINGW64)
 175 #       if !defined(_WIN32)
 176 #        if !defined(__HAIKU__)
 177 static unsigned int dl_iterate_phdr_callback_called = 0;
 178 #        endif
 179 #       endif
 180 #      endif
 181 #     endif
 182 #    endif
 183 #   endif
 184 #  endif
 185 # endif
 186 #endif
 187 
 188 #if defined(MAX)
 189 # undef MAX
 190 #endif /* if defined(MAX) */
 191 #define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
 192 
 193 #if defined(FREE)
 194 # undef FREE
 195 #endif /* if defined(FREE) */
 196 #define FREE(p) do  \
 197   {                 \
 198     free((p));      \
 199     (p) = NULL;     \
 200   } while(0)
 201 
 202 /* search logical and boolean ops */
 203 
 204 #define SCH_OR          0                               /* search logicals */
 205 #define SCH_AND         1
 206 #define SCH_XOR         2
 207 #define SCH_E           0                               /* search booleans */
 208 #define SCH_N           1
 209 #define SCH_G           2
 210 #define SCH_L           3
 211 #define SCH_EE          4
 212 #define SCH_NE          5
 213 #define SCH_GE          6
 214 #define SCH_LE          7
 215 
 216 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
 217 #define SRBSIZ          1024                            /* save/restore buffer */
 218 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
 219 #define SIM_BRK_ALLTYP  0xFFFFFFFB
 220 
 221 #define UPDATE_SIM_TIME                                         \
 222     if (1) {                                                    \
 223         int32 _x;                                               \
 224         if (sim_clock_queue == QUEUE_LIST_END)                  \
 225             _x = noqueue_time;                                  \
 226         else                                                    \
 227             _x = sim_clock_queue->time;                         \
 228         sim_time = sim_time + (_x - sim_interval);              \
 229         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
 230         if (sim_clock_queue == QUEUE_LIST_END)                  \
 231             noqueue_time = sim_interval;                        \
 232         else                                                    \
 233             sim_clock_queue->time = sim_interval;               \
 234         }                                                       \
 235     else                                                        \
 236         (void)0
 237 
 238 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
 239 
 240 #define SZ_R(rp) \
 241     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
 242 
 243 #define SZ_LOAD(sz,v,mb,j)                                                 \
 244     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j));        \
 245     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
 246     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
 247     else v = *(((t_uint64 *) mb) + ((uint32) j));
 248 
 249 #define SZ_STORE(sz,v,mb,j)                                                         \
 250     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v;                    \
 251     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
 252     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
 253     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
 254 
 255 #define GET_SWITCHES(cp) \
 256     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
 257 
 258 #define GET_RADIX(val,dft)                          \
 259     if (sim_switches & SWMASK ('O')) val = 8;       \
 260     else if (sim_switches & SWMASK ('D')) val = 10; \
 261     else if (sim_switches & SWMASK ('H')) val = 16; \
 262     else val = dft;
 263 
 264 /*
 265  * The per-simulator init routine is a weak global that defaults to NULL
 266  * The other per-simulator pointers can be overridden by the init routine
 267  */
 268 
 269 t_bool sim_asynch_enabled = FALSE;
 270 t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
 271 t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
 272 extern void (*sim_vm_init) (void);
 273 extern void (*sim_vm_exit) (void);
 274 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
 275 void (*sim_vm_post) (t_bool from_scp) = NULL;
 276 CTAB *sim_vm_cmd = NULL;
 277 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
 278 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
 279 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
 280 t_value (*sim_vm_pc_value) (void) = NULL;
 281 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
 282 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
 283 unsigned int nprocs;
 284 
 285 /* Prototypes */
 286 
 287 /* Set and show command processors */
 288 
 289 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 290 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 291 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 292 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 293 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
 294 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
 295 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 296 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 297 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 298 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 299 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 300 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 301 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 302 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 303 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 304 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 305 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 306 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 307 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
 308 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 309 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 310 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 311 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 312 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 313 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 314 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 315 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
 316 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
 317 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
 318 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
 319 t_stat sim_save (FILE *sfile);
 320 t_stat sim_rest (FILE *rfile);
 321 
 322 /* Breakpoint package */
 323 
 324 t_stat sim_brk_init (void);
 325 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
 326 t_stat sim_brk_clr (t_addr loc, int32 sw);
 327 t_stat sim_brk_clrall (int32 sw);
 328 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
 329 t_stat sim_brk_showall (FILE *st, uint32 sw);
 330 CONST char *sim_brk_getact (char *buf, int32 size);
 331 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
 332 char *sim_brk_clract (void);
 333 
 334 FILE *stdnul;
 335 
 336 /* Command support routines */
 337 
 338 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 339 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 340 int32 test_search (t_value *val, SCHTAB *schptr);
 341 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
 342 int32 get_switches (const char *cptr);
 343 CONST char *get_sim_sw (CONST char *cptr);
 344 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
 345 t_value get_rval (REG *rptr, uint32 idx);
 346 void put_rval (REG *rptr, uint32 idx, t_value val);
 347 void fprint_help (FILE *st);
 348 void fprint_stopped (FILE *st, t_stat r);
 349 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
 350 void fprint_sep (FILE *st, int32 *tokens);
 351 char *read_line (char *ptr, int32 size, FILE *stream);
 352 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
 353 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
 354 char *sim_trim_endspc (char *cptr);
 355 
 356 /* Forward references */
 357 
 358 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
 359 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
 360 t_bool qdisable (DEVICE *dptr);
 361 t_stat attach_err (UNIT *uptr, t_stat stat);
 362 t_stat detach_all (int32 start_device, t_bool shutdown);
 363 t_stat assign_device (DEVICE *dptr, const char *cptr);
 364 t_stat deassign_device (DEVICE *dptr);
 365 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
 366 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
 367     REG *lowr, REG *highr, uint32 lows, uint32 highs);
 368 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
 369 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
 370 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
 371     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
 372 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
 373 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
 374     UNIT *uptr, int32 dfltinc);
 375 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
 376 t_stat step_svc (UNIT *ptr);
 377 t_stat expect_svc (UNIT *ptr);
 378 t_stat set_on (int32 flag, CONST char *cptr);
 379 t_stat set_verify (int32 flag, CONST char *cptr);
 380 t_stat set_message (int32 flag, CONST char *cptr);
 381 t_stat set_quiet (int32 flag, CONST char *cptr);
 382 t_stat set_localopc (int32 flag, CONST char *cptr);
 383 t_stat set_asynch (int32 flag, CONST char *cptr);
 384 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 385 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
 386 void int_handler (int signal);
 387 t_stat set_prompt (int32 flag, CONST char *cptr);
 388 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
 389 t_stat sim_set_environment (int32 flag, CONST char *cptr);
 390 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
 391 
 392 /* Global data */
 393 
 394 DEVICE *sim_dflt_dev             = NULL;
 395 UNIT *sim_clock_queue            = QUEUE_LIST_END;
 396 int32 sim_interval               = 0;
 397 int32 sim_switches               = 0;
 398 FILE *sim_ofile                  = NULL;
 399 SCHTAB *sim_schrptr              = FALSE;
 400 SCHTAB *sim_schaptr              = FALSE;
 401 DEVICE *sim_dfdev                = NULL;
 402 UNIT *sim_dfunit                 = NULL;
 403 DEVICE **sim_internal_devices    = NULL;
 404 uint32 sim_internal_device_count = 0;
 405 int32 sim_opt_out                = 0;
 406 int32 sim_is_running             = 0;
 407 t_bool sim_processing_event      = FALSE;
 408 uint32 sim_brk_summ              = 0;
 409 uint32 sim_brk_types             = 0;
 410 BRKTYPTAB *sim_brk_type_desc     = NULL;         /* type descriptions */
 411 uint32 sim_brk_dflt              = 0;
 412 uint32 sim_brk_match_type;
 413 t_addr sim_brk_match_addr;
 414 char *sim_brk_act[MAX_DO_NEST_LVL];
 415 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
 416 BRKTAB **sim_brk_tab             = NULL;
 417 int32 sim_brk_ent                = 0;
 418 int32 sim_brk_lnt                = 0;
 419 int32 sim_brk_ins                = 0;
 420 int32 sim_iglock                 = 0;
 421 int32 sim_nolock                 = 0;
 422 int32 sim_quiet                  = 0;
 423 int32 sim_localopc               = 1;
 424 int32 sim_randompst              = 0;
 425 int32 sim_randstate              = 0;
 426 int32 sim_step                   = 0;
 427 int nodist                       = 0;
 428 #if defined(PERF_STRIP)
 429 int32 sim_nostate                = 1;
 430 #else
 431 int32 sim_nostate                = 0;
 432 #endif /* if defined(PERF_STRIP) */
 433 static double sim_time;
 434 static uint32 sim_rtime;
 435 static int32 noqueue_time;
 436 volatile int32 stop_cpu          = 0;
 437 t_value *sim_eval                = NULL;
 438 static t_value sim_last_val;
 439 static t_addr sim_last_addr;
 440 FILE *sim_log                    = NULL;         /* log file */
 441 FILEREF *sim_log_ref             = NULL;         /* log file file reference */
 442 FILE *sim_deb                    = NULL;         /* debug file */
 443 FILEREF *sim_deb_ref             = NULL;         /* debug file file reference */
 444 int32 sim_deb_switches           = 0;            /* debug switches */
 445 struct timespec sim_deb_basetime;                /* debug timestamp relative base time */
 446 char *sim_prompt                 = NULL;         /* prompt string */
 447 static FILE *sim_gotofile;                       /* the currently open do file */
 448 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];   /* the current line number in the currently open do file */
 449 static int32 sim_do_echo         = 0;            /* the echo status of the currently open do file */
 450 static int32 sim_show_message    = 1;            /* the message display status of the currently open do file */
 451 static int32 sim_on_inherit      = 0;            /* the inherit status of on state and conditions when executing do files */
 452 static int32 sim_do_depth        = 0;
 453 
 454 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
 455 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
 456 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
 457 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
 458 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
 459 
 460 t_stat sim_last_cmd_stat;                        /* Command Status */
 461 
 462 static SCHTAB sim_stabr;                         /* Register search specifier */
 463 static SCHTAB sim_staba;                         /* Memory search specifier */
 464 
 465 static UNIT sim_step_unit   = { UDATA (&step_svc, 0, 0)  };
 466 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
 467 
 468 /* Tables and strings */
 469 
 470 const char save_vercur[] = "V4.1";
 471 const char save_ver40[]  = "V4.0";
 472 const char save_ver35[]  = "V3.5";
 473 const char save_ver32[]  = "V3.2";
 474 const char save_ver30[]  = "V3.0";
 475 const struct scp_error {
 476     const char *code;
 477     const char *message;
 478     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
 479         {{"NXM",     "Address space exceeded"},
 480          {"UNATT",   "Unit not attached"},
 481          {"IOERR",   "I/O error"},
 482          {"CSUM",    "Checksum error"},
 483          {"FMT",     "Format error"},
 484          {"NOATT",   "Unit not attachable"},
 485          {"OPENERR", "File open error"},
 486          {"MEM",     "Memory exhausted"},
 487          {"ARG",     "Invalid argument"},
 488          {"STEP",    "Step expired"},
 489          {"UNK",     "Unknown command"},
 490          {"RO",      "Read only argument"},
 491          {"INCOMP",  "Command not completed"},
 492          {"STOP",    "Simulation stopped"},
 493          {"EXIT",    "Goodbye"},
 494          {"TTIERR",  "Console input I/O error"},
 495          {"TTOERR",  "Console output I/O error"},
 496          {"EOF",     "End of file"},
 497          {"REL",     "Relocation error"},
 498          {"NOPARAM", "No settable parameters"},
 499          {"ALATT",   "Unit already attached"},
 500          {"TIMER",   "Hardware timer error"},
 501          {"SIGERR",  "Signal handler setup error"},
 502          {"TTYERR",  "Console terminal setup error"},
 503          {"SUB",     "Subscript out of range"},
 504          {"NOFNC",   "Command not allowed"},
 505          {"UDIS",    "Unit disabled"},
 506          {"NORO",    "Read only operation not allowed"},
 507          {"INVSW",   "Invalid switch"},
 508          {"MISVAL",  "Missing value"},
 509          {"2FARG",   "Too few arguments"},
 510          {"2MARG",   "Too many arguments"},
 511          {"NXDEV",   "Non-existent device"},
 512          {"NXUN",    "Non-existent unit"},
 513          {"NXREG",   "Non-existent register"},
 514          {"NXPAR",   "Non-existent parameter"},
 515          {"NEST",    "Nested DO command limit exceeded"},
 516          {"IERR",    "Internal error"},
 517          {"MTRLNT",  "Invalid magtape record length"},
 518          {"LOST",    "Console Telnet connection lost"},
 519          {"TTMO",    "Console Telnet connection timed out"},
 520          {"STALL",   "Console Telnet output stall"},
 521          {"AFAIL",   "Assertion failed"},
 522          {"INVREM",  "Invalid remote console command"},
 523          {"NOTATT",  "Not attached"},
 524          {"EXPECT",  "Expect matched"},
 525          {"REMOTE",  "Remote console command"},
 526     };
 527 
 528 const size_t size_map[] = { sizeof (int8),
 529     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
 530     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
 531 };
 532 
 533 const t_value width_mask[] = { 0,
 534     0x1, 0x3, 0x7, 0xF,
 535     0x1F, 0x3F, 0x7F, 0xFF,
 536     0x1FF, 0x3FF, 0x7FF, 0xFFF,
 537     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
 538     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
 539     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
 540     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
 541     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
 542     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
 543     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
 544     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
 545     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
 546     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
 547     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
 548     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
 549     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
 550     };
 551 
 552 static const char simh_help[] =
 553        /***************** 80 character line width template *************************/
 554       "1Commands\n"
 555 #define HLP_RESET       "*Commands Resetting Devices"
 556        /***************** 80 character line width template *************************/
 557       "2Resetting Devices\n"
 558       " The `RESET` command (*abbreviated* `RE`) resets a device or the entire\n"
 559       " simulator to a predefined condition.  If the switch \"`-p`\" is specified,\n"
 560       " the device is reset to its initial power-on state:\n\n"
 561       "++RESET                  resets all devices\n"
 562       "++RESET -p               power-cycle all devices\n"
 563       "++RESET ALL              resets all devices\n"
 564       "++RESET <device>         resets the specified <device>\n\n"
 565       " * Typically, `RESET` *aborts* in-progress I/O operations, *clears* any\n"
 566       " interrupt requests, and returns the device to a quiescent state.\n\n"
 567       " * It does **NOT** clear the main memory or affect associated I/O\n"
 568       " connections.\n"
 569 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
 570 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
 571 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
 572 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
 573        /***************** 80 character line width template *************************/
 574       "2Examining and Changing State\n"
 575       " There are four commands to examine and change state:\n\n"
 576       " * `EXAMINE` (*abbreviated* `E`) examines state\n"
 577       " * `DEPOSIT` (*abbreviated* `D`) changes state\n"
 578       " * `IEXAMINE` (\"interactive examine\", *abbreviated* `IE`) examines state\n"
 579       "    and allows the user to interactively change it\n"
 580       " * `IDEPOSIT` (interactive deposit, *abbreviated* `ID`) allows the user to\n"
 581       "    interactively change state\n\n"
 582       " All four commands take the form:\n\n"
 583       "++command {modifiers} <object list>\n\n"
 584       " The `DEPOSIT` command requires the deposit value at the end of the command.\n\n"
 585       " There are four kinds of modifiers: **switches**, **device/unit name**,\n"
 586       " **search specifier**, and for `EXAMINE`, **output file**.\n\n"
 587       " * **Switches** have been described previously.\n"
 588       " * A **device/unit name** identifies the device and unit whose address\n"
 589       " space is to be examined or modified. If no device is specified, the CPU\n"
 590       " main memory is selected. If a device but no unit is specified, unit `0`\n"
 591       " of the specified device is selected automatically.\n"
 592       " * The **search specifier** provides criteria for testing addresses or\n"
 593       " registers to see if they should be processed.  The search specifier\n"
 594       " consists of a \"<`logical operator`>\", a \"<`relational operator`>\", or\n"
 595       " both, optionally separated by spaces:\n\n"
 596       "++{ < logical op >  < value > }  < relational op >  < value >\n\n"
 597        /***************** 80 character line width template *************************/
 598       " * * The \"<`logical operator`>\" may be \"`&`\" (*and*), \"`|`\" (*or*),\n"
 599       " or \"`^`\" (*exclusive or*), and the \"<`relational operator`>\" may\n"
 600       " be \"`=`\" or \"`==`\" (*equal*), \"`!`\" or \"`!=`\" (*not\n"
 601       " equal*), \">=\" (*greater than or equal*), \">\" (*greater\n"
 602       " than*), \"<=\" (*less than or equal*), or \"<\" (*less than*).\n"
 603       " * * If any \"<`logical operator`>\" is specified without\n"
 604       " a \"<`relational operator`>\", it is ignored.\n"
 605       " * * If any \"<`relational operator`>\" is specified without\n"
 606       " a \"<`logical operator`>\", no logical operation is performed.\n"
 607       " * * All comparisons are unsigned.\n\n"
 608       " * The **output file** modifier redirects the command output to a file\n"
 609       " instead of the console.  The **output file** modifier is specified with\n"
 610       " the \"`@`\" (*commercial-at*) character, followed by a valid file name.\n\n"
 611       " **NOTE**: Modifiers may be specified in any order.  If multiple\n"
 612       " modifiers of the same type are specified, later modifiers override earlier\n"
 613       " modifiers. If the **device/unit name** comes *after* the search specifier,\n"
 614       " the search values will interpreted in the *radix of the CPU*, rather than\n"
 615       " of the device/unit.\n\n"
 616       " The \"<`object list`>\" argument consists of one or more of the following,\n"
 617       " separated by commas:\n\n"
 618        /***************** 80 character line width template *************************/
 619       "++register                the specified register\n"
 620       "++register[sub1-sub2]     the specified register array locations,\n"
 621       "++++++++                  starting at location sub1 up to and\n"
 622       "++++++++                  including location sub2\n"
 623       "++register[sub1/length]   the specified register array locations,\n"
 624       "++++++++                  starting at location sub1 up to but\n"
 625       "++++++++                  not including sub1+length\n"
 626       "++register[ALL]           all locations in the specified register\n"
 627       "++++++++                  array\n"
 628       "++register1-register2     all the registers starting at register1\n"
 629       "++++++++                  up to and including register2\n"
 630       "++address                 the specified location\n"
 631       "++address1-address2       all locations starting at address1 up to\n"
 632       "++++++++                  and including address2\n"
 633       "++address/length          all location starting at address up to\n"
 634       "++++++++                  but not including address+length\n"
 635       "++STATE                   all registers in the device\n"
 636       "++ALL                     all locations in the unit\n"
 637       "++$                       the last value displayed by an EXAMINE\n"
 638       "++++++++                  command interpreted as an address\n"
 639       "3Switches\n"
 640       "4Formatting Control\n"
 641       " Switches can be used to control the format of the displayed information:\n\n"
 642        /***************** 80 character line width template *************************/
 643       "5-a\n"
 644       " display as ASCII\n"
 645       "5-c\n"
 646       " display as character string\n"
 647       "5-m\n"
 648       " display as instruction mnemonics\n"
 649       "5-o\n"
 650       " display as octal\n"
 651       "5-d\n"
 652       " display as decimal\n"
 653       "5-h\n"
 654       " display as hexadecimal\n\n"
 655       "3Examples\n"
 656       "++ex 1000-1100                examine 1000 to 1100\n"
 657       "++de PC 1040                  set PC to 1040\n"
 658       "++ie 40-50                    interactively examine 40:50\n"
 659       "++ie >1000 40-50              interactively examine the subset\n"
 660       "+++++++++                     of locations 40:50 that are >1000\n"
 661       "++ex rx0 50060                examine 50060, RX unit 0\n"
 662       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\n"
 663       "++de all 0                    set main memory to 0\n"
 664       "++de &77>0 0                  set all addresses whose low order\n"
 665       "+++++++++                     bits are non-zero to 0\n"
 666       "++ex -m @memdump.txt 0-7777   dump memory to file\n\n"
 667       " * **NOTE**: To terminate an interactive command, simply type any bad value\n"
 668       "           (*e.g.* `XYZ`) when input is requested.\n"
 669 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
 670        /***************** 80 character line width template *************************/
 671       "2Evaluating Instructions\n"
 672       " The `EVAL` command evaluates a symbolic expression and returns the\n"
 673       " equivalent numeric value.\n\n"
 674        /***************** 80 character line width template *************************/
 675       "2Running A Simulated Program\n"
 676 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
 677       "3RUN\n"
 678       " The `RUN` command (*abbreviated* `RU`) resets all devices, deposits its\n"
 679       " argument, if given, in the PC (program counter), and starts execution.\n"
 680       " If no argument is given execution starts at the current PC.\n"
 681 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
 682       "3GO\n"
 683       " The `GO` command does *not* reset devices, deposits its argument (if\n"
 684       " given) in the PC, and starts execution.  If no argument is given,\n"
 685       " execution starts at the current PC (program counter).\n"
 686 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program Continuing_Execution"
 687       "3Continuing Execution\n"
 688       " The `CONTINUE` command (*abbreviated* `CONT` or `CO`) resumes execution\n"
 689       " (if execution was stopped, possibly due to hitting a breakpoint) at the\n"
 690       " current program counter without resetting any devices.\n"
 691 #define HLP_STEP        "*Commands Running_A_Simulated_Program Step_Execution"
 692       "3Step Execution\n"
 693       " The `STEP` command (*abbreviated* `S`) resumes execution at the current\n"
 694       " PC for the number of instructions given by its argument.  If no argument\n"
 695       " is supplied, one instruction is executed.\n"
 696       "4Switches\n"
 697       "5`-T`\n"
 698       " If the `STEP` command is invoked with the \"`-T`\" switch, the step\n"
 699       " command will cause execution to run for *microseconds* rather than\n"
 700       " instructions.\n"
 701 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
 702       "3NEXT\n"
 703       " The `NEXT` command (*abbreviated* `N`) resumes execution at the current PC\n"
 704       " for one instruction, attempting to execute *through* subroutine calls.\n"
 705       " If the next instruction to be executed is *not* a subroutine call, then\n"
 706       " one instruction is executed.\n"
 707 #define HLP_BOOT        "*Commands Running_A_Simulated_Program Booting_the_system"
 708       "3Booting the system\n"
 709       " The `BOOT` command (*abbreviated* `BO`) resets all devices and bootstraps\n"
 710       " the device and unit given by its argument. If no unit is supplied,\n"
 711       " unit `0` is bootstrapped.  The specified unit must be `ATTACH`'ed.\n\n"
 712       " When booting Multics, the boot device should always be `iom0`.\n"
 713       " Assuming a tape is attached to the `tape0` device, it will be bootstrapped\n"
 714       " into memory and the system will transfer control to the boot record.\n\n"
 715       " **Example**\n\n"
 716       "++; Boot Multics using iom0\n"
 717       "++boot iom0\n\n"
 718        /***************** 80 character line width template *************************/
 719       "2Stopping The Simulator\n"
 720       " The simulator runs until the simulated hardware encounters an error, or\n"
 721       " until the user forces a stop condition.\n"
 722       "3Simulator Detected Stop Conditions\n"
 723       " These simulator-detected conditions stop simulation:\n\n"
 724       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\n\n"
 725       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\n"
 726       "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
 727       "+++usually stops.\n\n"
 728       "++-  Processor condition.  Certain processor conditions can stop\n"
 729       "+++the simulation.\n"
 730       "3User Specified Stop Conditions\n"
 731       " Typing the interrupt character stops simulation.  The interrupt character\n"
 732       " is defined by the `WRU` (*Where aRe yoU*) console option, and is initially\n"
 733       " set to `005` (`^E`).\n\n"
 734        /***************** 80 character line width template *************************/
 735 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 736 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 737       "4Breakpoints\n"
 738       " The simulator offers breakpoint capability for debugging. Users may define\n"
 739       " breakpoints of different types, identified by letter (for example, `E`\n"
 740       " for *execution*, `R` for *read*, `W` for *write*, etc).\n\n"
 741       " Associated with each breakpoint is a count and, optionally, one or more\n"
 742       " actions.  Each time a breakpoint occurs, the associated count\n"
 743       " is *decremented*.  If the count is less than or equal to `0`, the breakpoint\n"
 744       " occurs; otherwise, it is deferred.  When the breakpoint occurs, any\n"
 745       " optional actions are automatically executed.\n\n"
 746       " A breakpoint is set by the `BREAK` (or `SET BREAK`) command:\n\n"
 747       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
 748       " If no type is specified, the default breakpoint type (`E`, *execution*) is\n"
 749       " used.  If no address range is specified, the current PC is used.  As\n"
 750       " with `EXAMINE` and `DEPOSIT`, an address range may be a single address, a\n"
 751       " range of addresses low-high, or a relative range of address/length.\n"
 752        /***************** 80 character line width template *************************/
 753       "5Displaying Breakpoints\n"
 754       " Currently set breakpoints can be displayed with the `SHOW BREAK` command:\n\n"
 755       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
 756       " Locations with breakpoints of the specified type are displayed.\n\n"
 757       " The \"`-C`\" switch displays the selected breakpoint(s) formatted as\n"
 758       " commands which may be subsequently used to establish the same\n"
 759       " breakpoint(s).\n\n"
 760       "5Removing Breakpoints\n"
 761       " Breakpoints can be cleared by the `NOBREAK` or the `SET NOBREAK` commands.\n"
 762       "5Examples\n"
 763       " The following examples illustrate breakpoint usage:\n\n"
 764       "++BREAK                      set E break at current PC\n"
 765       "++BREAK -e 200               set E break at 200\n"
 766       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\n"
 767       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\n"
 768       "+++++++++D MQ 0\n"
 769       "++BREAK 100;                 delete action on break at 100\n\n"
 770        /***************** 80 character line width template *************************/
 771       "2Connecting and Disconnecting Devices\n"
 772       " Units are simulated as files on the host file system.  Before using any\n"
 773       " simulated unit, the user must specify the file to be accessed by that unit.\n"
 774 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices Attaching_devices"
 775       "3Attaching devices\n"
 776       " The `ATTACH` (*abbreviation* `AT`) command associates a unit and a file:\n\n"
 777       "++ATTACH <unit> <filename>\n\n"
 778       " Some devices have more detailed or specific help available with:\n\n"
 779       "++HELP <device> ATTACH\n\n"
 780       "4Switches\n"
 781       "5-n\n"
 782       " If the \"`-n`\" switch is specified when `ATTACH` is executed, a new\n"
 783       " file will be created when the filename specified does not exist, or an\n"
 784       " existing file will have it's size truncated to zero, and an appropriate\n"
 785       " message is printed.\n"
 786       "5-e\n"
 787       " If the file does not exist, and the \"`-e`\" switch *was not* specified,\n"
 788       " a new file is created, and an appropriate message is printed.  If\n"
 789       " the \"`-e`\" switch *was* specified, a new file is *not* created, and an\n"
 790       " error message is printed.\n"
 791       "5-r\n"
 792       " If the \"`-r`\" switch is specified, or the file is write protected by\n"
 793       " host operating system, `ATTACH` tries to open the file in read only mode.\n"
 794       " If the file does not exist, or the unit does not support read only\n"
 795       " operation, an error occurs.  Input-only devices, such as card readers, or\n"
 796       " storage devices with write locking switches, such as disks or tapes,\n"
 797       " support read only operation - other devices do not.  If a file is\n"
 798       " attached read only, its contents can be examined but not modified.\n"
 799       "5-q\n"
 800       " If the \"`-q`\" switch is specified when creating a new file (\"`-n`\")\n"
 801       " or opening one read only (\"`-r`\"), the message announcing this fact\n"
 802       " is suppressed.\n"
 803       "5-f\n"
 804       " For simulated magnetic tapes, the `ATTACH` command can specify the format\n"
 805       " of the attached tape image file:\n\n"
 806       "++ATTACH -f <tape_unit> <format> <filename>\n\n"
 807       " * The currently supported magnetic tape image file formats are:\n\n"
 808       " |                  |                                                      |\n"
 809       " | ----------------:|:---------------------------------------------------- |\n"
 810       " | \"**`SIMH`**\"   | The **SIMH** / **DPS8M** native portable tape format |\n"
 811       " | \"**`E11`**\"    | The *D Bit* **Ersatz-11** simulator format           |\n"
 812       " | \"**`TPC`**\"    | The **TPC** format (*used by _SIMH_ prior to V2.3*)  |\n"
 813       " | \"**`P7B`**\"    | The **Paul Pierce** `7`-track tape archive format    |\n\n"
 814        /***************** 80 character line width template *************************/
 815       " * The default tape format can also be specified with the `SET` command\n"
 816       " prior to using the `ATTACH` command:\n\n"
 817       "++SET <tape_unit> FORMAT=<format>\n"
 818       "++ATTACH <tape_unit> <filename>\n\n"
 819       " * The format of a currently attached tape image can be displayed with\n"
 820       "   the `SHOW FORMAT` command:\n\n"
 821       "++SHOW <unit> FORMAT\n\n"
 822       " **Examples**\n\n"
 823       " The following example illustrates common `ATTACH` usage:\n"
 824       "++; Associate the tape image file \"12.7MULTICS.tap\" with the tape0 unit\n"
 825       "++; in read-only mode, where tape0 corresponds to the first tape device.\n"
 826       "++ATTACH -r tape0 12.7MULTICS.tap\n\n"
 827       "++; Associate the disk image file \"root.dsk\" with the disk0 unit.\n"
 828       "++; The disk0 unit corresponds to the first disk device.\n"
 829       "++ATTACH disk0 root.dsk\n\n"
 830        /***************** 80 character line width template *************************/
 831 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices Detaching_devices"
 832       "3Detaching devices\n"
 833       " The `DETACH` (*abbreviation* `DET`) command breaks the association between\n"
 834       " a unit and its backing file or device:\n\n"
 835       "++DETACH ALL             Detach all units\n"
 836       "++DETACH <unit>          Detach specified unit\n\n"
 837       " * **NOTE:** The `EXIT` command performs an automatic `DETACH ALL`.\n"
 838 #define HLP_SET         "*Commands SET"
 839       "2SET\n"
 840        /***************** 80 character line width template *************************/
 841 #define HLP_SET_LOG    "*Commands SET Logging"
 842       "3Logging\n"
 843       " Interactions with the simulator session can be recorded to a log file.\n\n"
 844       "+SET LOG log_file            Specify the log destination\n"
 845       "++++++++                     (STDOUT, DEBUG, or filename)\n"
 846       "+SET NOLOG                   Disables any currently active logging\n"
 847       "4Switches\n"
 848       "5`-N`\n"
 849       " By default, log output is written at the *end* of the specified log file.\n"
 850       " A new log file can created if the \"`-N`\" switch is used on the command\n"
 851       " line.\n\n"
 852       "5`-B`\n"
 853       " By default, log output is written in *text* mode.  The log file can be\n"
 854       " opened for *binary* mode writing if the \"`-B`\" switch is used on the\n"
 855       " command line.\n"
 856 #define HLP_SET_DEBUG  "*Commands SET Debug_Messages"
 857        /***************** 80 character line width template *************************/
 858       "3Debug Messages\n"
 859       "+SET DEBUG debug_file        Specify the debug destination\n"
 860       "++++++++                     (STDOUT, STDERR, LOG, or filename)\n"
 861       "+SET NODEBUG                 Disables any currently active debug output\n"
 862       "4Switches\n"
 863       " Debug message output contains a timestamp which indicates the number of\n"
 864       " simulated instructions which have been executed prior to the debug event.\n\n"
 865       " Debug message output can be enhanced to contain additional, potentially\n"
 866       " useful information.\n\n\n"
 867       " **NOTE**: If neither \"`-T`\" or \"`-A`\" is specified, \"`-T`\" is implied.\n"
 868       "5-T\n"
 869       " The \"`-T`\" switch causes debug output to contain a time of day displayed\n"
 870       " as `hh:mm:ss.msec`.\n"
 871       "5-A\n"
 872       " The \"`-A`\" switch causes debug output to contain a time of day displayed\n"
 873       " as `seconds.msec`.\n"
 874       "5-R\n"
 875       " The \"`-R`\" switch causes timing to be relative to the start of debugging.\n"
 876       "5-P\n"
 877       " The \"`-P`\" switch adds the output of the PC (program counter) to each\n"
 878       " debug message.\n"
 879       "5-N\n"
 880       " The \"`-N`\" switch causes a new (empty) file to be written to.\n"
 881       " (The default is to append to an existing debug log file).\n"
 882       "5-D\n"
 883       " The \"`-D`\" switch causes data blob output to also display the data\n"
 884       " as **`RADIX-50`** characters.\n"
 885       "5-E\n"
 886       " The \"`-E`\" switch causes data blob output to also display the data\n"
 887       " as \"**EBCDIC**\" characters.\n"
 888        /***************** 80 character line width template *************************/
 889 #define HLP_SET_ENVIRON "*Commands SET Environment_Variables"
 890       "3Environment Variables\n"
 891       "+SET ENVIRONMENT NAME=val    Set environment variable\n"
 892       "+SET ENVIRONMENT NAME        Clear environment variable\n"
 893 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
 894       "3Command Status Trap Dispatching\n"
 895       "+SET ON                      Enables error checking command execution\n"
 896       "+SET NOON                    Disables error checking command execution\n"
 897       "+SET ON INHERIT              Enables inheritance of ON state and actions\n"
 898       "+SET ON NOINHERIT            Disables inheritance of ON state and actions\n"
 899 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
 900       "3Command Execution Display\n"
 901       "+SET VERIFY                  Enables display of processed script commands\n"
 902       "+SET VERBOSE                 Enables display of processed script commands\n"
 903       "+SET NOVERIFY                Disables display of processed script commands\n"
 904       "+SET NOVERBOSE               Disables display of processed script commands\n"
 905 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
 906       "3Command Error Status Display\n"
 907       "+SET MESSAGE                 Re-enables display of script error messages\n"
 908       "+SET NOMESSAGE               Disables display of script error messages\n"
 909 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
 910       "3Command Output Display\n"
 911       "+SET QUIET                   Disables suppression of some messages\n"
 912       "+SET NOQUIET                 Re-enables suppression of some messages\n"
 913 #define HLP_SET_LOCALOPC "*Commands SET Local_Operator_Console"
 914       "3Local Operator Console\n"
 915       "+SET LOCALOPC                Enables local operator console\n"
 916       "+SET NOLOCALOPC              Disables local operator console\n"
 917 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
 918       "3Command Prompt\n"
 919       "+SET PROMPT \"string\"         Sets an alternate simulator prompt string\n"
 920       "3Device and Unit Settings\n"
 921       "+SET <dev> OCT|DEC|HEX       Set device display radix\n"
 922       "+SET <dev> ENABLED           Enable device\n"
 923       "+SET <dev> DISABLED          Disable device\n"
 924       "+SET <dev> DEBUG{=arg}       Set device debug flags\n"
 925       "+SET <dev> NODEBUG={arg}     Clear device debug flags\n"
 926       "+SET <dev> arg{,arg...}      Set device parameters\n"
 927       "+SET <unit> ENABLED          Enable unit\n"
 928       "+SET <unit> DISABLED         Disable unit\n"
 929       "+SET <unit> arg{,arg...}     Set unit parameters\n"
 930       "+HELP <dev> SET              Displays any device specific SET commands\n"
 931       " \n\n"
 932       " See the Omnibus documentation for a complete SET command reference.\n"
 933        /***************** 80 character line width template *************************/
 934 #define HLP_SHOW        "*Commands SHOW"
 935       "2SHOW\n"
 936       "+SH{OW} B{UILDINFO}               Show build-time compilation information\n"
 937       "+SH{OW} CL{OCKS}                  Show wall clock and timer information\n"
 938       "+SH{OW} C{ONFIGURATION}           Show simulator configuration\n"
 939       "+SH{OW} D{EFAULT_BASE_SYSTEM}     Show default base system script\n"
 940       "+SH{OW} DEV{ICES}                 Show devices\n"
 941       "+SH{OW} M{ODIFIERS}               Show SET commands for all devices\n"
 942       "+SH{OW} O{N}                      Show ON condition actions\n"
 943       "+SH{OW} P{ROM}                    Show CPU ID PROM initialization data\n"
 944       "+SH{OW} Q{UEUE}                   Show event queue\n"
 945       "+SH{OW} S{HOW}                    Show SHOW commands for all devices\n"
 946       "+SH{OW} T{IME}                    Show simulated timer\n"
 947       "+SH{OW} VE{RSION}                 Show simulator version\n"
 948       "+H{ELP} <dev> SHOW                Show device-specific SHOW commands\n"
 949       "+SH{OW} <dev> {arg,...}           Show device parameters\n"
 950       "+SH{OW} <dev> DEBUG               Show device debug flags\n"
 951       "+SH{OW} <dev> MODIFIERS           Show device modifiers\n"
 952       "+SH{OW} <dev> RADIX               Show device display radix\n"
 953       "+SH{OW} <dev> SHOW                Show device SHOW commands\n"
 954       "+SH{OW} <unit> {arg,...}          Show unit parameters\n\n"
 955       " See the Omnibus documentation for a complete SHOW command reference.\n\n"
 956 #define HLP_SHOW_CONFIG         "*Commands SHOW"
 957 #define HLP_SHOW_DEVICES        "*Commands SHOW"
 958 #define HLP_SHOW_FEATURES       "*Commands SHOW"
 959 #define HLP_SHOW_QUEUE          "*Commands SHOW"
 960 #define HLP_SHOW_TIME           "*Commands SHOW"
 961 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
 962 #define HLP_SHOW_NAMES          "*Commands SHOW"
 963 #define HLP_SHOW_SHOW           "*Commands SHOW"
 964 #define HLP_SHOW_VERSION        "*Commands SHOW"
 965 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
 966 #define HLP_SHOW_PROM           "*Commands SHOW"
 967 #define HLP_SHOW_DBS            "*Commands SHOW"
 968 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
 969 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
 970 #define HLP_SHOW_REMOTE         "*Commands SHOW"
 971 #define HLP_SHOW_BREAK          "*Commands SHOW"
 972 #define HLP_SHOW_LOG            "*Commands SHOW"
 973 #define HLP_SHOW_DEBUG          "*Commands SHOW"
 974 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
 975 #define HLP_SHOW_ON             "*Commands SHOW"
 976 #define HLP_SHOW_SEND           "*Commands SHOW"
 977 #define HLP_SHOW_EXPECT         "*Commands SHOW"
 978 #define HLP_HELP                "*Commands HELP"
 979        /***************** 80 character line width template *************************/
 980       "2HELP\n"
 981       "+H{ELP}                      Show this message\n"
 982       "+H{ELP} <command>            Show help for command\n"
 983       "+H{ELP} <dev>                Show help for device\n"
 984       "+H{ELP} <dev> REGISTERS      Show help for device register variables\n"
 985       "+H{ELP} <dev> ATTACH         Show help for device specific ATTACH command\n"
 986       "+H{ELP} <dev> SET            Show help for device specific SET commands\n"
 987       "+H{ELP} <dev> SHOW           Show help for device specific SHOW commands\n"
 988       "+H{ELP} <dev> <command>      Show help for device specific <command> command\n"
 989        /***************** 80 character line width template *************************/
 990       "2Altering The Simulated Configuration\n"
 991       " The \"SET <device> DISABLED\" command removes a device from the configuration.\n"
 992       " A `DISABLED` device is invisible to running programs.  The device can still\n"
 993       " be `RESET`, but it cannot be `ATTACH`ed, `DETACH`ed, or `BOOT`ed.\n\n"
 994       " The \"SET <device> ENABLED\" command restores a disabled device to a\n"
 995       " configuration.\n\n"
 996       " Most multi-unit devices allow units to be enabled or disabled:\n\n"
 997       "++SET <unit> ENABLED\n"
 998       "++SET <unit> DISABLED\n\n"
 999       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
1000        /***************** 80 character line width template *************************/
1001 #define HLP_DO          "*Commands Executing_Command_Files Processing_Command_Files"
1002       "2Executing Command Files\n"
1003       "3Processing Command Files\n"
1004       " The simulator can invoke another script file with the \"`DO`\" command:\n\n"
1005       "++DO <filename> {arguments...}       execute commands in specified file\n\n"
1006       " The \"`DO`\" command allows command files to contain substitutable\n"
1007       " arguments. The string \"`%%n`\", where \"`n`\" is a number\n"
1008       " between \"`1`\" and \"`9`\", is replaced with argument \"`n`\" from\n"
1009       " the \"`DO`\" command line. (*i.e.* \"`%%0`\", \"`%%1`\", \"`%%2`\", etc.).\n"
1010       " The string \"`%%0`\" is replaced with \"<`filename`>\".\n The\n"
1011       " sequences \"`\\%%`\" and \"`\\\\`\" are replaced with the literal\n"
1012       " characters \"`%%`\" and \"`\\`\", respectively. Arguments with spaces must\n"
1013       " be enclosed in matching single or double quotation marks.\n\n"
1014       " * **NOTE**: Nested \"`DO`\" commands are supported, up to ten invocations\n"
1015       " deep.\n\n"
1016       "4Switches\n"
1017       "5`-v`\n\n"
1018       " If the switch \"`-v`\" is specified, commands in the command file are\n"
1019       " echoed *before* they are executed.\n\n"
1020       "5`-e`\n\n"
1021       " If the switch \"`-e`\" is specified, command processing (including nested\n"
1022       " command invocations) will be aborted if any command error is encountered.\n"
1023       " (A simulation stop **never** aborts processing; use `ASSERT` to catch\n"
1024       " unexpected stops.) Without this switch, all errors except `ASSERT` failures\n"
1025       " will be ignored, and command processing will continue.\n\n"
1026       "5`-o`\n\n"
1027       " If the switch \"`-o`\" is specified, the `ON` conditions and actions from\n"
1028       " the calling command file will be inherited by the command file being\n"
1029       " invoked.\n"
1030       "5`-q`\n\n"
1031       " If the switch \"`-q`\" is specified, *quiet mode* will be explicitly\n"
1032       " enabled for the called command file, otherwise the *quiet mode* setting\n"
1033       " is inherited from the calling context.\n"
1034        /***************** 80 character line width template *************************/
1035 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
1036       "3GOTO\n"
1037       " Commands in a command file execute in sequence until either an error\n"
1038       " trap occurs (when a command completes with an error status), or when an\n"
1039       " explicit request is made to start command execution elsewhere with\n"
1040       " the `GOTO` command:\n\n"
1041       "++GOTO <label>\n\n"
1042       " * Labels are lines in a command file which the first non-whitespace\n"
1043       " character is a \"`:`\".\n"
1044       " * The target of a `GOTO` is the first matching label in the current `DO`\n"
1045       " command file which is encountered.\n\n"
1046       " **Example**\n\n"
1047       " The following example illustrates usage of the `GOTO` command (by\n"
1048       " creating an infinite loop):\n\n"
1049       "++:Label\n"
1050       "++:: This is a loop.\n"
1051       "++GOTO Label\n\n"
1052 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
1053        /***************** 80 character line width template *************************/
1054       "3RETURN\n"
1055       " The `RETURN` command causes the current procedure call to be restored to\n"
1056       " the calling context, possibly returning a specific return status.\n"
1057       " If no return status is specified, the return status from the last command\n"
1058       " executed will be returned.  The calling context may have `ON` traps defined\n"
1059       " which may redirect command flow in that context.\n\n"
1060       "++RETURN                 return from command file with last command status\n"
1061       "++RETURN {-Q} <status>   return from command file with specific status\n\n"
1062       " * The status return can be any numeric value or one of the standard SCPE_\n"
1063       " condition names.\n\n"
1064       " * The \"`-Q`\" switch on the `RETURN` command will cause the specified\n"
1065       " status to be returned, but normal error status message printing to be\n"
1066       " suppressed.\n\n"
1067       " **Condition Names**\n\n"
1068       " The available standard SCPE_ condition names and their meanings are:\n\n"
1069       " | Name    | Meaning                         | Name    | Meaning                             |\n"
1070       " | ------- | --------------------------------| ------- | ----------------------------------- |\n"
1071       " | NXM     | Address space exceeded          | UNATT   | Unit not attached                   |\n"
1072       " | IOERR   | I/O error                       | CSUM    | Checksum error                      |\n"
1073       " | FMT     | Format error                    | NOATT   | Unit not attachable                 |\n"
1074       " | OPENERR | File open error                 | MEM     | Memory exhausted                    |\n"
1075       " | ARG     | Invalid argument                | STEP    | Step expired                        |\n"
1076       " | UNK     | Unknown command                 | RO      | Read only argument                  |\n"
1077       " | INCOMP  | Command not completed           | STOP    | Simulation stopped                  |\n"
1078       " | EXIT    | Goodbye                         | TTIERR  | Console input I/O error             |\n"
1079       " | TTOERR  | Console output I/O error        | EOF     | End of file                         |\n"
1080       " | REL     | Relocation error                | NOPARAM | No settable parameters              |\n"
1081       " | ALATT   | Unit already attached           | TIMER   | Hardware timer error                |\n"
1082       " | SIGERR  | Signal handler setup error      | TTYERR  | Console terminal setup error        |\n"
1083       " | NOFNC   | Command not allowed             | UDIS    | Unit disabled                       |\n"
1084       " | NORO    | Read only operation not allowed | INVSW   | Invalid switch                      |\n"
1085       " | MISVAL  | Missing value                   | 2FARG   | Too few arguments                   |\n"
1086       " | 2MARG   | Too many arguments              | NXDEV   | Non-existent device                 |\n"
1087       " | NXUN    | Non-existent unit               | NXREG   | Non-existent register               |\n"
1088       " | NXPAR   | Non-existent parameter          | NEST    | Nested DO command limit exceeded    |\n"
1089       " | IERR    | Internal error                  | MTRLNT  | Invalid magtape record length       |\n"
1090       " | LOST    | Console Telnet connection lost  | TTMO    | Console Telnet connection timed out |\n"
1091       " | STALL   | Console Telnet output stall     | AFAIL   | Assertion failed                    |\n"
1092       " | INVREM  | Invalid remote console command  |         |                                     |\n"
1093       "\n\n"
1094 #define HLP_SHIFT       "*Commands Executing_Command_Files Shift_Parameters"
1095       "3Shift Parameters\n"
1096       " Shift the command files positional parameters\n"
1097 #define HLP_CALL        "*Commands Executing_Command_Files Call_a_subroutine"
1098       "3Call a subroutine\n"
1099       " Control can be transferred to a labeled subroutine using `CALL`.\n\n"
1100       " **Example**\n\n"
1101       "++CALL routine\n"
1102       "++BYE\n"
1103       "++\n"
1104       "++:routine\n"
1105       "++ECHO routine called\n"
1106       "++RETURN\n\n"
1107 #define HLP_ON          "*Commands Executing_Command_Files ON"
1108       "3ON\n"
1109       " The `ON` command performs actions after a condition, or clears a condition.\n"
1110       "++ON <condition> <action>  Perform action after condition\n"
1111       "++ON <condition>           Clears action of specified condition\n"
1112 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1113 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1114        /***************** 80 character line width template *************************/
1115       "3PROCEED or IGNORE\n"
1116       " The `PROCEED` (or `IGNORE`) command does nothing.  It is potentially\n"
1117       " useful as a placeholder for any `ON` action condition that should be\n"
1118       " explicitly ignored, allowing command file execution to continue without\n"
1119       " taking any specific action.\n"
1120 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1121        /***************** 80 character line width template *************************/
1122       "3Displaying Arbitrary Text\n"
1123       " The `ECHO` command is a useful way of annotating command files.  `ECHO`\n"
1124       " prints out its arguments to the console (and to any applicable log file):\n\n"
1125       "++ECHO <string>      Output string to console\n\n"
1126       " **NOTE**: If no arguments are specified, `ECHO` prints a blank line.\n"
1127       " This may be used to provide spacing for console messages or log file\n"
1128       " output.\n"
1129        /***************** 80 character line width template *************************/
1130 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Assertions"
1131       "3Testing Assertions\n"
1132       " The `ASSERT` command tests a simulator state condition and halts command\n"
1133       " file execution if the condition is false:\n\n"
1134       "++ASSERT <Simulator State Expressions>\n\n"
1135       " * If the indicated expression evaluates to false, the command completes\n"
1136       " with an `AFAIL` condition.  By default, when a command file encounters a\n"
1137       " command which returns the `AFAIL` condition, it will exit the running\n"
1138       " command file with the `AFAIL` status to the calling command file.  This\n"
1139       " behavior can be changed with the `ON` command as well as switches to the\n"
1140       " invoking `DO` command.\n\n"
1141       " **Examples**\n\n"
1142       " The command file below might be used to bootstrap a hypothetical system\n"
1143       " that halts after the initial load from disk. The `ASSERT` command can then\n"
1144       " be used to confirm that the load completed successfully by examining the\n"
1145       " CPU's \"`A`\" register for the expected value:\n\n"
1146       "++; Example INI file\n"
1147       "++BOOT\n"
1148       "++; A register contains error code; 0 = good boot\n"
1149       "++ASSERT A=0\n"
1150       "++RUN\n\n"
1151        /***************** 80 character line width template *************************/
1152       " * In the above example, if the \"`A`\" register is *not* `0`,\n"
1153       " the \"`ASSERT A=0`\" command will be displayed to the user, and the\n"
1154       " command file will be aborted with an \"`Assertion failed`\" message.\n"
1155       " Otherwise, the command file will continue to bring up the system.\n\n"
1156       " * See the **`IF`** command documentation for more information and details\n"
1157       " regarding simulator state expressions.\n\n"
1158 #define HLP_IF          "*Commands Executing_Command_Files Testing_Conditions"
1159       "3Testing Conditions\n"
1160       " The `IF` command tests a simulator state condition and executes additional\n"
1161       " commands if the condition is true:\n\n"
1162       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommand}...\n\n"
1163       " **Examples**\n\n"
1164       " The command file below might be used to bootstrap a hypothetical system\n"
1165       " that halts after the initial load from disk. The `IF` command can then\n"
1166       " be used to confirm that the load completed successfully by examining the\n"
1167       " CPU's \"`A`\" register for an expected value:\n\n"
1168       "++; Example INI file\n"
1169       "++BOOT\n"
1170       "++; A register contains error code; 0 = good boot\n"
1171       "++IF NOT A=0 echo Boot failed - Failure Code ; EX A; exit AFAIL\n"
1172       "++RUN\n\n"
1173        /***************** 80 character line width template *************************/
1174       " * In the above example, if the \"`A`\" register is *not* `0`, the\n"
1175       " message \"`Boot failed - Failure Code `\" will be displayed, the contents\n"
1176       " of the \"`A`\" register will be displayed, and the command file will be\n"
1177       " aborted with an \"`Assertion failed`\" message.  Otherwise, the command\n"
1178       " file will continue to bring up the system.\n"
1179       "4Conditional Expressions\n"
1180       " The `IF` and `ASSERT` commands evaluate the following two different forms\n"
1181       " of conditional expressions.\n\n"
1182       "5Simulator State Expressions\n"
1183       "  &nbsp;\n \n"
1184       " The values of simulator registers can be evaluated with:\n\n"
1185       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1186       " * If \"<`dev`>\" is not specified, `CPU` is assumed.  \"<`reg`>\" is a\n"
1187       " register belonging to the indicated device.\n"
1188       " * The \"<`addr`>\" is an address in the address space of the indicated\n"
1189       " device.\n"
1190       " * The \"<`conditional-op`>\" and optional \"<`logical-op`>\" are\n"
1191       " the same as those used for \"search specifiers\" by the `EXAMINE` and\n"
1192       " `DEPOSIT` commands.\n"
1193       " The \"<`value`>\" is expressed in the radix specified for \"<`reg`>\",\n"
1194       " not in the radix for the device when referencing a register; when an\n"
1195       " address is referenced the device radix is used as the default.\n\n"
1196       " * If \"<`logical-op`>\" and \"<`value`>\" are specified, the target\n"
1197       " register value is first altered as indicated.  The result is then compared\n"
1198       " to the \"<`value`>\" via the \"<`conditional-op`>\".\n"
1199       " * * If the result is *true*, the command(s) are executed before proceeding\n"
1200       " to the next line in the command file.\n"
1201       " * * If the result is *false*, the next command in the command file is\n"
1202       " processed.\n\n"
1203       "5String Comparison Expressions\n"
1204       "  &nbsp;\n \n"
1205       " String Values can be compared with:\n\n"
1206       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1207       " * The \"`-i`\" switch, if present, causes a comparison to be case\n"
1208       "   insensitive.\n"
1209       " * The \"<`string1`>\" and \"<`string2`>\" arguments are quoted string\n"
1210       "   values which may have environment variables substituted as desired.\n"
1211       " * The \"<`compare-op`>\" may be one of:\n\n"
1212       " |                |                        |\n"
1213       " | --------------:|:---------------------- |\n"
1214       " | \"**`==`**\"     |  equal                 |\n"
1215       " | \"**`EQU`**\"    |  equal                 |\n"
1216       " | \"**`!=`**\"     |  not equal             |\n"
1217       " | \"**`NEQ`**\"    |  not equal             |\n"
1218       " | \"**<**\"        |  less than             |\n"
1219       " | \"**`LSS`**\"    |  less than             |\n"
1220       " | \"**<=**\"       |  less than or equal    |\n"
1221       " | \"**`LEQ`**\"    |  less than or equal    |\n"
1222       " | \"**>**\"        |  greater than          |\n"
1223       " | \"**`GTR`**\"    |  greater than          |\n"
1224       " | \"**>=**\"       |  greater than or equal |\n"
1225       " | \"**`GEQ`**\"    |  greater than or equal |\n"
1226       " * **NOTE**: Comparisons are *generic*.  This means that if\n"
1227       " both \"<`string1`>\" and \"<`string2`>\" are comprised of all numeric\n"
1228       " digits, then the strings are converted to numbers and a numeric\n"
1229       " comparison is performed.  For example, the comparison\n"
1230       " '`\"+1\"` `EQU` `\"1\"`' evaluates to *true*.\n"
1231        /***************** 80 character line width template *************************/
1232 #define HLP_EXIT        "*Commands Exiting_the_Simulator"
1233       "2Exiting the Simulator\n"
1234       " The `EXIT` command (*synonyms* `QUIT` *and* `BYE`) exits the simulator,\n"
1235       " returning control to the host operating system.\n"
1236        /***************** 80 character line width template *************************/
1237 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1238       "2Executing System Commands\n"
1239       " * The simulator can execute host operating system commands with\n"
1240       " the \"`!`\" (*spawn*) command.\n\n"
1241       " |                         |                                             |\n"
1242       " |:----------------------- |:------------------------------------------- |\n"
1243       " | \"**`!`**\"             | Spawn the hosts default command interpreter |\n"
1244       " | \"**`!`** <`command`>\" | Execute the host operating system `command` |\n\n"
1245       " * **NOTE**: The *exit status* from the command which was executed is set\n"
1246       " as the *command completion status* for the \"`!`\" command.  This may\n"
1247       " influence any enabled `ON` condition traps.\n" ;
1248        /***************** 80 character line width template *************************/
1249 
1250 static CTAB cmd_table[] = {
1251     { "RESET",    &reset_cmd,  0,         HLP_RESET     },
1252     { "EXAMINE",  &exdep_cmd,  EX_E,      HLP_EXAMINE   },
1253     { "IEXAMINE", &exdep_cmd,  EX_E+EX_I, HLP_IEXAMINE  },
1254     { "DEPOSIT",  &exdep_cmd,  EX_D,      HLP_DEPOSIT   },
1255     { "IDEPOSIT", &exdep_cmd,  EX_D+EX_I, HLP_IDEPOSIT  },
1256     { "EVALUATE", &eval_cmd,   0,         HLP_EVALUATE  },
1257     { "RUN",      &run_cmd,    RU_RUN,    HLP_RUN,      NULL, &run_cmd_message },
1258     { "GO",       &run_cmd,    RU_GO,     HLP_GO,       NULL, &run_cmd_message },
1259     { "STEP",     &run_cmd,    RU_STEP,   HLP_STEP,     NULL, &run_cmd_message },
1260     { "NEXT",     &run_cmd,    RU_NEXT,   HLP_NEXT,     NULL, &run_cmd_message },
1261     { "CONTINUE", &run_cmd,    RU_CONT,   HLP_CONTINUE, NULL, &run_cmd_message },
1262     { "BOOT",     &run_cmd,    RU_BOOT,   HLP_BOOT,     NULL, &run_cmd_message },
1263     { "BREAK",    &brk_cmd,    SSH_ST,    HLP_BREAK     },
1264     { "NOBREAK",  &brk_cmd,    SSH_CL,    HLP_NOBREAK   },
1265     { "ATTACH",   &attach_cmd, 0,         HLP_ATTACH    },
1266     { "DETACH",   &detach_cmd, 0,         HLP_DETACH    },
1267     { "EXIT",     &exit_cmd,   0,         HLP_EXIT      },
1268     { "QUIT",     &exit_cmd,   0,         NULL          },
1269     { "BYE",      &exit_cmd,   0,         NULL          },
1270     { "SET",      &set_cmd,    0,         HLP_SET       },
1271     { "SHOW",     &show_cmd,   0,         HLP_SHOW      },
1272     { "DO",       &do_cmd,     1,         HLP_DO        },
1273     { "GOTO",     &goto_cmd,   1,         HLP_GOTO      },
1274     { "RETURN",   &return_cmd, 0,         HLP_RETURN    },
1275     { "SHIFT",    &shift_cmd,  0,         HLP_SHIFT     },
1276     { "CALL",     &call_cmd,   0,         HLP_CALL      },
1277     { "ON",       &on_cmd,     0,         HLP_ON        },
1278     { "IF",       &assert_cmd, 0,         HLP_IF        },
1279     { "PROCEED",  &noop_cmd,   0,         HLP_PROCEED   },
1280     { "IGNORE",   &noop_cmd,   0,         HLP_IGNORE    },
1281     { "ECHO",     &echo_cmd,   0,         HLP_ECHO      },
1282     { "ASSERT",   &assert_cmd, 1,         HLP_ASSERT    },
1283     { "SEND",     &send_cmd,   0          },            /* deprecated */
1284     { "EXPECT",   &expect_cmd, 1          },            /* deprecated */
1285     { "NOEXPECT", &expect_cmd, 0          },            /* deprecated */
1286     { "!",        &spawn_cmd,  0,         HLP_SPAWN     },
1287     { "HELP",     &help_cmd,   0,         HLP_HELP      },
1288     { NULL,       NULL,        0          }
1289     };
1290 
1291 static CTAB set_glob_tab[] = {
1292     { "CONSOLE",     &sim_set_console,        0      }, /* deprecated */
1293     { "REMOTE",      &sim_set_remote_console, 0      }, /* deprecated */
1294     { "BREAK",       &brk_cmd,                SSH_ST }, /* deprecated */
1295     { "NOBREAK",     &brk_cmd,                SSH_CL }, /* deprecated */
1296     { "TELNET",      &sim_set_telnet,         0      }, /* deprecated */
1297     { "NOTELNET",    &sim_set_notelnet,       0      }, /* deprecated */
1298     { "LOG",         &sim_set_logon,          0,     HLP_SET_LOG      },
1299     { "NOLOG",       &sim_set_logoff,         0,     HLP_SET_LOG      },
1300     { "DEBUG",       &sim_set_debon,          0,     HLP_SET_DEBUG    },
1301     { "NODEBUG",     &sim_set_deboff,         0,     HLP_SET_DEBUG    },
1302     { "ENVIRONMENT", &sim_set_environment,    1,     HLP_SET_ENVIRON  },
1303     { "ON",          &set_on,                 1,     HLP_SET_ON       },
1304     { "NOON",        &set_on,                 0,     HLP_SET_ON       },
1305     { "VERIFY",      &set_verify,             1,     HLP_SET_VERIFY   },
1306     { "VERBOSE",     &set_verify,             1,     HLP_SET_VERIFY   },
1307     { "NOVERIFY",    &set_verify,             0,     HLP_SET_VERIFY   },
1308     { "NOVERBOSE",   &set_verify,             0,     HLP_SET_VERIFY   },
1309     { "MESSAGE",     &set_message,            1,     HLP_SET_MESSAGE  },
1310     { "NOMESSAGE",   &set_message,            0,     HLP_SET_MESSAGE  },
1311     { "QUIET",       &set_quiet,              1,     HLP_SET_QUIET    },
1312     { "NOQUIET",     &set_quiet,              0,     HLP_SET_QUIET    },
1313     { "LOCALOPC",    &set_localopc,           1,     HLP_SET_LOCALOPC },
1314     { "NOLOCALOPC",  &set_localopc,           0,     HLP_SET_LOCALOPC },
1315     { "PROMPT",      &set_prompt,             0,     HLP_SET_PROMPT   },
1316     { NULL,          NULL,                    0      }
1317     };
1318 
1319 static C1TAB set_dev_tab[] = {
1320     { "OCTAL",    &set_dev_radix,   8  },
1321     { "DECIMAL",  &set_dev_radix,  10  },
1322     { "HEX",      &set_dev_radix,  16  },
1323     { "ENABLED",  &set_dev_enbdis,  1  },
1324     { "DISABLED", &set_dev_enbdis,  0  },
1325     { "DEBUG",    &set_dev_debug,   1  },
1326     { "NODEBUG",  &set_dev_debug,   0  },
1327     { NULL,       NULL,             0  }
1328     };
1329 
1330 static C1TAB set_unit_tab[] = {
1331     { "ENABLED",  &set_unit_enbdis, 1 },
1332     { "DISABLED", &set_unit_enbdis, 0 },
1333     { NULL,       NULL,             0 }
1334     };
1335 
1336 static SHTAB show_glob_tab[] = {
1337     { "CONFIGURATION",  &show_config,        0, HLP_SHOW_CONFIG    },
1338     { "DEFAULT_BASE_SYSTEM_SCRIPT",
1339            &show_default_base_system_script, 0, HLP_SHOW_DBS       },
1340     { "DEVICES",   &show_config,             1, HLP_SHOW_DEVICES   },
1341     { "FEATURES",  &show_config,             2, HLP_SHOW_FEATURES  },
1342     { "QUEUE",     &show_queue,              0, HLP_SHOW_QUEUE     },
1343     { "TIME",      &show_time,               0, HLP_SHOW_TIME      },
1344     { "MODIFIERS", &show_mod_names,          0, HLP_SHOW_MODIFIERS },
1345     { "NAMES",     &show_log_names,          0, HLP_SHOW_NAMES     },
1346     { "SHOW",      &show_show_commands,      0, HLP_SHOW_SHOW      },
1347     { "VERSION",   &show_version,            1, HLP_SHOW_VERSION   },
1348     { "BUILDINFO", &show_buildinfo,          1, HLP_SHOW_BUILDINFO },
1349     { "PROM",      &show_prom,               0, HLP_SHOW_PROM      },
1350     { "CONSOLE",   &sim_show_console,        0, HLP_SHOW_CONSOLE   },
1351     { "REMOTE",    &sim_show_remote_console, 0, HLP_SHOW_REMOTE    },
1352     { "BREAK",     &show_break,              0, HLP_SHOW_BREAK     },
1353     { "LOG",       &sim_show_log,            0, HLP_SHOW_LOG       },
1354     { "TELNET",    &sim_show_telnet,         0  },  /* deprecated */
1355     { "DEBUG",     &sim_show_debug,          0, HLP_SHOW_DEBUG     },
1356     { "CLOCKS",    &sim_show_timers,         0, HLP_SHOW_CLOCKS    },
1357     { "SEND",      &sim_show_send,           0, HLP_SHOW_SEND      },
1358     { "EXPECT",    &sim_show_expect,         0, HLP_SHOW_EXPECT    },
1359     { "ON",        &show_on,                 0, HLP_SHOW_ON        },
1360     { NULL,        NULL,                     0  }
1361     };
1362 
1363 static SHTAB show_dev_tab[] = {
1364     { "RADIX",     &show_dev_radix,         0 },
1365     { "DEBUG",     &show_dev_debug,         0 },
1366     { "MODIFIERS", &show_dev_modifiers,     0 },
1367     { "NAMES",     &show_dev_logicals,      0 },
1368     { "SHOW",      &show_dev_show_commands, 0 },
1369     { NULL,        NULL,                    0 }
1370     };
1371 
1372 static SHTAB show_unit_tab[] = {
1373     { NULL, NULL, 0 }
1374     };
1375 
1376 #if defined(_WIN32)
1377 static
1378 int setenv(const char *envname, const char *envval, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
1379 {
1380 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1381 int r;
1382 
1383 (void)sprintf(envstr, "%s=%s", envname, envval);
1384 r = _putenv(envstr);
1385 FREE(envstr);
1386 return r;
1387 }
1388 
1389 static
1390 int unsetenv(const char *envname)
     /* [previous][next][first][last][top][bottom][index][help] */
1391 {
1392 setenv(envname, "", 1);
1393 return 0;
1394 }
1395 #endif /* if defined(_WIN32) */
1396 
1397 #define XSTR_EMAXLEN 32767
1398 
1399 const char
1400 *xstrerror_l(int errnum)
     /* [previous][next][first][last][top][bottom][index][help] */
1401 {
1402   int saved = errno;
1403   const char *ret = NULL;
1404   static __thread char buf[XSTR_EMAXLEN];
1405 
1406 #if defined(__APPLE__) || defined(_AIX) || defined(__MINGW32__) || \
1407     defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1408 # if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1409   if (strerror_s(buf, sizeof(buf), errnum) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1410 # else
1411   if (strerror_r(errnum, buf, sizeof(buf)) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1412 # endif
1413 #else
1414 # if defined(__NetBSD__)
1415   locale_t loc = LC_GLOBAL_LOCALE;
1416 # else
1417   locale_t loc = uselocale((locale_t)0);
1418 # endif
1419   locale_t copy = loc;
1420   if (copy == LC_GLOBAL_LOCALE)
1421     copy = duplocale(copy);
1422 
1423   if (copy != (locale_t)0)
1424     {
1425       ret = strerror_l(errnum, copy); /*LINTOK: xstrerror_l*/
1426       if (loc == LC_GLOBAL_LOCALE)
1427         {
1428           freelocale(copy);
1429         }
1430     }
1431 #endif
1432 
1433   if (!ret)
1434     {
1435       (void)snprintf(buf, sizeof(buf), "Unknown error %d", errnum);
1436       ret = buf;
1437     }
1438 
1439   errno = saved;
1440   return ret;
1441 }
1442 
1443 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1444 
1445 /* Check if running on Rosetta 2 */
1446 
1447 #if defined(__APPLE__)
1448 int processIsTranslated(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1449 {
1450     int ret = 0;
1451     size_t size = sizeof(ret);
1452     if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1453         if (errno == ENOENT)
1454             return 0;
1455         return -1; }
1456     return ret;
1457 }
1458 #endif /* if defined(_APPLE_) */
1459 
1460 /* Substring removal hack */
1461 
1462 char *strremove(char *str, const char *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
1463 {
1464     char *p, *q, *r;
1465     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1466         size_t len = strlen(sub);
1467         while ((r = strstr(p = r + len, sub)) != NULL) {
1468             while (p < r)
1469                 *q++ = *p++;
1470         }
1471         while ((*q++ = *p++) != '\0')
1472             continue;
1473     }
1474     return str;
1475 }
1476 
1477 /* Trim whitespace */
1478 
1479 void strtrimspace (char *str_trimmed, const char *str_untrimmed)
     /* [previous][next][first][last][top][bottom][index][help] */
1480 {
1481     while (*str_untrimmed != '\0') {
1482       if(!isspace((unsigned char)*str_untrimmed)) {
1483         *str_trimmed = (char)*str_untrimmed;
1484         str_trimmed++;
1485       }
1486       str_untrimmed++;
1487     }
1488     *str_trimmed = '\0';
1489 }
1490 
1491 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1492 void allowCores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1493 {
1494   int ret;
1495   struct rlimit limit;
1496 # if defined(RLIMIT_CORE)
1497   ret = getrlimit(RLIMIT_CORE, &limit);
1498   (void)ret;
1499 #  if defined(TESTING)
1500   if (ret != 0)
1501     {
1502       sim_warn ("Failed to query core dump configuration.");
1503       return;
1504     }
1505 #  endif /* if defined(TESTING) */
1506   limit.rlim_cur = limit.rlim_max;
1507   ret = setrlimit(RLIMIT_CORE, &limit);
1508 #  if defined(TESTING)
1509   if (ret != 0)
1510     {
1511       sim_warn ("Failed to enable unlimited core dumps.");
1512       return;
1513     }
1514 #  endif /* if defined(TESTING) */
1515 # else
1516 #  if defined(TESTING)
1517   sim_warn ("Unable to query core dump configuration.");
1518 #  endif /* if defined(TESTING) */
1519 # endif /* if defined(RLIMIT_CORE) */
1520   return;
1521 }
1522 #endif
1523 
1524 #if defined(USE_DUMA)
1525 void CleanDUMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1526 {
1527   (void)fflush(stdout);
1528   DUMA_CHECKALL();
1529   (void)fflush(stderr);
1530 }
1531 # undef USE_DUMA
1532 # define USE_DUMA 1
1533 #endif /* if defined(USE_DUMA) */
1534 
1535 #if !defined(SIG_SETMASK)
1536 # undef USE_BACKTRACE
1537 #endif /* if !defined(SIG_SETMASK) */
1538 
1539 #if defined(PERF_STRIP)
1540 # undef USE_BACKTRACE
1541 #endif /* if defined(PERF_STRIP) */
1542 
1543 #if defined(USE_BACKTRACE)
1544 # include <backtrace.h>
1545 # include <backtrace-supported.h>
1546 # define BACKTRACE_SKIP 1
1547 # define BACKTRACE_MAIN "main"
1548 # undef USE_BACKTRACE
1549 # define USE_BACKTRACE 1
1550 #endif /* if defined(USE_BACKTRACE) */
1551 
1552 #if defined(BACKTRACE_SUPPORTED)
1553 # if defined(BACKTRACE_SUPPORTS_THREADS)
1554 #  if !(BACKTRACE_SUPPORTED)
1555 #   undef USE_BACKTRACE
1556 #  endif /* if !(BACKTRACE_SUPPORTED) */
1557 # else  /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1558 #  undef USE_BACKTRACE
1559 # endif /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1560 #else  /* if defined(BACKTRACE_SUPPORTED) */
1561 # undef USE_BACKTRACE
1562 #endif /* if defined(BACKTRACE_SUPPORTED) */
1563 
1564 #if defined(USE_BACKTRACE)
1565 # if defined(BACKTRACE_SUPPORTED)
1566 #  include "backtrace_func.c"
1567 # endif /* if defined(BACKTRACE_SUPPORTED) */
1568 #endif /* if defined(USE_BACKTRACE) */
1569 
1570 #if !defined(__CYGWIN__)
1571 # if !defined(__APPLE__)
1572 #  if !defined(_AIX)
1573 #   if !defined(__MINGW32__)
1574 #    if !defined(__MINGW64__)
1575 #     if !defined(CROSS_MINGW32)
1576 #      if !defined(CROSS_MINGW64)
1577 #       if !defined(_WIN32)
1578 #        if !defined(__HAIKU__)
1579 static int
1580 dl_iterate_phdr_callback (struct dl_phdr_info *info, size_t size, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1581 {
1582   (void)size;
1583   (void)data;
1584 
1585   if (strlen(info->dlpi_name) >= 2) {
1586       if (!dl_iterate_phdr_callback_called)
1587           (void)printf ("\r\n Loaded shared objects: ");
1588 
1589       dl_iterate_phdr_callback_called++;
1590       (void)printf ("%s ", info->dlpi_name);
1591   }
1592 
1593   return 0;
1594 }
1595 #        endif
1596 #       endif
1597 #      endif
1598 #     endif
1599 #    endif
1600 #   endif
1601 #  endif
1602 # endif
1603 #endif
1604 
1605 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1606 # if defined(_UCRT)
1607 #  define MINGW_CRT "UCRT"
1608 # else
1609 #  define MINGW_CRT "MSVCRT"
1610 # endif
1611 #endif
1612 
1613 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1614 struct UCRTVersion
1615 {
1616   uint16_t ProductVersion[4];
1617 };
1618 
1619 int
1620 GetUCRTVersion (struct UCRTVersion *ucrtversion)
     /* [previous][next][first][last][top][bottom][index][help] */
1621 {
1622 # ifdef _DEBUG
1623   static const wchar_t *DllName = L"ucrtbased.dll";
1624 # else
1625   static const wchar_t *DllName = L"ucrtbase.dll";
1626 # endif
1627 
1628   HMODULE ucrt = GetModuleHandleW (DllName);
1629   if (!ucrt)
1630     return GetLastError ();
1631 
1632   wchar_t path[MAX_PATH];
1633   if (!GetModuleFileNameW (ucrt, path, MAX_PATH))
1634     return GetLastError ();
1635 
1636   DWORD versionInfoSize = GetFileVersionInfoSizeW (path, NULL);
1637   if (!versionInfoSize)
1638     return GetLastError ();
1639 
1640   uint8_t versionInfo[versionInfoSize];
1641 
1642   if (!GetFileVersionInfoW (path, 0, versionInfoSize, versionInfo))
1643     return GetLastError ();
1644 
1645   VS_FIXEDFILEINFO *fixedFileInfo;
1646   UINT fixedFileInfoSize;
1647   if (!VerQueryValueW (versionInfo, L"\\", (void **)&fixedFileInfo, &fixedFileInfoSize))
1648     return GetLastError ();
1649 
1650   memcpy (ucrtversion->ProductVersion, &fixedFileInfo->dwProductVersionMS, sizeof (uint32_t) * 2);
1651 
1652   return 0;
1653 }
1654 #endif
1655 
1656 /* libsir support */
1657 
1658 #if !defined(PERF_STRIP)
1659 static int dps8_sir_report_error(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1660 {
1661   char message[SIR_MAXERROR] = {0};
1662   (void)sir_geterror(message);
1663   (void)fprintf(stderr, SIR_BREDB("libsir error: ") SIR_RED("%s%s"), message, SIR_EOL);
1664   return EXIT_FAILURE;
1665 }
1666 
1667 /* Main command loop */
1668 
1669 int main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1670 {
1671 char *cptr, *cptr2;
1672 char nbuf[PATH_MAX + 7];
1673 char cbuf[4*CBUFSIZE];
1674 char **targv = NULL;
1675 # if defined(USE_BACKTRACE)
1676 #  if defined(BACKTRACE_SUPPORTED)
1677 #   if defined(_INC_BACKTRACE_FUNC)
1678 bt_pid = (long)getpid();
1679 (void)bt_pid;
1680 #   endif /* if defined(_INC_BACKTRACE_FUNC) */
1681 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1682 # endif /* if defined(USE_BACKTRACE) */
1683 int32 i, sw;
1684 t_bool lookswitch;
1685 t_stat stat;
1686 
1687 # if defined(__MINGW32__)
1688 #  undef IS_WINDOWS
1689 #  define IS_WINDOWS 1
1690 #  if !defined(NEED_CONSOLE_SETUP)
1691 #   define NEED_CONSOLE_SETUP
1692 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1693 # endif /* if defined(__MINGW32__)*/
1694 
1695 # if defined(CROSS_MINGW32)
1696 #  undef IS_WINDOWS
1697 #  define IS_WINDOWS 1
1698 #  if !defined(NEED_CONSOLE_SETUP)
1699 #   define NEED_CONSOLE_SETUP
1700 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1701 # endif /* if defined(CROSS_MINGW32) */
1702 
1703 # if defined(__MINGW64__)
1704 #  undef IS_WINDOWS
1705 #  define IS_WINDOWS 1
1706 #  if !defined(NEED_CONSOLE_SETUP)
1707 #   define NEED_CONSOLE_SETUP
1708 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1709 # endif /* if defined(__MINGW64__) */
1710 
1711 # if defined(CROSS_MINGW64)
1712 #  undef IS_WINDOWS
1713 #  define IS_WINDOWS 1
1714 #  if !defined(NEED_CONSOLE_SETUP)
1715 #   define NEED_CONSOLE_SETUP
1716 #  endif /* if !defined(NEED_CONSOLE_SETUP */
1717 # endif /* if defined(CROSS_MINGW64) */
1718 
1719 # if defined(__CYGWIN__)
1720 #  if defined(IS_WINDOWS)
1721 #   undef IS_WINDOWS
1722 #  endif /* if defined(IS_WINDOWS) */
1723 # endif /* if defined(__CYGWIN__) */
1724 
1725 # if defined(USE_DUMA)
1726 #  if defined(DUMA_EXPLICIT_INIT)
1727 duma_init();
1728 (void)fflush(stderr);
1729 #  endif /* if defined(DUMA_EXPLICIT_INIT) */
1730 #  if defined(DUMA_MIN_ALIGNMENT)
1731 #   if DUMA_MIN_ALIGNMENT > 0
1732 DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
1733 #   endif /* if DUMA_MIN_ALIGNMENT > 0 */
1734 #  endif /* if defined(DUMA_MIN_ALIGNMENT) */
1735 DUMA_SET_FILL(0x2E);
1736 (void)fflush(stderr);
1737 (void)atexit(CleanDUMA);
1738 # endif /* if defined(USE_DUMA) */
1739 
1740 # if defined(USE_BACKTRACE)
1741 #  if defined(BACKTRACE_SUPPORTED)
1742 #   include "backtrace_main.c"
1743 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1744 # endif /* if defined(USE_BACKTRACE) */
1745 
1746 (void)setlocale(LC_ALL, "");
1747 
1748 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1749 #  if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
1750 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1751 #  endif /* if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) */
1752 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1753 
1754 # if defined(NEED_CONSOLE_SETUP)
1755 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1756 if (handle != INVALID_HANDLE_VALUE)
1757   {
1758     DWORD mode = 0;
1759     if (GetConsoleMode(handle, &mode))
1760       {
1761         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1762         SetConsoleMode(handle, mode);
1763       }
1764   }
1765 puts ("\e[0m");
1766 # endif /* if defined(NEED_CONSOLE_SETUP) */
1767 
1768 # if defined(__HAIKU__)
1769 (void)disable_debugger(1);
1770 # endif /* if defined(__HAIKU__) */
1771 
1772 /* libsir init */
1773 
1774 sirinit si;
1775 if (!sir_makeinit(&si))
1776     return dps8_sir_report_error();
1777 
1778 /* Levels for stdout: send debug, information, warning, and notice there. */
1779 si.d_stdout.levels = SIRL_DEBUG | SIRL_INFO | SIRL_WARN | SIRL_NOTICE;
1780 
1781 /* Options for stdout: don't show the timestamp, hostname, or thread ID. */
1782 si.d_stdout.opts = SIRO_NOTIME | SIRO_NOHOST | SIRO_NOTID;
1783 
1784 /* Levels for stderr: send error and above there. */
1785 si.d_stderr.levels = SIRL_ERROR | SIRL_CRIT | SIRL_ALERT | SIRL_EMERG;
1786 
1787 /* Options for stderr: don't show the timestamp, hostname, or thread ID. */
1788 si.d_stderr.opts = SIRO_NOTIME | SIRO_NOHOST | SIRO_NOTID;
1789 
1790 /* Levels for the system logger: don't send any output there. */
1791 si.d_syslog.levels = SIRL_NONE;
1792 
1793 /* Options for the system logger: use the default value. */
1794 si.d_syslog.opts = SIRO_DEFAULT;
1795 
1796 /* Configure a name to associate with our output. */
1797 (void)_sir_strncpy(si.name, SIR_MAXNAME, appname, strnlen(appname, SIR_MAXNAME));
1798 
1799 /* Initialize libsir. */
1800 if (!sir_init(&si))
1801     return dps8_sir_report_error();
1802 
1803 /* Set a friendly name for the current thread. */
1804 char thread_name[SIR_MAXPID] = {0};
1805 _sir_snprintf_trunc(thread_name, SIR_MAXPID, "%s: SCP", appname);
1806 (void)_sir_setthreadname(thread_name);
1807 
1808 /* sanity checks */
1809 
1810 # if defined(__clang_analyzer__)
1811 (void)fprintf (stderr, "Error: Attempting to execute a Clang Analyzer build!\n");
1812 return 1;
1813 # endif /* if defined(__clang_analyzer__) */
1814 
1815 if (argc == 0) {
1816     (void)fprintf (stderr, "Error: main() called directly!\n");
1817     return 1;
1818 }
1819 
1820 /* Enable unlimited core dumps */
1821 # if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1822 allowCores();
1823 # endif
1824 
1825 int testEndian = decContextTestEndian();
1826 if (testEndian != 0) {
1827   if (testEndian == 1) {
1828     (void)fprintf (stderr,
1829                    "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1830     return 1;
1831   }
1832   if (testEndian == -1) {
1833     (void)fprintf (stderr,
1834                    "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1835     return 1;
1836   }
1837   (void)fprintf (stderr,
1838                  "Error: Unable to determine system byte order; aborting.\n");
1839   return 1;
1840 }
1841 # if defined(NEED_128)
1842 test_math128();
1843 # endif /* if defined(NEED_128) */
1844 
1845 # define UV_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
1846 
1847 # if defined(LINUX_OS) && UV_VERSION_HEX >= UV_VERSION(1, 44, 0)
1848 // Only use uv_available_parallelism on Linux and only on libuv 1.44.0 or higher.
1849 // This will return a value less than the actual number of CPUs if, for example,
1850 // the CPU affinity mask has been pinned to specific CPUs.  On all other systems,
1851 // prefer the _sir_nprocs routine to query the number of actual CPUs available.
1852 nprocs = (unsigned int)uv_available_parallelism();
1853 # else
1854 nprocs = (unsigned int)_sir_nprocs();
1855 # endif
1856 
1857 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1858 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1859 if (!targv)
1860   {
1861     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1862                    __func__, __FILE__, __LINE__);
1863 # if defined(USE_BACKTRACE)
1864 #  if defined(SIGUSR2)
1865     (void)raise(SIGUSR2);
1866     /*NOTREACHED*/ /* unreachable */
1867 #  endif /* if defined(SIGUSR2) */
1868 # endif /* if defined(USE_BACKTRACE) */
1869     abort();
1870   }
1871 for (i=0; i<argc; i++)
1872     targv[i] = argv[i];
1873 argv = targv;
1874 
1875 /* setup defaults */
1876 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1877 *cbuf = 0;                                              /* init arg buffer */
1878 sim_switches = 0;                                       /* init switches */
1879 lookswitch = TRUE;
1880 stdnul = fopen(NULL_DEVICE,"wb");
1881 
1882 /* process arguments */
1883 for (i = 1; i < argc; i++) {                            /* loop thru args */
1884     if (argv[i] == NULL)                                /* paranoia */
1885         continue;
1886 
1887 # if defined(THREADZ) || defined(LOCKLESS)
1888 /* performance test */
1889     int perftestflag  = strcmp(argv[i], "--perftest");
1890     if (perftestflag == 0) {
1891       char * testName = NULL;
1892       if (i + 1 < argc)
1893         testName = argv[i + 1];
1894       perfTest (testName);
1895       return 0;
1896     }
1897 # endif
1898 
1899 /* requested only version? */
1900     int onlyvers  = strcmp(argv[i], "--version");
1901     if (onlyvers == 0) {
1902 # if defined(VER_H_GIT_VERSION)
1903 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1904 #   if VER_H_GIT_PATCH_INT < 1
1905         (void)fprintf (stdout, "%s simulator %s\n",
1906                        sim_name, VER_H_GIT_VERSION);
1907 #   else
1908         (void)fprintf (stdout, "%s simulator %s+%s\n",
1909                        sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1910 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1911 #  else
1912         (void)fprintf (stdout, "%s simulator %s\n",
1913                        sim_name, VER_H_GIT_VERSION);
1914 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1915 # else
1916         (void)fprintf (stdout, "%s simulator\n", sim_name);
1917 # endif /* if defined(VER_H_GIT_VERSION) */
1918         FREE (targv);
1919         return 0;
1920     }
1921 
1922 /* requested short or long help? */
1923     int longhelp  = strcmp(argv[i], "--help");
1924     int shorthelp = strcmp(argv[i], "-h");
1925     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1926     if (longhelp == 0 || shorthelp == 0) {
1927 # if defined(VER_H_GIT_VERSION)
1928 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1929 #   if VER_H_GIT_PATCH_INT < 1
1930         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1931 #   else
1932         (void)fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1933 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1934 #  else
1935         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1936 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1937 # else
1938         (void)fprintf (stdout, "%s simulator", sim_name);
1939 # endif /* if defined(VER_H_GIT_VERSION) */
1940         (void)fprintf (stdout, "\n");
1941         (void)fprintf (stdout, "\n USAGE: %s { [ SWITCHES ] ... } { < SCRIPT > }", argv[0]);
1942         (void)fprintf (stdout, "\n");
1943         (void)fprintf (stdout, "\n Invokes the %s simulator, with optional switches and/or script file.", sim_name);
1944         (void)fprintf (stdout, "\n");
1945         (void)fprintf (stdout, "\n Switches:");
1946         (void)fprintf (stdout, "\n  -e, -E            Aborts script processing immediately upon any error");
1947         (void)fprintf (stdout, "\n  -h, -H, --help    Prints only this informational help text and exit");
1948         (void)fprintf (stdout, "\n  -k, -K            Disables all support for exclusive file locking");
1949         (void)fprintf (stdout, "\n  -l, -L            Reports but ignores all exclusive file locking errors");
1950         (void)fprintf (stdout, "\n  -o, -O            Makes scripting ON conditions and actions inheritable");
1951         (void)fprintf (stdout, "\n  -q, -Q            Disables printing of non-fatal informational messages");
1952         (void)fprintf (stdout, "\n  -r, -R            Enables an unlinked ephemeral system state file");
1953         (void)fprintf (stdout, "\n  -s, -S            Enables a randomized persistent system state file");
1954         (void)fprintf (stdout, "\n  -t, -T            Disables fsync and creation/usage of system state file");
1955         (void)fprintf (stdout, "\n  -v, -V            Prints commands read from script file before execution");
1956         (void)fprintf (stdout, "\n  --version         Prints only the simulator identification text and exit");
1957         (void)fprintf (stdout, "\n");
1958 # if defined(USE_DUMA)
1959         nodist++;
1960 # endif /* if defined(USE_DUMA) */
1961 if (!nodist) {
1962         (void)fprintf (stdout, "\n This software is made available under the terms of the ICU License.");
1963         (void)fprintf (stdout, "\n For complete license details, see the LICENSE file included with the");
1964         (void)fprintf (stdout, "\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1965 }
1966 else
1967 {
1968         (void)fprintf (stdout, "\n********** LICENSE RESTRICTED BUILD ****** NOT FOR REDISTRIBUTION **********");
1969 }
1970         (void)fprintf (stdout, "\n");
1971         FREE(argv); //-V726
1972         return 0;
1973     }
1974     /* invalid arguments? */
1975     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
1976         if ((sw = get_switches (argv[i])) < 0) {
1977             (void)fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1978             FREE(argv); //-V726
1979             return 1;
1980             }
1981         sim_switches = sim_switches | sw;
1982         }
1983     /* parse arguments */
1984     else {
1985         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1986             (void)fprintf (stderr, "Argument string too long\n");
1987             FREE(argv); //-V726
1988             return 1;
1989             }
1990         if (*cbuf)                                  /* concat args */
1991             strcat (cbuf, " ");
1992         (void)sprintf(&cbuf[strlen(cbuf)], "%s%s%s", //-V755
1993                       strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : ""); //-V755
1994         lookswitch = FALSE;                         /* no more switches */
1995         }
1996     }                                               /* end for */
1997 sim_nolock = sim_switches & SWMASK ('K');           /* -k means skip locking     */
1998 sim_iglock = sim_switches & SWMASK ('L');           /* -l means ignore locking   */
1999 sim_randompst = sim_switches & SWMASK ('S');        /* -s means persist random   */
2000 sim_quiet = sim_switches & SWMASK ('Q');            /* -q means quiet            */
2001 sim_randstate = sim_switches & SWMASK ('R');        /* -r means random sys_state */
2002 if (sim_randompst) sim_randstate = 1;               /*    and is implied with -s */
2003 sim_nostate = sim_switches & SWMASK ('T');          /* -t means no sys_state     */
2004 if (sim_nostate)                                    /*    and disables -s and -r */
2005   {
2006     sim_randompst = 0;
2007     sim_randstate = 0;
2008   }
2009 sim_on_inherit = sim_switches & SWMASK ('O');       /* -o means inherit on state */
2010 
2011 sim_init_sock ();                                   /* init socket capabilities */
2012 if (sim_dflt_dev == NULL)                           /* if no default */
2013     sim_dflt_dev = sim_devices[0];
2014 if (sim_vm_init != NULL)                            /* call once only */
2015     (*sim_vm_init)();
2016 sim_finit ();                                       /* init fio package */
2017 for (i = 0; cmd_table[i].name; i++) {
2018     size_t alias_len = strlen (cmd_table[i].name);
2019     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
2020     if (!cmd_name)
2021       {
2022         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2023                        __func__, __FILE__, __LINE__);
2024 # if defined(USE_BACKTRACE)
2025 #  if defined(SIGUSR2)
2026         (void)raise(SIGUSR2);
2027         /*NOTREACHED*/ /* unreachable */
2028 #  endif /* if defined(SIGUSR2) */
2029 # endif /* if defined(USE_BACKTRACE) */
2030         abort();
2031       }
2032 
2033     strcpy (cmd_name, cmd_table[i].name);
2034     while (alias_len > 1) {
2035         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
2036         --alias_len;
2037         if (getenv (cmd_name))                      /* Externally defined command alias? */
2038             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
2039         }
2040     FREE (cmd_name);
2041     }
2042 stop_cpu = 0;
2043 sim_interval = 0;
2044 sim_time = sim_rtime = 0;
2045 noqueue_time = 0;
2046 sim_clock_queue = QUEUE_LIST_END;
2047 sim_is_running = 0;
2048 sim_log = NULL;
2049 if (sim_emax <= 0)
2050     sim_emax = 1;
2051 sim_timer_init ();
2052 
2053 if ((stat = sim_ttinit ()) != SCPE_OK) {
2054     (void)fprintf (stderr, "Fatal terminal initialization error\n%s\n",
2055                    sim_error_text (stat));
2056     FREE(argv); //-V726
2057     return 1;
2058     }
2059 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
2060     (void)fprintf (stderr, "Unable to allocate examine buffer\n");
2061     FREE(argv); //-V726
2062     return 1;
2063     };
2064 if ((stat = reset_all_p (0)) != SCPE_OK) {
2065     (void)fprintf (stderr, "Fatal simulator initialization error\n%s\n",
2066                    sim_error_text (stat));
2067     FREE(argv); //-V726
2068     return 1;
2069     }
2070 if ((stat = sim_brk_init ()) != SCPE_OK) {
2071     (void)fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
2072                    sim_error_text (stat));
2073     FREE(argv); //-V726
2074     return 1;
2075     }
2076 if (!sim_quiet) {
2077     (void)printf ("\n");
2078     show_version (stdout, NULL, NULL, 0, NULL);
2079     }
2080 
2081 cptr = getenv("HOME");
2082 if (cptr == NULL) {
2083     cptr = getenv("HOMEPATH");
2084     cptr2 = getenv("HOMEDRIVE");
2085     }
2086 else
2087     cptr2 = NULL;
2088 (void)cptr2;
2089 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
2090     stat = do_cmd (0, cbuf);                            /* proc cmd file */
2091 else if (*argv[0]) {                                    /* sim name arg? */
2092     char *np;                                           /* "path.ini" */
2093     nbuf[0] = '"';                                      /* starting " */
2094     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
2095     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
2096         np = strrchr (nbuf, '/');                       /* strip path and try again in cwd */
2097         if (np == NULL)
2098             np = strrchr (nbuf, '\\');                  /* windows path separator */
2099         if (np != NULL) {
2100             *np = '"';
2101             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
2102             }
2103         }
2104     }
2105 
2106 argv = uv_setup_args(argc, argv);
2107 
2108 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
2109 
2110 if (sim_vm_exit != NULL)                                /* call once only */
2111     (*sim_vm_exit)();
2112 
2113 detach_all (0, TRUE);                                   /* close files */
2114 sim_set_deboff (0, NULL);                               /* close debug */
2115 sim_set_logoff (0, NULL);                               /* close log */
2116 sim_set_notelnet (0, NULL);                             /* close Telnet */
2117 sim_ttclose ();                                         /* close console */
2118 sim_cleanup_sock ();                                    /* cleanup sockets */
2119 fclose (stdnul);                                        /* close bit bucket file handle */
2120 FREE (targv);                                           /* release any argv copy that was made */
2121 FREE (sim_prompt);
2122 FREE (sim_eval);
2123 FREE (sim_internal_devices);
2124 FREE (sim_brk_tab);
2125 FREE (sim_staba.comp);
2126 FREE (sim_staba.mask);
2127 FREE (sim_stabr.comp);
2128 FREE (sim_stabr.mask);
2129 return sir_cleanup() ? EXIT_SUCCESS : dps8_sir_report_error();
2130 }
2131 #endif
2132 
2133 t_stat process_stdin_commands (t_stat stat, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
2134 {
2135 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
2136 CONST char *cptr;
2137 t_stat stat_nomessage;
2138 CTAB *cmdp = NULL;
2139 
2140 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
2141 while (stat != SCPE_EXIT) {                             /* in case exit */
2142     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
2143         (void)printf ("%s%s\n", sim_prompt, cptr);      /* echo */
2144     else if (sim_vm_read != NULL) {                     /* sim routine? */
2145         (void)printf ("%s", sim_prompt);                /* prompt */
2146         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
2147         }
2148     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prompt*/
2149     if (cptr == NULL) {                                 /* EOF? */
2150         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
2151         else break;                                     /* otherwise exit */
2152         }
2153     if (*cptr == 0)                                     /* ignore blank */
2154         continue;
2155     sim_sub_args (cbuf, sizeof(cbuf), argv);
2156     if (sim_log)                                        /* log cmd */
2157         (void)fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
2158     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
2159         (void)fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
2160     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2161     sim_switches = 0;                                   /* init switches */
2162     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
2163         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
2164     else
2165         stat = SCPE_UNK;
2166     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
2167     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2168     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2169     sim_last_cmd_stat = stat;                           /* save command error status */
2170     if (!stat_nomessage) {                              /* displaying message status? */
2171         if (cmdp && (cmdp->message))                    /* special message handler? */
2172             cmdp->message (NULL, stat);                 /* let it deal with display */
2173         else
2174             if (stat >= SCPE_BASE)                      /* error? */
2175                 sim_printf ("%s\n", sim_error_text (stat));
2176         }
2177     if (sim_vm_post != NULL)
2178         (*sim_vm_post) (TRUE);
2179     }                                                   /* end while */
2180 return stat;
2181 }
2182 
2183 /* Set prompt routine */
2184 
2185 t_stat set_prompt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2186 {
2187 char gbuf[CBUFSIZE], *gptr;
2188 
2189 if ((NULL == cptr) || (*cptr == '\0'))
2190     return SCPE_ARG;
2191 
2192 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
2193 if (gbuf[0] == '\0') {                                  /* Token started with quote */
2194     gbuf[sizeof (gbuf)-1] = '\0';
2195     strncpy (gbuf, cptr, sizeof (gbuf)-1);
2196     gptr = strchr (gbuf, '"');
2197     if (NULL != gptr)
2198         *gptr = '\0';
2199     }
2200 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
2201 if (!sim_prompt)
2202   {
2203     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2204                    __func__, __FILE__, __LINE__);
2205 #if defined(USE_BACKTRACE)
2206 # if defined(SIGUSR2)
2207     (void)raise(SIGUSR2);
2208     /*NOTREACHED*/ /* unreachable */
2209 # endif /* if defined(SIGUSR2) */
2210 #endif /* if defined(USE_BACKTRACE) */
2211     abort();
2212   }
2213 (void)sprintf (sim_prompt, "%s ", gbuf);
2214 return SCPE_OK;
2215 }
2216 
2217 /* Find command routine */
2218 
2219 CTAB *find_cmd (const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
2220 {
2221 CTAB *cmdp = NULL;
2222 
2223 if (sim_vm_cmd)                                         /* try ext commands */
2224     cmdp = find_ctab (sim_vm_cmd, gbuf);
2225 if (cmdp == NULL)                                       /* try regular cmds */
2226     cmdp = find_ctab (cmd_table, gbuf);
2227 return cmdp;
2228 }
2229 
2230 /* Exit command */
2231 
2232 t_stat exit_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2233 {
2234 return SCPE_EXIT;
2235 }
2236 
2237 /* Help command */
2238 
2239 /* Used when sorting a list of command names */
2240 static int _cmd_name_compare (const void *pa, const void *pb)
     /* [previous][next][first][last][top][bottom][index][help] */
2241 {
2242 CTAB * const *a = (CTAB * const *)pa;
2243 CTAB * const *b = (CTAB * const *)pb;
2244 
2245 return strcmp((*a)->name, (*b)->name);
2246 }
2247 
2248 void fprint_help (FILE *st)
     /* [previous][next][first][last][top][bottom][index][help] */
2249 {
2250 CTAB *cmdp;
2251 CTAB **hlp_cmdp = NULL;
2252 size_t cmd_cnt = 0;
2253 size_t cmd_size = 0;
2254 size_t max_cmdname_size = 0;
2255 size_t i, line_offset;
2256 
2257 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
2258     if (cmdp->help) {
2259         if (cmd_cnt >= cmd_size) {
2260             cmd_size += 20;
2261             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2262             if (!hlp_cmdp)
2263               {
2264                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2265                                __func__, __FILE__, __LINE__);
2266 #if defined(USE_BACKTRACE)
2267 # if defined(SIGUSR2)
2268                 (void)raise(SIGUSR2);
2269                 /*NOTREACHED*/ /* unreachable */
2270 # endif /* if defined(SIGUSR2) */
2271 #endif /* if defined(USE_BACKTRACE) */
2272                 abort();
2273               }
2274             }
2275         hlp_cmdp[cmd_cnt] = cmdp;
2276         ++cmd_cnt;
2277         if (strlen(cmdp->name) > max_cmdname_size)
2278             max_cmdname_size = strlen(cmdp->name);
2279         }
2280     }
2281 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
2282     if (cmdp->help && (NULL == sim_vm_cmd || NULL == find_ctab (sim_vm_cmd, cmdp->name))) {
2283         if (cmd_cnt >= cmd_size) {
2284             cmd_size += 20;
2285             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2286             if (!hlp_cmdp)
2287               {
2288                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2289                                __func__, __FILE__, __LINE__);
2290 #if defined(USE_BACKTRACE)
2291 # if defined(SIGUSR2)
2292                 (void)raise(SIGUSR2);
2293                 /*NOTREACHED*/ /* unreachable */
2294 # endif /* if defined(SIGUSR2) */
2295 #endif /* if defined(USE_BACKTRACE) */
2296                 abort();
2297               }
2298             }
2299         hlp_cmdp[cmd_cnt] = cmdp;
2300         ++cmd_cnt;
2301         if (strlen (cmdp->name) > max_cmdname_size)
2302             max_cmdname_size = strlen(cmdp->name);
2303         }
2304     }
2305 (void)fprintf (st, "HELP is available for the following commands:\n\n    ");
2306 if (hlp_cmdp)
2307   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
2308 line_offset = 4;
2309 for ( i = 0 ; i < cmd_cnt ; ++i ) {
2310     fputs (hlp_cmdp[i]->name, st);
2311     line_offset += 5 + max_cmdname_size;
2312     if (line_offset + max_cmdname_size > 79) {
2313         line_offset = 4;
2314         (void)fprintf (st, "\n    ");
2315         }
2316     else
2317         (void)fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
2318     }
2319 FREE (hlp_cmdp);
2320 (void)fprintf (st, "\n");
2321 return;
2322 }
2323 
2324 static void fprint_header (FILE *st, t_bool *pdone, char *context)
     /* [previous][next][first][last][top][bottom][index][help] */
2325 {
2326 if (!*pdone)
2327     (void)fprintf (st, "%s", context);
2328 *pdone = TRUE;
2329 }
2330 
2331 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2332 {
2333 REG *rptr, *trptr;
2334 t_bool found = FALSE;
2335 t_bool all_unique = TRUE;
2336 size_t max_namelen = 0;
2337 DEVICE *tdptr;
2338 CONST char *tptr;
2339 char *namebuf;
2340 char rangebuf[32];
2341 
2342 if (dptr->registers)
2343     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2344         if (rptr->flags & REG_HIDDEN)
2345             continue;
2346         if (rptr->depth > 1)
2347             (void)sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2348         else
2349             strcpy (rangebuf, "");
2350         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2351             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2352         found = TRUE;
2353         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2354         if ((trptr == NULL) || (tdptr != dptr))
2355             all_unique = FALSE;
2356         }
2357 if (!found) {
2358     if (!silent)
2359         (void)fprintf (st, "No register HELP available for the %s device\n",
2360                        dptr->name);
2361     }
2362 else {
2363     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2364     if (!namebuf)
2365       {
2366         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2367                        __func__, __FILE__, __LINE__);
2368 #if defined(USE_BACKTRACE)
2369 # if defined(SIGUSR2)
2370         (void)raise(SIGUSR2);
2371         /*NOTREACHED*/ /* unreachable */
2372 # endif /* if defined(SIGUSR2) */
2373 #endif /* if defined(USE_BACKTRACE) */
2374         abort();
2375       }
2376     (void)fprintf (st, "\nThe %s device implements these registers:\n\n",
2377                    dptr->name);
2378     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2379         if (rptr->flags & REG_HIDDEN)
2380             continue;
2381         if (rptr->depth <= 1)
2382             (void)sprintf (namebuf, "%*s",
2383                            -((int)max_namelen),
2384                            rptr->name);
2385         else {
2386             (void)sprintf (rangebuf, "[%d:%d]",
2387                            0,
2388                            rptr->depth-1);
2389             (void)sprintf (namebuf, "%s%*s",
2390                            rptr->name,
2391                            (int)(strlen(rptr->name))-((int)max_namelen),
2392                            rangebuf);
2393             }
2394         if (all_unique) {
2395             (void)fprintf (st, "  %s %4d  %s\n",
2396                            namebuf,
2397                            rptr->width,
2398                            rptr->desc ? rptr->desc : "");
2399             continue;
2400             }
2401         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2402         if ((trptr == NULL) || (tdptr != dptr))
2403             (void)fprintf (st, "  %s %s %4d  %s\n",
2404                            dptr->name,
2405                            namebuf,
2406                            rptr->width,
2407                            rptr->desc ? rptr->desc : "");
2408         else
2409             (void)fprintf (st, "  %*s %s %4d  %s\n",
2410                            (int)strlen(dptr->name), "",
2411                            namebuf,
2412                            rptr->width,
2413                            rptr->desc ? rptr->desc : "");
2414         }
2415     FREE (namebuf);
2416     }
2417 }
2418 
2419 void fprint_reg_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2420 {
2421 fprint_reg_help_ex (st, dptr, TRUE);
2422 }
2423 
2424 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2425 {
2426 if (dptr->attach_help) {
2427     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2428     dptr->attach_help (st, dptr, NULL, 0, NULL);
2429     return;
2430     }
2431 if (DEV_TYPE(dptr) == DEV_DISK) {
2432     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2433     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2434     return;
2435     }
2436 if (DEV_TYPE(dptr) == DEV_TAPE) {
2437     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2438     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2439     return;
2440     }
2441 if (!silent) {
2442     (void)fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2443     if (dptr->help)
2444         dptr->help (st, dptr, NULL, 0, NULL);
2445     }
2446 }
2447 
2448 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2449 {
2450 MTAB *mptr;
2451 DEBTAB *dep;
2452 t_bool found = FALSE;
2453 char buf[CBUFSIZE], header[CBUFSIZE];
2454 uint32 enabled_units = dptr->numunits;
2455 uint32 unit;
2456 
2457 (void)sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2458 for (unit=0; unit < dptr->numunits; unit++)
2459     if (dptr->units[unit].flags & UNIT_DIS)
2460         --enabled_units;
2461 if (dptr->modifiers) {
2462     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2463         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2464             continue;                                       /* skip unit only extended modifiers */
2465         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2466             continue;                                       /* skip unit only simple modifiers */
2467         if (mptr->mstring) {
2468             fprint_header (st, &found, header);
2469             (void)sprintf (buf, "SET %s %s%s", sim_dname (dptr),
2470                            mptr->mstring,
2471                            (strchr(mptr->mstring, '=')) \
2472                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2473                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2474                                ? "{=val}" : "")));
2475             if ((strlen (buf) < 30) || (NULL == mptr->help))
2476                 (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2477             else
2478                 (void)fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2479             }
2480         }
2481     }
2482 if (dptr->flags & DEV_DISABLE) {
2483     fprint_header (st, &found, header);
2484     (void)sprintf (buf, "SET %s ENABLE", sim_dname (dptr));
2485     (void)fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2486     (void)sprintf (buf, "SET %s DISABLE", sim_dname (dptr));
2487     (void)fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2488     }
2489 if (dptr->flags & DEV_DEBUG) {
2490     fprint_header (st, &found, header);
2491     (void)sprintf (buf, "SET %s DEBUG", sim_dname (dptr));
2492     (void)fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2493     (void)sprintf (buf, "SET %s NODEBUG", sim_dname (dptr));
2494     (void)fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2495     if (dptr->debflags) {
2496         t_bool desc_available = FALSE;
2497         strcpy (buf, "");
2498         (void)fprintf (st, "SET %s DEBUG=", sim_dname (dptr));
2499         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2500             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2501             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2502             }
2503         (void)fprintf (st, "\n");
2504         (void)fprintf (st,  "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2505         (void)fprintf (st, "SET %s NODEBUG=", sim_dname (dptr));
2506         for (dep = dptr->debflags; dep->name != NULL; dep++)
2507             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2508         (void)fprintf (st, "\n");
2509         (void)fprintf (st,  "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2510         if (desc_available) {
2511             (void)fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2512             for (dep = dptr->debflags; dep->name != NULL; dep++)
2513                 (void)fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2514             }
2515         }
2516     }
2517 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2518     if (dptr->units->flags & UNIT_DISABLE) {
2519         fprint_header (st, &found, header);
2520         (void)sprintf (buf, "SET %sn ENABLE", sim_dname (dptr));
2521         (void)fprintf (st,  "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2522         (void)sprintf (buf, "SET %sn DISABLE", sim_dname (dptr));
2523         (void)fprintf (st,  "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2524         }
2525     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2526         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2527             continue;                                           /* skip device only modifiers */
2528         if ((NULL == mptr->valid) && MODMASK(mptr,MTAB_XTD))
2529             continue;                                           /* skip show only modifiers */
2530         if (mptr->mstring) {
2531             fprint_header (st, &found, header);
2532             (void)sprintf (buf, "SET %s%s %s%s", sim_dname (dptr),
2533                            (dptr->numunits > 1) ? "n" : "0", mptr->mstring,
2534                            (strchr(mptr->mstring, '=')) \
2535                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2536                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2537                                ? "{=val}" : "")));
2538             (void)fprintf (st, "%-30s\t%s\n", buf,
2539                            (strchr(mptr->mstring, '=')) \
2540                                ? "" : (mptr->help ? mptr->help : ""));
2541             }
2542         }
2543     }
2544 if (!found && !silent)
2545     (void)fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2546 }
2547 
2548 void fprint_set_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2549 {
2550   fprint_set_help_ex (st, dptr, TRUE);
2551 }
2552 
2553 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2554 {
2555 MTAB *mptr;
2556 t_bool found = FALSE;
2557 char buf[CBUFSIZE], header[CBUFSIZE];
2558 uint32 enabled_units = dptr->numunits;
2559 uint32 unit;
2560 
2561 (void)sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2562 for (unit=0; unit < dptr->numunits; unit++)
2563     if (dptr->units[unit].flags & UNIT_DIS)
2564         --enabled_units;
2565 if (dptr->modifiers) {
2566     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2567         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2568             continue;                                       /* skip unit only extended modifiers */
2569         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2570             continue;                                       /* skip unit only simple modifiers */
2571         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2572             continue;
2573         fprint_header (st, &found, header);
2574         (void)sprintf (buf, "SHOW %s %s%s", sim_dname (dptr),
2575                        mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2576         (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2577         }
2578     }
2579 if (dptr->flags & DEV_DEBUG) {
2580     fprint_header (st, &found, header);
2581     (void)sprintf (buf, "SHOW %s DEBUG", sim_dname (dptr));
2582     (void)fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2583     }
2584 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2585     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2586         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2587             continue;                                           /* skip device only modifiers */
2588         if ((!mptr->disp) || (!mptr->pstring))
2589             continue;
2590         fprint_header (st, &found, header);
2591         (void)sprintf (buf, "SHOW %s%s %s%s", sim_dname (dptr),
2592                        (dptr->numunits > 1) ? "n" : "0", mptr->pstring,
2593                        MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2594         (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2595         }
2596     }
2597 if (!found && !silent)
2598     (void)fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2599 }
2600 
2601 void fprint_show_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2602     {
2603     fprint_show_help_ex (st, dptr, TRUE);
2604     }
2605 
2606 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2607 {
2608 BRKTYPTAB *brkt = dptr->brk_types;
2609 char gbuf[CBUFSIZE];
2610 
2611 if (sim_brk_types == 0) {
2612     if ((dptr != sim_dflt_dev) && (!silent)) {
2613         (void)fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2614         if (dptr->help)
2615             dptr->help (st, dptr, NULL, 0, NULL);
2616         }
2617     return;
2618     }
2619 if (brkt == NULL) {
2620     int i;
2621 
2622     if (dptr == sim_dflt_dev) {
2623         if (sim_brk_types & ~sim_brk_dflt) {
2624             (void)fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2625             for (i=0; i<26; i++) {
2626                 if (sim_brk_types & (1<<i))
2627                     (void)fprintf (st, "  -%c\n", 'A'+i);
2628                 }
2629             }
2630         (void)fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2631         }
2632     return;
2633     }
2634 (void)fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2635 while (brkt->btyp) {
2636     (void)fprintf (st, "  %s     %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2637     ++brkt;
2638     }
2639 (void)fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2640 }
2641 
2642 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2643 {
2644 char gbuf[CBUFSIZE];
2645 CTAB *cmdp;
2646 
2647 if (*cptr) {
2648     (void)get_glyph (cptr, gbuf, 0);
2649     if ((cmdp = find_cmd (gbuf))) {
2650         if (cmdp->action == &exdep_cmd) {
2651             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2652                 return dptr->help (st, dptr, uptr, flag, cptr);
2653             else
2654                 (void)fprintf (st, "No HELP available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2655             return SCPE_OK;
2656             }
2657         if (cmdp->action == &set_cmd) {
2658             fprint_set_help_ex (st, dptr, FALSE);
2659             return SCPE_OK;
2660             }
2661         if (cmdp->action == &show_cmd) {
2662             fprint_show_help_ex (st, dptr, FALSE);
2663             return SCPE_OK;
2664             }
2665         if (cmdp->action == &attach_cmd) {
2666             fprint_attach_help_ex (st, dptr, FALSE);
2667             return SCPE_OK;
2668             }
2669         if (cmdp->action == &brk_cmd) {
2670             fprint_brk_help_ex (st, dptr, FALSE);
2671             return SCPE_OK;
2672             }
2673         if (dptr->help)
2674             return dptr->help (st, dptr, uptr, flag, cptr);
2675         (void)fprintf (st, "No %s HELP is available for the %s device\n", cmdp->name, dptr->name);
2676         return SCPE_OK;
2677         }
2678     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2679         fprint_reg_help_ex (st, dptr, FALSE);
2680         return SCPE_OK;
2681         }
2682     if (dptr->help)
2683         return dptr->help (st, dptr, uptr, flag, cptr);
2684     (void)fprintf (st, "No %s HELP is available for the %s device\n", gbuf, dptr->name);
2685     return SCPE_OK;
2686     }
2687 if (dptr->help) {
2688     return dptr->help (st, dptr, uptr, flag, cptr);
2689     }
2690 if (dptr->description)
2691     (void)fprintf (st, "%s %s HELP\n", dptr->description (dptr), dptr->name);
2692 else
2693     (void)fprintf (st, "%s HELP\n", dptr->name);
2694 fprint_set_help_ex    (st, dptr, TRUE);
2695 fprint_show_help_ex   (st, dptr, TRUE);
2696 fprint_attach_help_ex (st, dptr, TRUE);
2697 fprint_reg_help_ex    (st, dptr, TRUE);
2698 fprint_brk_help_ex    (st, dptr, TRUE);
2699 return SCPE_OK;
2700 }
2701 
2702 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
     /* [previous][next][first][last][top][bottom][index][help] */
2703 {
2704 switch (help[0]) {
2705     case '*':
2706         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2707         if (sim_log)
2708             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2709         break;
2710     default:
2711         fputs (help, stdout);
2712         if (sim_log)
2713             fputs (help, sim_log);
2714         break;
2715     }
2716 return SCPE_OK;
2717 }
2718 
2719 t_stat help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2720 {
2721 char gbuf[CBUFSIZE];
2722 CTAB *cmdp;
2723 
2724 GET_SWITCHES (cptr);
2725 if (sim_switches & SWMASK ('F'))
2726     flag = flag | SCP_HELP_FLAT;
2727 if (*cptr) {
2728     cptr = get_glyph (cptr, gbuf, 0);
2729     if ((cmdp = find_cmd (gbuf))) {
2730         if (*cptr) {
2731             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2732                 DEVICE *dptr;
2733                 UNIT *uptr;
2734                 t_stat r;
2735                 cptr = get_glyph (cptr, gbuf, 0);
2736                 dptr = find_unit (gbuf, &uptr);
2737                 if (dptr == NULL)
2738                     dptr = find_dev (gbuf);
2739                 if (dptr != NULL) {
2740                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2741                     if (sim_log)
2742                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2743                     return r;
2744                     }
2745                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2746                     /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2747                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2748                          (cmdp->help))
2749                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2750                     }
2751                 else { /* HELP SHOW xxx (not device or unit) */
2752                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2753                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2754                         return SCPE_ARG;
2755                     return help_cmd_output (flag, shptr->help, NULL);
2756                     }
2757                 return SCPE_ARG;
2758                 }
2759             else
2760                 return SCPE_2MARG;
2761             }
2762         if (cmdp->help) {
2763             if (strcmp (cmdp->name, "HELP") == 0) {
2764 
2765 
2766 
2767 
2768 
2769 
2770 
2771 
2772 
2773 
2774 
2775 
2776 
2777 
2778 
2779 
2780 
2781 
2782 
2783 
2784 
2785 
2786 
2787 
2788 
2789 
2790 
2791 
2792 
2793 
2794 
2795 
2796 
2797 
2798                 }
2799             else {
2800                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2801                     sim_dflt_dev && sim_dflt_dev->help) {
2802                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2803                         if (sim_log)
2804                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2805                     }
2806                 }
2807             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2808             }
2809         else { /* no help so it is likely a command alias */
2810             CTAB *cmdpa;
2811             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2812                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2813                     sim_printf ("%s is an alias for the %s command:\n%s",
2814                                 cmdp->name, cmdpa->name, cmdpa->help);
2815                     break;
2816                     }
2817             if (cmdpa->name == NULL)                /* not found? */
2818                 sim_printf ("No help available for the %s command\n", cmdp->name);
2819             }
2820         }
2821     else {
2822         DEVICE *dptr;
2823         UNIT *uptr;
2824         t_stat r;
2825         dptr = find_unit (gbuf, &uptr);
2826         if (dptr == NULL) {
2827             dptr = find_dev (gbuf);
2828             if (dptr == NULL)
2829                 return SCPE_ARG;
2830             if (dptr->flags & DEV_DIS)
2831                 sim_printf ("Device %s is currently disabled\n", dptr->name);
2832             }
2833         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2834         if (sim_log)
2835             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2836         return r;
2837         }
2838     }
2839 else {
2840     fprint_help (stdout);
2841     if (sim_log)
2842         fprint_help (sim_log);
2843     }
2844 return SCPE_OK;
2845 }
2846 
2847 /* Spawn command */
2848 
2849 t_stat spawn_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2850 {
2851 t_stat status;
2852 if ((cptr == NULL) || (strlen (cptr) == 0))
2853     cptr = getenv("SHELL");
2854 if ((cptr == NULL) || (strlen (cptr) == 0))
2855     cptr = getenv("ComSpec");
2856 (void)fflush(stdout);                                   /* flush stdout */
2857 if (sim_log)                                            /* flush log if enabled */
2858     (void)fflush (sim_log);
2859 if (sim_deb)                                            /* flush debug if enabled */
2860     (void)fflush (sim_deb);
2861 status = system (cptr);
2862 
2863 return status;
2864 }
2865 
2866 /* Echo command */
2867 
2868 t_stat echo_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2869 {
2870 sim_printf ("%s\n", cptr);
2871 return SCPE_OK;
2872 }
2873 
2874 /*
2875  * DO command
2876  *
2877  * Note that SCPE_STEP ("Step expired") is considered a note and
2878  * not an error; it does not abort command execution when using -E.
2879  *
2880  * Inputs:
2881  *      flag    =   caller and nesting level indicator
2882  *      fcptr   =   filename and optional arguments, space-separated
2883  * Outputs:
2884  *      status  =   error status
2885  *
2886  * The "flag" input value indicates the source of the call, as follows:
2887  *
2888  *      -1      =   initialization file (no error if not found)
2889  *       0      =   command line file
2890  *       1      =   "DO" command
2891  *      >1      =   nested "DO" command
2892  */
2893 
2894 t_stat do_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2895 {
2896 return do_cmd_label (flag, fcptr, NULL);
2897 }
2898 
2899 static char *do_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2900 {
2901 static char cbuf[4*CBUFSIZE];
2902 
2903 (void)snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth],
2904                 sim_do_label[sim_do_depth] ? "::" : "",
2905                 sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "",
2906                 sim_goto_line[sim_do_depth]);
2907 return cbuf;
2908 }
2909 
2910 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
2911 {
2912 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2913 CONST char *cptr;
2914 FILE *fpin;
2915 CTAB *cmdp = NULL;
2916 int32 echo, nargs, errabort, i;
2917 int32 saved_sim_do_echo = sim_do_echo,
2918       saved_sim_show_message = sim_show_message,
2919       saved_sim_on_inherit = sim_on_inherit,
2920       saved_sim_quiet = sim_quiet;
2921 t_bool staying;
2922 t_stat stat, stat_nomessage;
2923 
2924 stat = SCPE_OK;
2925 staying = TRUE;
2926 if (flag > 0)                                           /* need switches? */
2927     GET_SWITCHES (fcptr);                               /* get switches */
2928 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
2929 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2930 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2931 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
2932 
2933 abuf[sizeof(abuf)-1] = '\0';
2934 strncpy (abuf, fcptr, sizeof(abuf)-1);
2935 c = abuf;
2936 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
2937 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
2938     while (sim_isspace (*c))                            /* skip blanks */
2939         c++;
2940     if (*c == 0)                                        /* all done? */
2941         do_arg [nargs++] = NULL;                        /* null argument */
2942     else {
2943         if (*c == '\'' || *c == '"')                    /* quoted string? */
2944             quote = *c++;
2945         else quote = 0;
2946         do_arg[nargs++] = c;                            /* save start */
2947         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2948             c++;
2949         if (*c)                                         /* term at quote/spc */
2950             *c++ = 0;
2951         }
2952     }                                                   /* end for */
2953 
2954 if (do_arg [0] == NULL)                                 /* need at least 1 */
2955     return SCPE_2FARG;
2956 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
2957     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
2958     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
2959         if (flag == 0)                                  /* cmd line file? */
2960              (void)fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2961         return SCPE_OPENERR;                            /* return failure */
2962         }
2963     }
2964 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2965     ++sim_do_depth;
2966     if (sim_on_inherit) {                               /* inherit ON condition actions? */
2967         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2968         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
2969             if (sim_on_actions[sim_do_depth-1][i]) {
2970                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2971                 if (NULL == sim_on_actions[sim_do_depth][i]) {
2972                     while (--i >= 0) {
2973                         FREE(sim_on_actions[sim_do_depth][i]);
2974                         sim_on_actions[sim_do_depth][i] = NULL;
2975                         }
2976                     sim_on_check[sim_do_depth] = 0;
2977                     sim_brk_clract ();                  /* defang breakpoint actions */
2978                     --sim_do_depth;                     /* unwind nesting */
2979                     fclose(fpin);
2980                     return SCPE_MEM;
2981                     }
2982                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2983                 }
2984             }
2985         }
2986     }
2987 
2988 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
2989 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
2990 sim_goto_line[sim_do_depth] = 0;
2991 if (label) {
2992     sim_gotofile = fpin;
2993     sim_do_echo = echo;
2994     stat = goto_cmd (0, label);
2995     if (stat != SCPE_OK) {
2996         strcpy(cbuf, "RETURN SCPE_ARG");
2997         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
2998         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
2999         goto Cleanup_Return;
3000         }
3001     }
3002 if (errabort)                                           /* -e flag? */
3003     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
3004 
3005 do {
3006     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
3007     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
3008         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
3009         sim_goto_line[sim_do_depth] += 1;
3010         }
3011     if (cptr != NULL && strlen(cptr) < sizeof(cbuf)) {  /* validate */
3012         sim_sub_args(cbuf, sizeof(cbuf), do_arg);       /* substitute args */
3013         }
3014     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
3015     if (cptr == NULL) {                                 /* EOF? */
3016         stat = SCPE_OK;                                 /* set good return */
3017         break;
3018         }
3019     if (*cptr == 0)                                     /* ignore blank */
3020         continue;
3021     if (echo)                                           /* echo if -v */
3022         sim_printf("%s> %s\n", do_position(), cptr);
3023     if (*cptr == ':')                                   /* ignore label */
3024         continue;
3025     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
3026     sim_switches = 0;                                   /* init switches */
3027     sim_gotofile = fpin;
3028     sim_do_echo = echo;
3029     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
3030         if (cmdp->action == &return_cmd)                /* RETURN command? */
3031             break;                                      /*    done! */
3032         if (cmdp->action == &do_cmd) {                  /* DO command? */
3033             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
3034                 stat = SCPE_NEST;
3035             else
3036                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
3037             }
3038         else
3039             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
3040         }
3041     else stat = SCPE_UNK;                               /* bad cmd given */
3042     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
3043     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
3044     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
3045     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
3046     if (cmdp)
3047       if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
3048           ((cmdp->action != &return_cmd) &&
3049            (cmdp->action != &goto_cmd) &&
3050            (cmdp->action != &on_cmd) &&
3051            (cmdp->action != &echo_cmd)))
3052         sim_last_cmd_stat = stat;                       /* save command error status */
3053     switch (stat) {
3054         case SCPE_AFAIL:
3055             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
3056                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
3057             break;
3058         case SCPE_EXIT:
3059             staying = FALSE;
3060             break;
3061         case SCPE_OK:
3062         case SCPE_STEP:
3063             break;
3064         default:
3065             break;
3066         }
3067     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
3068         (stat != SCPE_STEP)) {
3069         if (!echo && !sim_quiet &&                      /* report if not echoing */
3070             !stat_nomessage &&                          /* and not suppressing messages */
3071             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
3072             sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
3073             }
3074         }
3075     if (!stat_nomessage) {                              /* report error if not suppressed */
3076         if (cmdp && cmdp->message)                      /* special message handler */
3077             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
3078         else
3079             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
3080                 sim_printf ("%s\n", sim_error_text (stat));
3081         }
3082     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
3083         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
3084     if (staying &&
3085         (sim_on_check[sim_do_depth]) &&
3086         (stat != SCPE_OK) &&
3087         (stat != SCPE_STEP)) {
3088         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
3089             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
3090         else
3091             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
3092         }
3093     if (sim_vm_post != NULL)
3094         (*sim_vm_post) (TRUE);
3095     } while (staying);
3096 Cleanup_Return:
3097 if (fpin) //-V547
3098     fclose (fpin);                                      /* close file */
3099 sim_gotofile = NULL;
3100 if (flag >= 0) {
3101     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
3102     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
3103     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
3104     }
3105 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
3106 if ((flag >= 0) || (!sim_on_inherit)) {
3107     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
3108         FREE (sim_on_actions[sim_do_depth][i]);
3109         sim_on_actions[sim_do_depth][i] = NULL;
3110         }
3111     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
3112     }
3113 if (flag >= 0)
3114     --sim_do_depth;                                     /* unwind nesting */
3115 sim_brk_clract ();                                      /* defang breakpoint actions */
3116 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
3117     sim_string_to_stat (cptr, &stat);
3118     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
3119     if (sim_switches & SWMASK ('Q'))
3120         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
3121     return stat;                                        /* return with explicit return status */
3122     }
3123 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
3124 }
3125 
3126 /*
3127  * Substitute_args - replace %n tokens in 'instr' with the do command's arguments
3128  *                   and other environment variables
3129  *
3130  * Calling sequence
3131  * instr        =       input string
3132  * instr_size   =       sizeof input string buffer
3133  * do_arg[10]   =       arguments
3134  *
3135  * Token "%0" expands to the command file name.
3136  * Token %n (n being a single digit) expands to the n'th argument
3137  * Tonen %* expands to the whole set of arguments (%1 ... %9)
3138  *
3139  * The input sequence "\%" represents a literal "%", and "\\" represents a
3140  * literal "\".  All other character combinations are rendered literally.
3141  *
3142  * Omitted parameters result in null-string substitutions.
3143  *
3144  * A Tokens preceded and followed by % characters are expanded as environment
3145  * variables, and if one isn't found then can be one of several special
3146  * variables:
3147  *   %DATE%              yyyy-mm-dd
3148  *   %TIME%              hh:mm:ss
3149  *   %STIME%             hh_mm_ss
3150  *   %CTIME%             Www Mmm dd hh:mm:ss yyyy
3151  *   %STATUS%            Status value from the last command executed
3152  *   %TSTATUS%           The text form of the last status value
3153  *   %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
3154  *   %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
3155  *   %SIM_QUIET%         The Quiet mode of the current Do command file
3156  *   %SIM_MESSAGE%       The message display status of the current Do command file
3157  * Environment variable lookups are done first with the precise name between
3158  * the % characters and if that fails, then the name between the % characters
3159  * is upcased and a lookup of that value is attempted.
3160 
3161  * The first Space delimited token on the line is extracted in uppercase and
3162  * then looked up as an environment variable.  If found it the value is
3163  * substituted for the original string before expanding everything else.  If
3164  * it is not found, then the original beginning token on the line is left
3165  * untouched.
3166  */
3167 
3168 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
     /* [previous][next][first][last][top][bottom][index][help] */
3169 {
3170 char gbuf[CBUFSIZE];
3171 char *ip = instr, *op, *oend, *tmpbuf;
3172 const char *ap;
3173 char rbuf[CBUFSIZE];
3174 int i;
3175 time_t now;
3176 struct tm *tmnow;
3177 
3178 time(&now);
3179 tmnow = localtime(&now);
3180 tmpbuf = (char *)malloc(instr_size);
3181 if (!tmpbuf)
3182   {
3183      (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3184                    __func__, __FILE__, __LINE__);
3185 #if defined(USE_BACKTRACE)
3186 # if defined(SIGUSR2)
3187      (void)raise(SIGUSR2);
3188      /*NOTREACHED*/ /* unreachable */
3189 # endif /* if defined(SIGUSR2) */
3190 #endif /* if defined(USE_BACKTRACE) */
3191      abort();
3192   }
3193 op = tmpbuf;
3194 oend = tmpbuf + instr_size - 2;
3195 while (sim_isspace (*ip))                               /* skip leading spaces */
3196     *op++ = *ip++;
3197 for (; *ip && (op < oend); ) {
3198     if ((ip [0] == '\\') &&                             /* literal escape? */
3199         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
3200         ip++;                                           /* skip '\' */
3201         *op++ = *ip++;                                  /* copy escaped char */
3202         }
3203     else
3204         if ((*ip == '%') &&
3205             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
3206             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
3207                 ap = do_arg[ip[1] - '0'];
3208                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
3209                     if (do_arg[i] == NULL) {
3210                         ap = NULL;
3211                         break;
3212                         }
3213                 ip = ip + 2;
3214                 }
3215             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
3216                 (void)memset (rbuf, '\0', sizeof(rbuf));
3217                 ap = rbuf;
3218                 for (i=1; i<=9; ++i)
3219                     if (do_arg[i] == NULL)
3220                         break;
3221                     else
3222                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
3223                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
3224                                 char quote = '"';
3225                                 if (strchr(do_arg[i], quote))
3226                                     quote = '\'';
3227                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"",
3228                                               (i != 1) ? " " : "", quote,
3229                                               do_arg[i], quote);
3230                                 }
3231                             else
3232                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%s",
3233                                               (i != 1) ? " " : "", do_arg[i]);
3234                             }
3235                         else
3236                             break;
3237                 ip = ip + 2;
3238                 }
3239             else {                                      /* environment variable */
3240                 ap = NULL;
3241                 (void)get_glyph_nc (ip+1, gbuf, '%');   /* first try using the literal name */
3242                 ap = getenv(gbuf);
3243                 if (!ap) {
3244                     (void)get_glyph (ip+1, gbuf, '%');  /* now try using the upcased name */
3245                     ap = getenv(gbuf);
3246                     }
3247                 ip += 1 + strlen (gbuf);
3248                 if (*ip == '%') ++ip;
3249                 if (!ap) {
3250                     /* ISO 8601 format date/time info */
3251                     if (!strcmp ("DATE", gbuf)) {
3252                         (void)sprintf (rbuf, "%4d-%02d-%02d",
3253                                        tmnow->tm_year + 1900,
3254                                        tmnow->tm_mon  + 1,
3255                                        tmnow->tm_mday);
3256                         ap = rbuf;
3257                         }
3258                     else if (!strcmp ("TIME", gbuf)) {
3259                         (void)sprintf (rbuf, "%02d:%02d:%02d",
3260                                        tmnow->tm_hour,
3261                                        tmnow->tm_min,
3262                                        tmnow->tm_sec);
3263                         ap = rbuf;
3264                         }
3265                     else if (!strcmp ("DATETIME", gbuf)) {
3266                         (void)sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d",
3267                                        tmnow->tm_year + 1900,
3268                                        tmnow->tm_mon  + 1,
3269                                        tmnow->tm_mday,
3270                                        tmnow->tm_hour,
3271                                        tmnow->tm_min,
3272                                        tmnow->tm_sec);
3273                         ap = rbuf;
3274                         }
3275                     /* Locale oriented formatted date/time info */
3276                     if (!strcmp ("LDATE", gbuf)) {
3277                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
3278                         ap = rbuf;
3279                         }
3280                     else if (!strcmp ("LTIME", gbuf)) {
3281                         strftime (rbuf, sizeof(rbuf), "%I:%M:%S %p", tmnow);
3282                         ap = rbuf;
3283                         }
3284                     else if (!strcmp ("CTIME", gbuf)) {
3285                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
3286                         ap = rbuf;
3287                         }
3288                     /* Separate Date/Time info */
3289                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
3290                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
3291                         ap = rbuf;
3292                         }
3293                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3294                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
3295                         ap = rbuf;
3296                         }
3297                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
3298                         (void)sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
3299                         ap = rbuf;
3300                         }
3301                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
3302                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
3303                         int year = tmnow->tm_year + 1900;
3304                         int days = year - 2001;
3305                         int leaps = days/4 - days/100 + days/400;
3306                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
3307                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
3308                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
3309                                               96, 80, 92, 76, 88, 72, 84};
3310                         int cal_year = years[selector];
3311 
3312                         if (!strcmp ("DATE_19XX_YY", gbuf))
3313                             (void)sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
3314                         else
3315                             (void)sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
3316                         ap = rbuf;
3317                         }
3318                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
3319                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
3320                         ap = rbuf;
3321                         }
3322                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
3323                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
3324                         ap = rbuf;
3325                         }
3326                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
3327                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
3328                         ap = rbuf;
3329                         }
3330                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
3331                         (void)sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
3332                         ap = rbuf;
3333                         }
3334                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
3335                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
3336                         int iso_yr = tmnow->tm_year + 1900;
3337                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
3338 
3339                         if (iso_wk == 0) {
3340                             iso_yr = iso_yr - 1;
3341                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
3342                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
3343                             }
3344                         else
3345                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
3346                                 ++iso_yr;
3347                                 iso_wk = 1;
3348                                 }
3349                         if (!strcmp ("DATE_WW", gbuf))
3350                             (void)sprintf (rbuf, "%02d", iso_wk);
3351                         else
3352                             (void)sprintf (rbuf, "%04d", iso_yr);
3353                         ap = rbuf;
3354                         }
3355                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
3356                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
3357                         ap = rbuf;
3358                         }
3359                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
3360                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
3361                         ap = rbuf;
3362                         }
3363                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
3364                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
3365                         ap = rbuf;
3366                         }
3367                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
3368                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
3369                         ap = rbuf;
3370                         }
3371                     else if (!strcmp ("STATUS", gbuf)) {
3372                         (void)sprintf (rbuf, "%08X", sim_last_cmd_stat);
3373                         ap = rbuf;
3374                         }
3375                     else if (!strcmp ("TSTATUS", gbuf)) {
3376                         (void)sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
3377                         ap = rbuf;
3378                         }
3379                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
3380                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3381                         ap = rbuf;
3382                         }
3383                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
3384                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3385                         ap = rbuf;
3386                         }
3387                     else if (!strcmp ("SIM_LOCALOPC", gbuf)) {
3388                         (void)sprintf (rbuf, "%s", sim_localopc ? "1" : "");
3389                         ap = rbuf;
3390                         }
3391                     else if (!strcmp ("SIM_QUIET", gbuf)) {
3392                         (void)sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
3393                         ap = rbuf;
3394                         }
3395                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
3396                         (void)sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
3397                         ap = rbuf;
3398                         }
3399                     else if (!strcmp ("HOSTID", gbuf)) {
3400 #if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__)
3401                         (void)sprintf (rbuf, "%ld", (long)gethostid());
3402 #else
3403                         (void)sprintf (rbuf, "00000000");
3404 #endif /* if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__) */
3405                         ap = rbuf;
3406                         }
3407                     else if (!strcmp ("UID", gbuf)) {
3408 #if defined(HAVE_UNISTD)
3409                         (void)sprintf (rbuf, "%ld", (long)getuid());
3410 #else
3411                         (void)sprintf (rbuf, "0");
3412 #endif /* if defined(HAVE_UNISTD) */
3413                         ap = rbuf;
3414                         }
3415                     else if (!strcmp ("GID", gbuf)) {
3416 #if defined(HAVE_UNISTD)
3417                         (void)sprintf (rbuf, "%ld", (long)getgid());
3418 #else
3419                         (void)sprintf (rbuf, "0");
3420 #endif /* if defined(HAVE_UNISTD) */
3421                         ap = rbuf;
3422                         }
3423                     else if (!strcmp ("EUID", gbuf)) {
3424 #if defined(HAVE_UNISTD)
3425                         (void)sprintf (rbuf, "%ld", (long)geteuid());
3426 #else
3427                         (void)sprintf (rbuf, "0");
3428 #endif /* if defined(HAVE_UNISTD) */
3429                         ap = rbuf;
3430                         }
3431                     else if (!strcmp ("EGID", gbuf)) {
3432 #if defined(HAVE_UNISTD)
3433                         (void)sprintf (rbuf, "%ld", (long)getegid());
3434 #else
3435                         (void)sprintf (rbuf, "0");
3436 #endif /* if defined(HAVE_UNISTD) */
3437                         ap = rbuf;
3438                         }
3439                     else if (!strcmp ("PID", gbuf)) {
3440 #if defined(HAVE_UNISTD)
3441                         (void)sprintf (rbuf, "%ld", (long)getpid());
3442 #else
3443                         (void)sprintf (rbuf, "0");
3444 #endif /* if defined(HAVE_UNISTD) */
3445                         ap = rbuf;
3446                         }
3447                     else if (!strcmp ("PPID", gbuf)) {
3448 #if defined(HAVE_UNISTD)
3449                         (void)sprintf (rbuf, "%ld", (long)getppid());
3450 #else
3451                         (void)sprintf (rbuf, "0");
3452 #endif /* if defined(HAVE_UNISTD) */
3453                         ap = rbuf;
3454                         }
3455                     else if (!strcmp ("PGID", gbuf)) {
3456 #if defined(HAVE_UNISTD)
3457                         (void)sprintf (rbuf, "%ld", (long)getpgid(getpid()));
3458 #else
3459                         (void)sprintf (rbuf, "0");
3460 #endif /* if defined(HAVE_UNISTD) */
3461                         ap = rbuf;
3462                         }
3463                     else if (!strcmp ("SID", gbuf)) {
3464 #if defined(HAVE_UNISTD)
3465                         (void)sprintf (rbuf, "%ld", (long)getsid(getpid()));
3466 #else
3467                         (void)sprintf (rbuf, "0");
3468 #endif /* if defined(HAVE_UNISTD) */
3469                         ap = rbuf;
3470                         }
3471                     else if (!strcmp ("ENDIAN", gbuf)) {
3472 #if ( defined(DECLITEND) && DECLITEND == 1 )
3473                         (void)sprintf (rbuf, "LITTLE");
3474 #elif ( defined(DECLITEND) && DECLITEND == 0 )
3475                         (void)sprintf (rbuf, "BIG");
3476 #else
3477                         (void)sprintf (rbuf, "UNKNOWN");
3478 #endif /* if ( defined(DECLITEND) && DECLITEND == 1 ) */
3479                         ap = rbuf;
3480                         }
3481                     else if (!strcmp("SIM_NAME", gbuf)) {
3482                         (void)sprintf (rbuf, "%s", sim_name);
3483                         ap = rbuf;
3484                         }
3485                     else if (!strcmp("SIM_VERSION", gbuf)) {
3486 #if defined(VER_H_GIT_VERSION)
3487                         (void)sprintf (rbuf, "%s", VER_H_GIT_VERSION);
3488 #else
3489                         (void)sprintf (rbuf, "UNKNOWN");
3490 #endif /* if defined(VER_H_GIT_VERSION) */
3491                         ap = rbuf;
3492                         }
3493                     else if (!strcmp("SIM_HASH", gbuf)) {
3494 #if defined(VER_H_GIT_HASH)
3495                         (void)sprintf (rbuf, "%s", VER_H_GIT_HASH);
3496 #else
3497                         (void)sprintf (rbuf, "0000000000000000000000000000000000000000");
3498 #endif /* if defined(VER_H_GIT_HASH) */
3499                         ap = rbuf;
3500                         }
3501                     else if (!strcmp("SIM_RELT", gbuf)) {
3502 #if defined(VER_H_GIT_RELT)
3503                         (void)sprintf (rbuf, "%s", VER_H_GIT_RELT);
3504 #else
3505                         (void)sprintf (rbuf, "X");
3506 #endif /* if defined(VER_H_GIT_RELT) */
3507                         ap = rbuf;
3508                         }
3509                     else if (!strcmp("SIM_DATE", gbuf)) {
3510 #if defined(VER_H_GIT_DATE)
3511                         (void)sprintf (rbuf, "%s", VER_H_GIT_DATE);
3512 #else
3513                         (void)sprintf (rbuf, "UNKNOWN");
3514 #endif /* if defined(VER_H_GIT_DATE) */
3515                         ap = rbuf;
3516                         }
3517                     else if ( (!strcmp("CPUS", gbuf)) \
3518                       || (!strcmp("SIM_PROCESSORS", gbuf) ) ) {
3519                         (void)sprintf(rbuf, "%u", nprocs);
3520                         ap = rbuf;
3521                         }
3522                     }
3523                 }
3524             if (ap) {                                   /* non-null arg? */
3525                 while (*ap && (op < oend))              /* copy the argument */
3526                     *op++ = *ap++;
3527                 }
3528             }
3529         else
3530             *op++ = *ip++;
3531     }
3532 *op = 0;                                                /* term buffer */
3533 strcpy (instr, tmpbuf);
3534 FREE (tmpbuf);
3535 return;
3536 }
3537 
3538 static
3539 int sim_cmp_string (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
3540 {
3541 long int v1, v2;
3542 char *ep1, *ep2;
3543 
3544 v1 = strtol(s1+1, &ep1, 0);
3545 v2 = strtol(s2+1, &ep2, 0);
3546 if ((ep1 != s1 + strlen (s1) - 1) ||
3547     (ep2 != s2 + strlen (s2) - 1))
3548     return strcmp (s1, s2);
3549 if (v1 == v2)
3550     return 0;
3551 if (v1 < v2)
3552     return -1;
3553 return 1;
3554 }
3555 
3556 /* Assert command */
3557 
3558 t_stat assert_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3559 {
3560 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3561 CONST char *tptr, *gptr;
3562 REG *rptr;
3563 uint32 idx = 0;
3564 t_value val;
3565 t_stat r;
3566 t_bool Not = FALSE;
3567 t_bool result;
3568 t_addr addr = 0;
3569 t_stat reason = SCPE_AFAIL; /* default fail reason */
3570 
3571 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3572                                                         /* get sw, default */
3573 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3574 # pragma diagnostic push
3575 # pragma diag_suppress = integer_sign_change
3576 #endif
3577 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3578 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3579 # pragma diagnostic pop
3580 #endif
3581 if (*cptr == 0)                                         /* must be more */
3582     return SCPE_2FARG;
3583 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3584 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3585     Not = TRUE;                                         /* remember that, and */
3586     cptr = (CONST char *)tptr;
3587     }
3588 if (*cptr == '"') {                                     /* quoted string comparison? */
3589     char op[CBUFSIZE];
3590     static struct {
3591         const char *op;
3592         int aval;
3593         int bval;
3594         t_bool invert;
3595         } *optr, compare_ops[] =
3596         {
3597             { "==",   0,  0, FALSE },
3598             { "EQU",  0,  0, FALSE },
3599             { "!=",   0,  0, TRUE  },
3600             { "NEQ",  0,  0, TRUE  },
3601             { "<",   -1, -1, FALSE },
3602             { "LSS", -1, -1, FALSE },
3603             { "<=",   0, -1, FALSE },
3604             { "LEQ",  0, -1, FALSE },
3605             { ">",    1,  1, FALSE },
3606             { "GTR",  1,  1, FALSE },
3607             { ">=",   0,  1, FALSE },
3608             { "GEQ",  0,  1, FALSE },
3609             { NULL }
3610         };
3611 
3612     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3613                                                     /* get first string */
3614     if (!*tptr)
3615         return SCPE_2FARG;
3616     cptr += strlen (gbuf);
3617     while (sim_isspace (*cptr))                     /* skip spaces */
3618         ++cptr;
3619     (void)get_glyph (cptr, op, '"');
3620     for (optr = compare_ops; optr->op; optr++)
3621         if (0 == strcmp (op, optr->op))
3622             break;
3623     if (!optr->op)
3624         return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3625     cptr += strlen (op);
3626     while (sim_isspace (*cptr))                         /* skip spaces */
3627         ++cptr;
3628     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3629                                                         /* get second string */
3630     if (*cptr) {                                        /* more? */
3631         if (flag)                                       /* ASSERT has no more args */
3632             return SCPE_2MARG;
3633         }
3634     else {
3635         if (!flag)
3636             return SCPE_2FARG;                          /* IF needs actions! */
3637         }
3638     result = sim_cmp_string (gbuf, gbuf2);
3639     result = ((result == optr->aval) || (result == optr->bval));
3640     if (optr->invert)
3641         result = !result;
3642     }
3643 else {
3644     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3645     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3646     if (rptr) {                                         /* got register? */
3647         if (*gptr == '[') {                             /* subscript? */
3648             if (rptr->depth <= 1)                       /* array register? */
3649                 return SCPE_ARG;
3650             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3651             if ((gptr == tptr) || (*tptr++ != ']'))
3652                 return SCPE_ARG;
3653             gptr = tptr;                                /* update */
3654             }
3655         else idx = 0;                                   /* not array */
3656         if (idx >= rptr->depth)                         /* validate subscript */
3657             return SCPE_SUB;
3658         }
3659     else {                                              /* not reg, check for memory */
3660         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3661             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3662         else if (sim_dfdev) //-V547
3663             addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix); //-V547
3664         if (gbuf == gptr)                               /* error? */
3665             return SCPE_NXREG;
3666         }
3667     if (*gptr != 0)                                     /* more? must be search */
3668         (void)get_glyph (gptr, gbuf, 0);
3669     else {
3670         if (*cptr == 0)                                 /* must be more */
3671             return SCPE_2FARG;
3672         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3673         }
3674     if (*cptr) {                                        /* more? */
3675         if (flag)                                       /* ASSERT has no more args */
3676             return SCPE_2MARG;
3677         }
3678     else {
3679         if (!flag)
3680             return SCPE_2FARG;                          /* IF needs actions! */
3681         }
3682     if (rptr) {                                         /* Handle register case */
3683 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3684 # pragma diagnostic push
3685 # pragma diag_suppress = integer_sign_change
3686 #endif
3687         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3688             (sim_stabr.boolop == -1))                   /* relational op reqd */
3689             return SCPE_MISVAL;
3690 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3691 # pragma diagnostic pop
3692 #endif
3693         val = get_rval (rptr, idx);                     /* get register value */
3694         result = test_search (&val, &sim_stabr);        /* test condition */
3695         }
3696     else {                                              /* Handle memory case */
3697 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3698 # pragma diagnostic push
3699 # pragma diag_suppress = integer_sign_change
3700 #endif
3701         if (sim_dfdev)
3702             if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3703                 (sim_staba.boolop == -1))                    /* relational op reqd */
3704                 return SCPE_MISVAL;
3705 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3706 # pragma diagnostic pop
3707 #endif
3708         if (sim_dfdev)
3709             reason = get_aval (addr, sim_dfdev, sim_dfunit); /* get data */
3710         if (reason != SCPE_OK)                          /* return if error */
3711             return reason;
3712         result = test_search (sim_eval, &sim_staba);    /* test condition */
3713         }
3714     }
3715 if (Not ^ result) {
3716     if (!flag)
3717         sim_brk_setact (cptr);                          /* set up IF actions */
3718     }
3719 else
3720     if (flag)
3721         return SCPE_AFAIL;                              /* return assert status */
3722 return SCPE_OK;
3723 }
3724 
3725 /* Send command */
3726 
3727 t_stat send_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3728 {
3729 char gbuf[CBUFSIZE];
3730 CONST char *tptr;
3731 uint8 dbuf[CBUFSIZE];
3732 uint32 dsize = 0;
3733 uint32 delay = 0;
3734 uint32 after = 0;
3735 t_stat r;
3736 SEND *snd = NULL;
3737 
3738 GET_SWITCHES (cptr);                                    /* get switches */
3739 tptr = get_glyph (cptr, gbuf, ',');
3740 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3741     r = tmxr_locate_line_send (gbuf, &snd);
3742     if (r != SCPE_OK)
3743       return r;
3744     cptr = tptr;
3745     tptr = get_glyph (tptr, gbuf, ',');
3746     }
3747 else
3748     snd = sim_cons_get_send ();
3749 
3750 while (*cptr) {
3751     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3752         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3753         if (r != SCPE_OK)
3754             return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3755         cptr = tptr;
3756         tptr = get_glyph (cptr, gbuf, ',');
3757         continue;
3758         }
3759     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3760         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3761         if (r != SCPE_OK)
3762             return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3763         cptr = tptr;
3764         tptr = get_glyph (cptr, gbuf, ',');
3765         continue;
3766         }
3767     if ((*cptr == '"') || (*cptr == '\''))
3768         break;
3769     return SCPE_ARG;
3770     }
3771 if (*cptr) {
3772     if ((*cptr != '"') && (*cptr != '\'')) //-V560
3773         return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3774     cptr = get_glyph_quoted (cptr, gbuf, 0);
3775     if (*cptr != '\0')
3776         return SCPE_2MARG;                  /* No more arguments */
3777 
3778     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3779         return sim_messagef (SCPE_ARG, "Invalid String\n");
3780     }
3781 if ((dsize == 0) && (delay == 0) && (after == 0))
3782     return SCPE_2FARG;
3783 return sim_send_input (snd, dbuf, dsize, after, delay);
3784 }
3785 
3786 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3787 {
3788 char gbuf[CBUFSIZE];
3789 CONST char *tptr;
3790 t_stat r;
3791 SEND *snd = NULL;
3792 
3793 tptr = get_glyph (cptr, gbuf, ',');
3794 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3795     r = tmxr_locate_line_send (gbuf, &snd);
3796     if (r != SCPE_OK)
3797       return r;
3798     cptr = tptr;
3799     }
3800 else
3801     snd = sim_cons_get_send ();
3802 if (*cptr)
3803     return SCPE_2MARG;
3804 return sim_show_send_input (st, snd);
3805 }
3806 
3807 t_stat expect_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3808 {
3809 char gbuf[CBUFSIZE];
3810 CONST char *tptr;
3811 EXPECT *exp = NULL;
3812 
3813 GET_SWITCHES (cptr);                                    /* get switches */
3814 tptr = get_glyph (cptr, gbuf, ',');
3815 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3816     cptr = tptr;
3817 } else {
3818     exp = sim_cons_get_expect ();
3819 }
3820 
3821 if (flag) {
3822     return sim_set_expect (exp, cptr);
3823 } else {
3824     if (exp == NULL) {
3825         exp = sim_cons_get_expect();
3826     }
3827     return sim_set_noexpect (exp, cptr);
3828 }
3829 }
3830 
3831 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3832 {
3833 char gbuf[CBUFSIZE];
3834 CONST char *tptr;
3835 EXPECT *exp = NULL;
3836 t_stat r;
3837 
3838 tptr = get_glyph (cptr, gbuf, ',');
3839 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3840     r = tmxr_locate_line_expect (gbuf, &exp);
3841     if (r != SCPE_OK)
3842         return r;
3843     cptr = tptr;
3844     }
3845 else
3846     exp = sim_cons_get_expect ();
3847 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3848     return SCPE_ARG;            /* String must be quote delimited */
3849 tptr = get_glyph_quoted (cptr, gbuf, 0);
3850 if (*tptr != '\0')
3851     return SCPE_2MARG;          /* No more arguments */
3852 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3853     return SCPE_ARG;            /* String must be quote delimited */
3854 return sim_exp_show (st, exp, gbuf);
3855 }
3856 
3857 /* Goto command */
3858 
3859 t_stat goto_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3860 {
3861 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3862 const char *cptr;
3863 long fpos;
3864 int32 saved_do_echo = sim_do_echo;
3865 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3866 
3867 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3868 (void)get_glyph (fcptr, gbuf1, 0);
3869 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
3870 fpos = ftell(sim_gotofile);                             /* Save start position */
3871 rewind(sim_gotofile);                                   /* start search for label */
3872 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
3873 sim_do_echo = 0;                                        /* Don't echo while searching for label */
3874 while (1) {
3875     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3876     if (cptr == NULL) break;                            /* exit on eof */
3877     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
3878     if (*cptr == 0) continue;                           /* ignore blank */
3879     if (*cptr != ':') continue;                         /* ignore non-labels */
3880     ++cptr;                                             /* skip : */
3881     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
3882     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
3883     if (0 == strcmp(gbuf, gbuf1)) {
3884         sim_brk_clract ();                              /* goto defangs current actions */
3885         sim_do_echo = saved_do_echo;                    /* restore echo mode */
3886         if (sim_do_echo)                                /* echo if -v */
3887             sim_printf("%s> %s\n", do_position(), cbuf);
3888         return SCPE_OK;
3889         }
3890     }
3891 sim_do_echo = saved_do_echo;                       /* restore echo mode         */
3892 fseek(sim_gotofile, fpos, SEEK_SET);               /* restore start position    */
3893 sim_goto_line[sim_do_depth] = saved_goto_line;     /* restore start line number */
3894 return SCPE_ARG;
3895 }
3896 
3897 /* Return command */
3898 
3899 /* The return command is invalid unless encountered in a do_cmd context,    */
3900 /* and in that context, it is handled as a special case inside of do_cmd()  */
3901 /* and not dispatched here, so if we get here a return has been issued from */
3902 /* interactive input */
3903 
3904 t_stat return_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3905 {
3906 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3907 }
3908 
3909 /* Shift command */
3910 
3911 /* The shift command is invalid unless encountered in a do_cmd context,    */
3912 /* and in that context, it is handled as a special case inside of do_cmd() */
3913 /* and not dispatched here, so if we get here a shift has been issued from */
3914 /* interactive input (it is not valid interactively since it would have to */
3915 /* mess with the program's argv which is owned by the C runtime library    */
3916 
3917 t_stat shift_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3918 {
3919 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3920 }
3921 
3922 /* Call command */
3923 
3924 /* The call command is invalid unless encountered in a do_cmd context,     */
3925 /* and in that context, it is handled as a special case inside of do_cmd() */
3926 /* and not dispatched here, so if we get here a call has been issued from  */
3927 /* interactive input                                                       */
3928 
3929 t_stat call_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3930 {
3931 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3932 const char *cptr;
3933 
3934 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3935 cptr = get_glyph (fcptr, gbuf, 0);
3936 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
3937 (void)snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3938 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
3939 return do_cmd_label (flag, cbuf, gbuf);
3940 }
3941 
3942 /* On command */
3943 
3944 t_stat on_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3945 {
3946 char gbuf[CBUFSIZE];
3947 t_stat cond;
3948 
3949 cptr = get_glyph (cptr, gbuf, 0);
3950 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
3951 if (0 == strcmp("ERROR", gbuf))
3952     cond = 0;
3953 else
3954     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3955         return SCPE_ARG;
3956 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
3957     FREE(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
3958     sim_on_actions[sim_do_depth][cond] = NULL; }
3959 else {
3960     sim_on_actions[sim_do_depth][cond] =
3961         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3962     if (!sim_on_actions[sim_do_depth][cond])
3963       {
3964         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3965                        __func__, __FILE__, __LINE__);
3966 #if defined(USE_BACKTRACE)
3967 # if defined(SIGUSR2)
3968         (void)raise(SIGUSR2);
3969         /*NOTREACHED*/ /* unreachable */
3970 # endif /* if defined(SIGUSR2) */
3971 #endif /* if defined(USE_BACKTRACE) */
3972         abort();
3973       }
3974     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3975     }
3976 return SCPE_OK;
3977 }
3978 
3979 /* noop command */
3980 
3981 t_stat noop_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3982 {
3983 if (cptr && (*cptr != 0))                               /* now eol? */
3984     return SCPE_2MARG;
3985 return SCPE_OK;                                         /* we're happy doing nothing */
3986 }
3987 
3988 /* Set on/noon routine */
3989 
3990 t_stat set_on (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3991 {
3992 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
3993     char gbuf[CBUFSIZE];
3994 
3995     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
3996     if (((MATCH_CMD(gbuf,"INHERIT")) &&
3997          (MATCH_CMD(gbuf,"NOINHERIT"))) || //-V600
3998         (*cptr))
3999         return SCPE_2MARG;
4000     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT"))) //-V560
4001         sim_on_inherit = 1;
4002     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT"))) //-V560
4003         sim_on_inherit = 0;
4004     return SCPE_OK;
4005     }
4006 if (cptr && (*cptr != 0))                               /* now eol? */
4007     return SCPE_2MARG;
4008 sim_on_check[sim_do_depth] = flag;
4009 if ((sim_do_depth != 0) &&
4010     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
4011     sim_on_actions[sim_do_depth][0] =                   /* No, so make "RETURN" */
4012         (char *)malloc(1+strlen("RETURN"));             /* be the default action */
4013     strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
4014     }
4015 if ((sim_do_depth != 0) &&
4016     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
4017     sim_on_actions[sim_do_depth][SCPE_AFAIL] =          /* No, so make "RETURN" */
4018         (char *)malloc(1+strlen("RETURN"));             /* be the action */
4019     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
4020     }
4021 return SCPE_OK;
4022 }
4023 
4024 /* Set verify/noverify routine */
4025 
4026 t_stat set_verify (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4027 {
4028 if (cptr && (*cptr != 0))                               /* now eol? */
4029     return SCPE_2MARG;
4030 if (flag == sim_do_echo)                                /* already set correctly? */
4031     return SCPE_OK;
4032 sim_do_echo = flag;
4033 return SCPE_OK;
4034 }
4035 
4036 /* Set message/nomessage routine */
4037 
4038 t_stat set_message (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4039 {
4040 if (cptr && (*cptr != 0))                               /* now eol? */
4041     return SCPE_2MARG;
4042 if (flag == sim_show_message)                           /* already set correctly? */
4043     return SCPE_OK;
4044 sim_show_message = flag;
4045 return SCPE_OK;
4046 }
4047 
4048 /* Set localopc/nolocalopc routine */
4049 
4050 t_stat set_localopc (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4051 {
4052 if (cptr && (*cptr != 0))                               /* now eol? */
4053     return SCPE_2MARG;
4054 if (flag == sim_localopc)                               /* already set correctly? */
4055     return SCPE_OK;
4056 sim_localopc = flag;
4057 return SCPE_OK;
4058 }
4059 /* Set quiet/noquiet routine */
4060 
4061 t_stat set_quiet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4062 {
4063 if (cptr && (*cptr != 0))                               /* now eol? */
4064     return SCPE_2MARG;
4065 if (flag == sim_quiet)                                  /* already set correctly? */
4066     return SCPE_OK;
4067 sim_quiet = flag;
4068 return SCPE_OK;
4069 }
4070 
4071 /* Set environment routine */
4072 
4073 t_stat sim_set_environment (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4074 {
4075 char varname[CBUFSIZE];
4076 
4077 if ((NULL == cptr) || (*cptr == 0))                            /* now eol? */
4078     return SCPE_2FARG;
4079 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
4080 setenv(varname, cptr, 1);
4081 return SCPE_OK;
4082 }
4083 
4084 /* Set command */
4085 
4086 t_stat set_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4087 {
4088 uint32 lvl = 0;
4089 t_stat r;
4090 char gbuf[CBUFSIZE], *cvptr;
4091 CONST char *svptr;
4092 DEVICE *dptr;
4093 UNIT *uptr;
4094 MTAB *mptr;
4095 CTAB *gcmdp;
4096 C1TAB *ctbr = NULL, *glbr;
4097 
4098 GET_SWITCHES (cptr);                                    /* get switches */
4099 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
4100     return SCPE_2FARG;
4101 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
4102 
4103 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4104     uptr = dptr->units;                                 /* first unit */
4105     ctbr = set_dev_tab;                                 /* global table */
4106     lvl = MTAB_VDV;                                     /* device match */
4107     GET_SWITCHES (cptr);                                /* get more switches */
4108     }
4109 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4110     if (uptr == NULL)                                   /* invalid unit */
4111         return SCPE_NXUN;
4112     ctbr = set_unit_tab;                                /* global table */
4113     lvl = MTAB_VUN;                                     /* unit match */
4114     GET_SWITCHES (cptr);                                /* get more switches */
4115     }
4116 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
4117     GET_SWITCHES (cptr);                                /* get more switches */
4118     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
4119     }
4120 else {
4121     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4122         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4123             *cvptr++ = 0;
4124         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
4125             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
4126                 dptr = sim_dflt_dev;
4127                 cptr = svptr;
4128                 while (sim_isspace(*cptr))
4129                     ++cptr;
4130                 break;
4131                 }
4132             }
4133         }
4134     if (!dptr)
4135         return SCPE_NXDEV;                              /* no match */
4136     lvl = MTAB_VDV;                                     /* device match */
4137     uptr = dptr->units;                                 /* first unit */
4138     }
4139 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4140     return SCPE_2FARG;
4141 GET_SWITCHES (cptr);                                    /* get more switches */
4142 
4143 while (*cptr != 0) {                                    /* do all mods */
4144     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
4145     if (0 == strcmp (gbuf, ";"))
4146         break;
4147     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4148         *cvptr++ = 0;
4149     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4150         if ((mptr->mstring) &&                          /* match string */
4151             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
4152             if (mptr->mask & MTAB_XTD) {                /* extended? */
4153                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
4154                     return SCPE_ARG;
4155                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
4156                     return SCPE_UDIS;                   /* unit disabled? */
4157                 if (mptr->valid) {                      /* validation rtn? */
4158                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
4159                         svptr = get_glyph_quoted (svptr, gbuf, ',');
4160                         if ((cvptr = strchr (gbuf, '='))) {
4161                             *cvptr++ = 0;
4162                             cptr = svptr;
4163                             }
4164                         }
4165                     else {
4166                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
4167                             (void)get_glyph_nc (svptr, gbuf, ',');
4168                             if ((cvptr = strchr (gbuf, '=')))
4169                                 *cvptr++ = 0;
4170                             }
4171                         }
4172                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
4173                     if (r != SCPE_OK)
4174                         return r;
4175                     }
4176                 else if (!mptr->desc)                   /* value desc? */
4177                     break;
4178                 else if (cvptr)                         /* = value? */
4179                     return SCPE_ARG;
4180                 else *((int32 *) mptr->desc) = mptr->match;
4181                 }                                       /* end if xtd */
4182             else {                                      /* old style */
4183                 if (cvptr)                              /* = value? */
4184                     return SCPE_ARG;
4185                 if (uptr->flags & UNIT_DIS)             /* disabled? */
4186                      return SCPE_UDIS;
4187                 if ((mptr->valid) &&                    /* invalid? */
4188                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
4189                     return r;
4190                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
4191                     (mptr->match & mptr->mask);         /* set new value */
4192                 }                                       /* end else xtd */
4193             break;                                      /* terminate for */
4194             }                                           /* end if match */
4195         }                                               /* end for */
4196     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4197         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
4198             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
4199             if (r != SCPE_OK)
4200                 return r;
4201             }
4202         else if (!dptr->modifiers)                      /* no modifiers? */
4203             return SCPE_NOPARAM;
4204         else return SCPE_NXPAR;
4205         }                                               /* end if no mat */
4206     }                                                   /* end while */
4207 return SCPE_OK;                                         /* done all */
4208 }
4209 
4210 /* Match CTAB/CTAB1 name */
4211 
4212 CTAB *find_ctab (CTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4213 {
4214 if (!tab)
4215     return NULL;
4216 for (; tab->name != NULL; tab++) {
4217     if (MATCH_CMD (gbuf, tab->name) == 0)
4218         return tab;
4219     }
4220 return NULL;
4221 }
4222 
4223 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4224 {
4225 if (!tab)
4226     return NULL;
4227 for (; tab->name != NULL; tab++) {
4228     if (MATCH_CMD (gbuf, tab->name) == 0)
4229         return tab;
4230     }
4231 return NULL;
4232 }
4233 
4234 /* Set device data radix routine */
4235 
4236 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4237 {
4238 if (cptr)
4239     return SCPE_ARG;
4240 dptr->dradix = flag & 037;
4241 return SCPE_OK;
4242 }
4243 
4244 /* Set device enabled/disabled routine */
4245 
4246 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4247 {
4248 UNIT *up;
4249 uint32 i;
4250 
4251 if (cptr)
4252     return SCPE_ARG;
4253 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
4254     return SCPE_NOFNC;
4255 if (flag) {                                             /* enable? */
4256     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
4257         return SCPE_OK;
4258     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
4259     }
4260 else {
4261     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
4262         return SCPE_OK;
4263     for (i = 0; i < dptr->numunits; i++) {              /* check units */
4264         up = (dptr->units) + i;                         /* att or active? */
4265         if ((up->flags & UNIT_ATT) || sim_is_active (up))
4266             return SCPE_NOFNC;                          /* can't do it */
4267         }
4268     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
4269     }
4270 if (dptr->reset)                                        /* reset device */
4271     return dptr->reset (dptr);
4272 else return SCPE_OK;
4273 }
4274 
4275 /* Set unit enabled/disabled routine */
4276 
4277 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4278 {
4279 if (cptr)
4280     return SCPE_ARG;
4281 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
4282     return SCPE_NOFNC;
4283 if (flag)                                               /* enb? enable */
4284     uptr->flags = uptr->flags & ~UNIT_DIS;
4285 else {
4286     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
4287         sim_is_active (uptr))                           /* more tests */
4288         return SCPE_NOFNC;
4289     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
4290     }
4291 return SCPE_OK;
4292 }
4293 
4294 /* Set device debug enabled/disabled routine */
4295 
4296 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4297 {
4298 char gbuf[CBUFSIZE];
4299 DEBTAB *dep;
4300 
4301 if ((dptr->flags & DEV_DEBUG) == 0)
4302     return SCPE_NOFNC;
4303 if (cptr == NULL) {                                     /* no arguments? */
4304     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
4305     if (flag && dptr->debflags) {                       /* enable with table? */
4306         for (dep = dptr->debflags; dep->name != NULL; dep++)
4307             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
4308         }
4309     return SCPE_OK;
4310     }
4311 if (dptr->debflags == NULL)                             /* must have table */
4312     return SCPE_ARG;
4313 while (*cptr) {
4314     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
4315     for (dep = dptr->debflags; dep->name != NULL; dep++) {
4316         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
4317             if (flag)
4318                 dptr->dctrl = dptr->dctrl | dep->mask;
4319             else dptr->dctrl = dptr->dctrl & ~dep->mask;
4320             break;
4321             }
4322         }                                               /* end for */
4323     if (dep->mask == 0)                                 /* no match? */
4324         return SCPE_ARG;
4325     }                                                   /* end while */
4326 return SCPE_OK;
4327 }
4328 
4329 /* Show command */
4330 
4331 t_stat show_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4332 {
4333 t_stat r = SCPE_IERR;
4334 
4335 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
4336                                                         /* get sw, ofile */
4337 if (NULL == cptr)                                              /* error? */
4338     return r;
4339 if (sim_ofile) {                                        /* output file? */
4340     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
4341     fclose (sim_ofile);
4342     }
4343 else {
4344     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
4345     if (sim_log && (sim_log != stdout))
4346         show_cmd_fi (sim_log, flag, cptr);
4347     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
4348         show_cmd_fi (sim_deb, flag, cptr);
4349     }
4350 return r;
4351 }
4352 
4353 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4354 {
4355 uint32 lvl = 0xFFFFFFFF;
4356 char gbuf[CBUFSIZE], *cvptr;
4357 CONST char *svptr;
4358 DEVICE *dptr;
4359 UNIT *uptr;
4360 MTAB *mptr;
4361 SHTAB *shtb = NULL, *shptr;
4362 
4363 GET_SWITCHES (cptr);                                    /* get switches */
4364 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4365     return SCPE_2FARG;
4366 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
4367 
4368 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4369     uptr = dptr->units;                                 /* first unit */
4370     shtb = show_dev_tab;                                /* global table */
4371     lvl = MTAB_VDV;                                     /* device match */
4372     GET_SWITCHES (cptr);                                /* get more switches */
4373     }
4374 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4375     if (uptr == NULL)                                   /* invalid unit */
4376         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
4377     if (uptr->flags & UNIT_DIS)                         /* disabled? */
4378         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
4379     shtb = show_unit_tab;                               /* global table */
4380     lvl = MTAB_VUN;                                     /* unit match */
4381     GET_SWITCHES (cptr);                                /* get more switches */
4382     }
4383 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
4384     GET_SWITCHES (cptr);                                /* get more switches */
4385     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
4386     }
4387 else {
4388     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4389         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4390             *cvptr++ = 0;
4391         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
4392             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
4393                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) || //-V600
4394                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
4395                 dptr = sim_dflt_dev;
4396                 lvl = MTAB_VDV;                         /* device match */
4397                 cptr = svptr;
4398                 while (sim_isspace(*cptr))
4399                     ++cptr;
4400                 break;
4401                 }
4402             }
4403         }
4404     if (!dptr) {
4405         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
4406             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
4407         else
4408             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
4409         }
4410     }
4411 
4412 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
4413     return (lvl == MTAB_VDV)?
4414         show_device (ofile, dptr, 0):
4415         show_unit (ofile, dptr, uptr, -1);
4416     }
4417 GET_SWITCHES (cptr);                                    /* get more switches */
4418 
4419 while (*cptr != 0) {                                    /* do all mods */
4420     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
4421     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4422         *cvptr++ = 0;
4423     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4424         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
4425             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
4426             ((mptr->disp && mptr->pstring &&            /* named disp? */
4427             (MATCH_CMD (gbuf, mptr->pstring) == 0))
4428             )) {
4429             if (cvptr && !MODMASK(mptr,MTAB_SHP))
4430                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
4431             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
4432             break;
4433             }                                           /* end if */
4434         }                                               /* end for */
4435     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4436         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
4437             t_stat r;
4438 
4439             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
4440             if (r != SCPE_OK)
4441                 return r;
4442             }
4443         else {
4444             if (!dptr->modifiers)                       /* no modifiers? */
4445                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
4446             else
4447                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
4448             }
4449         }                                               /* end if */
4450     }                                                   /* end while */
4451 return SCPE_OK;
4452 }
4453 
4454 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4455 {
4456 if (!tab)
4457     return NULL;
4458 for (; tab->name != NULL; tab++) {
4459     if (MATCH_CMD (gbuf, tab->name) == 0)
4460         return tab;
4461     }
4462 return NULL;
4463 }
4464 
4465 /* Show device and unit */
4466 
4467 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4468 {
4469 uint32 j, udbl, ucnt;
4470 UNIT *uptr;
4471 int32 toks = 0;
4472 
4473 if (strcmp(sim_dname (dptr),"SYS") != 0) {
4474 (void)fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
4475 if ((flag == 2) && dptr->description) {
4476     (void)fprintf (st, "\t%s\n", dptr->description(dptr));
4477     }
4478 else {
4479     if ((sim_switches & SWMASK ('D')) && dptr->description)
4480         (void)fprintf (st, "\t%s\n", dptr->description(dptr));
4481     }
4482 if (qdisable (dptr)) {                                  /* disabled? */
4483     (void)fprintf (st, "\tdisabled\n");
4484     return SCPE_OK;
4485     }
4486 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
4487     uptr = dptr->units + j;
4488     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
4489         ucnt++;
4490     else if (uptr->flags & UNIT_DISABLE)
4491         udbl++;                                         /* count user-disabled */
4492     }
4493 //show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
4494 if (dptr->numunits == 0) {
4495     // if (toks) //-V547
4496     //     (void)fprintf (st, "\n");
4497 }
4498  else
4499 {
4500     if (ucnt == 0) {
4501         fprint_sep (st, &toks);
4502         (void)fprintf (st, "all units disabled\n");
4503         }
4504     else if ((ucnt + udbl) == 1) {
4505         fprint_sep (st, &toks);
4506         (void)fprintf (st, " 1 unit\n");
4507         }
4508     else if ((ucnt > 1) || (udbl > 0)) {
4509         fprint_sep (st, &toks);
4510         (void)fprintf (st, "%2.d units\n", ucnt + udbl);
4511         }
4512     else
4513         if ((flag != 2) || !dptr->description || toks)
4514             (void)fprintf (st, "\n");
4515     toks = 0;
4516 }
4517 if (flag)                                               /* dev only? */
4518     return SCPE_OK;
4519 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
4520     uptr = dptr->units + j;
4521     if ((uptr->flags & UNIT_DIS) == 0)
4522         show_unit (st, dptr, uptr, ucnt + udbl);
4523     }
4524 }
4525 return SCPE_OK;
4526 }
4527 
4528 void fprint_sep (FILE *st, int32 *tokens)
     /* [previous][next][first][last][top][bottom][index][help] */
4529 {
4530 (void)fprintf (st, "%s", (*tokens > 0) ? "" : "\t");
4531 *tokens += 1;
4532 }
4533 
4534 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4535 {
4536 int32 u = (int32)(uptr - dptr->units);
4537 int32 toks = 0;
4538 
4539 if (flag > 1)
4540     (void)fprintf (st, "   %s%d \n", sim_dname (dptr), u);
4541 else if (flag < 0)
4542     (void)fprintf (st, " %s%d ", sim_dname (dptr), u);
4543 if (uptr->flags & UNIT_ATT) {
4544     fprint_sep (st, &toks);
4545     (void)fprintf (st, "status   : attached to %s", uptr->filename);
4546     if (uptr->flags & UNIT_RO)
4547         (void)fprintf (st, ", read only");
4548     }
4549 else {
4550     if (uptr->flags & UNIT_ATTABLE) {
4551         fprint_sep (st, &toks);
4552         (void)fprintf (st, "status   : not attached");
4553         }
4554     }
4555 if ((uptr->capac > 0) && (uptr->flags & UNIT_FIX)) {
4556     fprint_sep (st, &toks);
4557     fprint_capac (st, dptr, uptr);
4558     }
4559 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
4560 if (toks || (flag < 0) || (flag > 1))
4561     (void)fprintf (st, "\n");
4562 return SCPE_OK;
4563 }
4564 
4565 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4566 {
4567 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4568 t_offset kval = (t_offset)((uptr->flags & UNIT_BINK) ? 1024: 1000);
4569 t_offset mval;
4570 t_offset psize = (t_offset)uptr->capac;
4571 char *scale, *width;
4572 
4573 if (sim_switches & SWMASK ('B'))
4574     kval = 1024;
4575 mval = kval * kval;
4576 if (dptr->flags & DEV_SECTORS) {
4577     kval = kval / 512;
4578     mval = mval / 512;
4579     }
4580 if ((dptr->dwidth / dptr->aincr) > 8)
4581     width = "W";
4582 else
4583     width = "B";
4584 if ((psize < (kval * 10)) &&
4585     (0 != (psize % kval))) {
4586     scale = "";
4587     }
4588 else if ((psize < (mval * 10)) &&
4589          (0 != (psize % mval))){
4590     scale = "K";
4591     psize = psize / kval;
4592     }
4593 else {
4594     scale = "M";
4595     psize = psize / mval;
4596     }
4597 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4598 (void)sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4599 return capac_buf;
4600 }
4601 
4602 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4603 {
4604 (void)fprintf (st, " %s", sprint_capac (dptr, uptr));
4605 }
4606 
4607 /* Show <global name> processors  */
4608 
4609 extern void print_default_base_system_script (void);
4610 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] */
4611 {
4612 #if !defined(PERF_STRIP)
4613   print_default_base_system_script();
4614 #endif /* if !defined(PERF_STRIP) */
4615   return 0;
4616 }
4617 
4618 static void printp (unsigned char * PROM, char * label, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4619   sim_printf (" %s ", label);
4620   sim_printf ("   %2d     %3o(8)     '", length, offset);
4621   for (int l = 0; l < length; l ++)
4622     {
4623       unsigned int byte = PROM[offset + l];
4624       if (byte == 255)
4625         {
4626           byte = ' ';
4627         }
4628       sim_printf (isprint (byte) ? "%c" : "\\%03o", byte);
4629     }
4630   sim_printf ("'\r\n");
4631 }
4632 
4633 static void strip_spaces(char* str) {
     /* [previous][next][first][last][top][bottom][index][help] */
4634   int i, x;
4635   for (i=x=0; str[i]; ++i)
4636     {
4637       if (!isspace((int)str[i]) || (i > 0 && !isspace((int)str[(int)(i-1)]))) //-V781
4638         {
4639           str[x++] = str[i];
4640         }
4641     }
4642   str[x] = '\0';
4643   i = -1;
4644   x = 0;
4645   while (str[x] != '\0')
4646     {
4647       if (str[x] != ' ' && str[x] != '\t' && str[x] != '\n')
4648         {
4649           i=x;
4650         }
4651       x++;
4652     }
4653   str[i+1] = '\0';
4654 }
4655 
4656 static void printpq (unsigned char * PROM, FILE * st, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4657   char sx[1024];
4658   sx[1023] = '\0';
4659   unsigned int lastbyte = 0;
4660   for (int l = 0; l < length; l ++)
4661     {
4662       unsigned int byte = PROM[offset + l];
4663       if (byte == 255)
4664         {
4665           byte = 20;
4666         }
4667       if ((lastbyte != 20) && (byte != 20))
4668         {
4669           (void)sprintf(&sx[l], isprint (byte) ? "%c" : " ", byte);
4670         }
4671       lastbyte = byte;
4672     }
4673   strip_spaces(sx);
4674   (void)fprintf (st, "%s", sx);
4675 }
4676 
4677 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4678 {
4679   unsigned char PROM[1024];
4680   setupPROM (0, PROM);
4681 
4682   sim_printf (" PROM size: %llu bytes\r\n",
4683               (long long unsigned)sizeof(PROM));
4684   sim_printf (" PROM initialization data:\r\n\r\n");
4685 
4686   sim_printf ("     Field Description      Length   Offset              Contents\r\n");
4687   sim_printf (" ========================= ======== ======== ==================================\r\n");
4688   sim_printf ("\r\n");
4689 
4690   //                      Field                 Offset       Length
4691   //             -------------------------    ----------   ----------
4692   printp (PROM, "CPU Model                ",       0,          11);
4693   printp (PROM, "CPU Serial               ",      11,          11);
4694   printp (PROM, "Ship Date                ",      22,           6);
4695   printp (PROM, "PROM Layout Version      ",      60,           1);
4696   printp (PROM, "Release Git Commit Date  ",      70,          10);
4697   printp (PROM, "Release Major            ",      80,           3);
4698   printp (PROM, "Release Minor            ",      83,           3);
4699   printp (PROM, "Release Patch            ",      86,           3);
4700   printp (PROM, "Release Iteration        ",      89,           3);
4701   printp (PROM, "Release Build Number     ",      92,           8);  /* Reserved */
4702   printp (PROM, "Release Type             ",     100,           1);
4703   printp (PROM, "Release Version Text     ",     101,          29);
4704   printp (PROM, "Build Architecture       ",     130,          20);
4705   printp (PROM, "Build Operating System   ",     150,          20);
4706   printp (PROM, "Target Architecture      ",     170,          20);
4707   printp (PROM, "Target Operating System  ",     190,          20);
4708 
4709   sim_printf("\r\n");
4710   return 0;
4711 }
4712 
4713 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4714 {
4715     (void)fprintf (st, "\r Build Information:\n");
4716 #if defined(BUILDINFO_scp)
4717     (void)fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4718 #else
4719     (void)fprintf (st, "\r\n      Compilation info: Not available\n" );
4720 #endif
4721 #if !defined(__CYGWIN__)
4722 # if !defined(__APPLE__)
4723 #  if !defined(_AIX)
4724 #   if !defined(__MINGW32__)
4725 #    if !defined(__MINGW64__)
4726 #     if !defined(CROSS_MINGW32)
4727 #      if !defined(CROSS_MINGW64)
4728 #       if !defined(_WIN32)
4729 #        if !defined(__HAIKU__)
4730     (void)dl_iterate_phdr (dl_iterate_phdr_callback, NULL);
4731     if (dl_iterate_phdr_callback_called)
4732         (void)fprintf (st, "\n");
4733     dl_iterate_phdr_callback_called = 0;
4734 #        endif
4735 #       endif
4736 #      endif
4737 #     endif
4738 #    endif
4739 #   endif
4740 #  endif
4741 # endif
4742 #endif
4743 #if defined(UV_VERSION_MAJOR) && \
4744     defined(UV_VERSION_MINOR) && \
4745     defined(UV_VERSION_PATCH)
4746 # if defined(UV_VERSION_MAJOR)
4747 #  if !defined(UV_VERSION_MINOR) && \
4748       !defined(UV_VERSION_PATCH) && \
4749       !defined(UV_VERSION_SUFFIX)
4750     (void)fprintf (st, "\r\n    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4751 #  endif /* if !defined(UV_VERSION_MINOR) && !defined(UV_VERSION_PATCH) && defined(UV_VERSION_SUFFIX) */
4752 #  if defined(UV_VERSION_MINOR)
4753 #   if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX)
4754     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4755                    UV_VERSION_MINOR);
4756 #   endif /* if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX) */
4757 #   if defined(UV_VERSION_PATCH)
4758 #    if !defined(UV_VERSION_SUFFIX)
4759     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4760                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4761 #    endif /* if !defined(UV_VERSION_SUFFIX) */
4762 #    if defined(UV_VERSION_SUFFIX)
4763     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4764                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4765 #     if defined(UV_VERSION_IS_RELEASE)
4766 #      if UV_VERSION_IS_RELEASE == 1
4767 #       define UV_RELEASE_TYPE " (release)"
4768 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4769 #      if UV_VERSION_IS_RELEASE == 0
4770 #       define UV_RELEASE_TYPE "-dev"
4771 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4772 #      if !defined(UV_RELEASE_TYPE)
4773 #       define UV_RELEASE_TYPE ""
4774 #      endif /* if !defined(UV_RELEASE_TYPE) */
4775 #      if defined(UV_RELEASE_TYPE)
4776     (void)fprintf (st, "%s", UV_RELEASE_TYPE);
4777 #      endif /* if defined(UV_RELEASE_TYPE) */
4778 #     endif /* if defined(UV_VERSION_IS_RELEASE) */
4779 #    endif /* if defined(UV_VERSION_SUFFIX) */
4780 #   endif /* if defined(UV_VERSION_PATCH) */
4781 #  endif /* if defined(UV_VERSION_MINOR) */
4782     unsigned int CurrentUvVersion = uv_version();
4783     if (CurrentUvVersion > 0)
4784         if (uv_version_string() != NULL)
4785             (void)fprintf (st, "; %s in use", uv_version_string());
4786 # endif /* if defined(UV_VERSION_MAJOR) */
4787 #else
4788     (void)fprintf (st, "\r\n    Event loop library: Using libuv (or compatible) library, unknown version");
4789 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4790         *    defined(UV_VERSION_MINOR) &&  \
4791         *    defined(UV_VERSION_PATCH)     \
4792         */
4793     (void)fprintf (st, "\r\n   Log support library: Built with libsir %d.%d.%d%s%s; %s%s in use",
4794                    SIR_VERSION_MAJOR, SIR_VERSION_MINOR, SIR_VERSION_PATCH,
4795                    SIR_VERSION_SUFFIX, SIR_VERSION_IS_RELEASE ? " (release)" : "",
4796                    sir_getversionstring(), SIR_VERSION_IS_RELEASE ? "" : sir_isprerelease() ? "" : " (release)");
4797 #if defined(DECNUMBERLOC)
4798 # if defined(DECVERSION)
4799 #  if defined(DECVERSEXT)
4800     (void)fprintf (st, "\r\n          Math library: %s-%s", DECVERSION, DECVERSEXT);
4801 #  else
4802 #   if defined(DECNLAUTHOR)
4803     (void)fprintf (st, "\r\n          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4804 #   else
4805     (void)fprintf (st, "\r\n          Math library: %s", DECVERSION);
4806 #   endif /* if defined(DECNLAUTHOR) */
4807 #  endif /* if defined(DECVERSEXT) */
4808 # else
4809     (void)fprintf (st, "\r\n          Math library: decNumber, unknown version");
4810 # endif /* if defined(DECVERSION) */
4811 #endif /* if defined(DECNUMBERLOC) */
4812 #if defined(LOCKLESS)
4813     (void)fprintf (st, "\r\n     Atomic operations: ");
4814 # if defined(AIX_ATOMICS)
4815     (void)fprintf (st, "C11 and IBM AIX-style");
4816 # elif defined(BSD_ATOMICS)
4817     (void)fprintf (st, "C11 and FreeBSD-style");
4818 # elif defined(GNU_ATOMICS)
4819     (void)fprintf (st, "C11 and GNU-style");
4820 # elif defined(SYNC_ATOMICS)
4821     (void)fprintf (st, "C11 and GNU sync-style");
4822 # elif defined(ISO_ATOMICS)
4823     (void)fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4824 # elif defined(NT_ATOMICS)
4825     (void)fprintf (st, "C11 and Windows NT interlocked operations");
4826 # endif
4827 #endif /* if defined(LOCKLESS) */
4828     (void)fprintf (st, "\r\n          File locking: ");
4829 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4830     (void)fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4831 #endif
4832 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4833     (void)fprintf (st, "POSIX-style fcntl() locking");
4834 #endif
4835 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4836     (void)fprintf (st, "BSD-style flock() locking");
4837 #endif
4838 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4839     (void)fprintf (st, "No file locking available");
4840 #endif
4841     (void)fprintf (st, "\r\n     Backtrace support: ");
4842 #if defined(USE_BACKTRACE)
4843     (void)fprintf (st, "Enabled (libbacktrace)");
4844 #else
4845     (void)fprintf (st, "Disabled");
4846 #endif /* if defined(USE_BACKTRACE) */
4847     (void)fprintf (st, "\r\n       Windows support: ");
4848 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
4849 # if defined(__MINGW64_VERSION_STR)
4850     (void)fprintf (st, "Built with MinGW-w64 %s", __MINGW64_VERSION_STR);
4851 # elif defined(__MINGW32_MAJOR_VERSION) && defined(__MINGW32_MINOR_VERSION)
4852     (void)fprintf (st, "Built with MinGW %d.%d", __MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);
4853 # else
4854     (void)fprintf (st, "Built with MinGW");
4855 # endif
4856 
4857 # if defined(MINGW_CRT)
4858     (void)fprintf (st, "; %s", MINGW_CRT);
4859 #  if !defined(_UCRT)
4860 #   if defined(__MSVCRT_VERSION__)
4861 #    if __MSVCRT_VERSION__ > 0x00
4862     (void)fprintf (st, " %d.%d", (__MSVCRT_VERSION__ >> CHAR_BIT) & UCHAR_MAX, __MSVCRT_VERSION__ & UCHAR_MAX);
4863 #    endif
4864 #   endif
4865 #  else
4866 
4867     struct UCRTVersion ucrtversion;
4868     int result = GetUCRTVersion (&ucrtversion);
4869 
4870     if (result == 0)
4871       (void)fprintf (st, " %u.%u.%u.%u",
4872                      ucrtversion.ProductVersion[1], ucrtversion.ProductVersion[0],
4873                      ucrtversion.ProductVersion[3], ucrtversion.ProductVersion[2]);
4874 #  endif
4875     (void)fprintf (st, " in use");
4876 # endif
4877 #elif defined(__CYGWIN__)
4878     struct utsname utsname;
4879     (void)fprintf (st, "Built with Cygwin %d.%d.%d",
4880                    CYGWIN_VERSION_DLL_MAJOR / 1000,
4881                    CYGWIN_VERSION_DLL_MAJOR % 1000,
4882                    CYGWIN_VERSION_DLL_MINOR);
4883     if (uname(&utsname) == 0)
4884       fprintf (st, "; %s in use", utsname.release);
4885 #else
4886     (void)fprintf (st, "Disabled");
4887 #endif
4888 
4889     (void)fprintf (st, "\r\n");
4890     return 0;
4891 }
4892 
4893 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4894 {
4895 const char *arch = "";
4896 char *whydirty = " ";
4897 int dirty = 0;
4898 
4899 if (cptr && (*cptr != 0))
4900     return SCPE_2MARG;
4901 if (flag) {
4902         (void)fprintf (st, " %s Simulator:", sim_name);
4903 #if defined(USE_DUMA)
4904 # undef NO_SUPPORT_VERSION
4905 # define NO_SUPPORT_VERSION 1
4906         nodist++;
4907 #endif /* if defined(USE_DUMA) */
4908 #if defined(NO_SUPPORT_VERSION) ||  \
4909     defined(WITH_SOCKET_DEV)    ||  \
4910     defined(WITH_ABSI_DEV)      ||  \
4911     defined(WITH_MGP_DEV)       ||  \
4912     defined(TESTING)            ||  \
4913     defined(ISOLTS)             ||  \
4914     defined(USE_DUMA)
4915 # if !defined(NO_SUPPORT_VERSION)
4916 #  define NO_SUPPORT_VERSION 1
4917 # endif /* if !defined(NO_SUPPORT_VERSION) */
4918 #endif
4919 #if defined(NO_SUPPORT_VERSION)
4920         dirty++;
4921 #endif
4922 #if defined(GENERATED_MAKE_VER_H)
4923 # if defined(VER_H_GIT_VERSION)
4924 
4925         /* Dirty if git source is dirty */
4926         if (strstr(VER_H_GIT_VERSION, "*"))
4927           {
4928                 dirty++;
4929           }
4930 
4931         /* Dirty if version contains "X", "D", "A", or "B" */
4932         if ((strstr(VER_H_GIT_VERSION, "X")) ||  \
4933             (strstr(VER_H_GIT_VERSION, "D")) ||  \
4934             (strstr(VER_H_GIT_VERSION, "A")) ||  \
4935             (strstr(VER_H_GIT_VERSION, "B")))
4936           {
4937                 dirty++;
4938           }
4939 
4940         /* Why? */
4941         if (dirty) //-V547
4942           {
4943             if ((strstr(VER_H_GIT_VERSION, "X")))
4944               {
4945                     whydirty = " ";
4946               }
4947             else if ((strstr(VER_H_GIT_VERSION, "D")))
4948               {
4949                     whydirty = " DEV ";
4950               }
4951             else if ((strstr(VER_H_GIT_VERSION, "A")))
4952               {
4953                     whydirty = " ALPHA ";
4954               }
4955             else if ((strstr(VER_H_GIT_VERSION, "B")))
4956               {
4957                     whydirty = " BETA ";
4958               }
4959           }
4960 
4961 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4962 #   if defined(VER_H_GIT_HASH)
4963 #    if VER_H_GIT_PATCH_INT < 1
4964     (void)fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4965                    VER_H_GIT_VERSION,
4966                    (long)(CHAR_BIT*sizeof(void *)),
4967                    VER_H_GIT_HASH);
4968 #    else
4969 #     define NO_SUPPORT_VERSION 1
4970     (void)fprintf (st, "\n   Version: %s+%s (%ld-bit)\n    Commit: %s",
4971                    VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4972                    (long)(CHAR_BIT*sizeof(void *)),
4973                    VER_H_GIT_HASH);
4974 #    endif
4975 #   else
4976 #    if VER_H_GIT_PATCH_INT < 1
4977         (void)fprintf (st, "\n   Version: %s (%ld-bit)",
4978                        VER_H_GIT_VERSION,
4979                        (long)(CHAR_BIT*sizeof(void *)));
4980 #    else
4981 #     define NO_SUPPORT_VERSION 1
4982         (void)fprintf (st, "\n   Version: %s+%s (%ld-bit)",
4983                        VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4984                        (long)(CHAR_BIT*sizeof(void *)));
4985 #    endif
4986 #   endif
4987 #  else
4988 #   if defined(VER_H_GIT_HASH)
4989         (void)fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4990                        VER_H_GIT_VERSION,
4991                        (long)(CHAR_BIT*sizeof(void *)),
4992                        VER_H_GIT_HASH);
4993 #   else
4994         (void)fprintf (st, "\n   Version: %s (%ld-bit)",
4995                        VER_H_GIT_VERSION,
4996                        (long)(CHAR_BIT*sizeof(void *)));
4997 #   endif
4998 #  endif
4999 # endif
5000 #endif
5001 
5002 /* TESTING */
5003 #if defined(TESTING)
5004     (void)fprintf (st, "\n   Options: ");
5005 # if !defined(HAVE_DPSOPT)
5006 #  define HAVE_DPSOPT 1
5007 # endif
5008     (void)fprintf (st, "TESTING");
5009 #endif /* if defined(TESTING) */
5010 
5011 /* ISOLTS */
5012 #if defined(ISOLTS)
5013 # if defined(HAVE_DPSOPT)
5014     (void)fprintf (st, ", ");
5015 # else
5016     (void)fprintf (st, "\n   Options: ");
5017 # endif
5018 # if !defined(HAVE_DPSOPT)
5019 #  define HAVE_DPSOPT 1
5020 # endif
5021     (void)fprintf (st, "ISOLTS");
5022 #endif /* if defined(ISOLTS) */
5023 
5024 /* NO_UCACHE */
5025 #if defined(NO_UCACHE)
5026 # if defined(HAVE_DPSOPT)
5027     (void)fprintf (st, ", ");
5028 # else
5029     (void)fprintf (st, "\n   Options: ");
5030 # endif
5031 # if !defined(HAVE_DPSOPT)
5032 #  define HAVE_DPSOPT 1
5033 # endif
5034     (void)fprintf (st, "NO_UCACHE");
5035 #endif /* if defined(NO_UCACHE) */
5036 
5037 /* NEED_128 */
5038 #if defined(NEED_128)
5039 # if defined(HAVE_DPSOPT)
5040     (void)fprintf (st, ", ");
5041 # else
5042     (void)fprintf (st, "\n   Options: ");
5043 # endif
5044 # if !defined(HAVE_DPSOPT)
5045 #  define HAVE_DPSOPT 1
5046 # endif
5047     (void)fprintf (st, "NEED_128");
5048 #endif /* if defined(NEED_128) */
5049 
5050 /* WAM */
5051 #if defined(WAM)
5052 # if defined(HAVE_DPSOPT)
5053     (void)fprintf (st, ", ");
5054 # else
5055     (void)fprintf (st, "\n   Options: ");
5056 # endif
5057 # if !defined(HAVE_DPSOPT)
5058 #  define HAVE_DPSOPT 1
5059 # endif
5060     (void)fprintf (st, "WAM");
5061 #endif /* if defined(WAM) */
5062 
5063 /* ROUND_ROBIN */
5064 #if defined(ROUND_ROBIN)
5065 # if defined(HAVE_DPSOPT)
5066     (void)fprintf (st, ", ");
5067 # else
5068     (void)fprintf (st, "\n   Options: ");
5069 # endif
5070 # if !defined(HAVE_DPSOPT)
5071 #  define HAVE_DPSOPT 1
5072 # endif
5073     (void)fprintf (st, "ROUND_ROBIN");
5074 #endif /* if defined(ROUND_ROBIN) */
5075 
5076 /* NO_LOCKLESS */
5077 #if !defined(LOCKLESS)
5078 # if defined(HAVE_DPSOPT)
5079     (void)fprintf (st, ", ");
5080 # else
5081     (void)fprintf (st, "\n   Options: ");
5082 # endif
5083 # if !defined(HAVE_DPSOPT)
5084 #  define HAVE_DPSOPT 1
5085 # endif
5086     (void)fprintf (st, "NO_LOCKLESS");
5087 #endif /* if !defined(LOCKLESS) */
5088 
5089 /* ABSI */  /* XXX: Change to NO_ABSI once code is non-experimental */
5090 #if defined(WITH_ABSI_DEV)
5091 # if defined(HAVE_DPSOPT)
5092     (void)fprintf (st, ", ");
5093 # else
5094     (void)fprintf (st, "\n   Options: ");
5095 # endif
5096 # if !defined(HAVE_DPSOPT)
5097 #  define HAVE_DPSOPT 1
5098 # endif
5099     (void)fprintf (st, "ABSI");
5100 #endif /* if defined(WITH_ABSI_DEV) */
5101 
5102 /* SOCKET */  /* XXX: Change to NO_SOCKET once code is non-experimental */
5103 #if defined(WITH_SOCKET_DEV)
5104 # if defined(HAVE_DPSOPT)
5105     (void)fprintf (st, ", ");
5106 # else
5107     (void)fprintf (st, "\n   Options: ");
5108 # endif
5109 # if !defined(HAVE_DPSOPT)
5110 #  define HAVE_DPSOPT 1
5111 # endif
5112     (void)fprintf (st, "SOCKET");
5113 #endif /* if defined(WITH_SOCKET_DEV) */
5114 
5115 /* CHAOSNET */  /* XXX: Change to NO_CHAOSNET once code is non-experimental */
5116 #if defined(WITH_MGP_DEV)
5117 # if defined(HAVE_DPSOPT)
5118     (void)fprintf (st, ", ");
5119 # else
5120     (void)fprintf (st, "\n   Options: ");
5121 # endif
5122 # if !defined(HAVE_DPSOPT)
5123 #  define HAVE_DPSOPT 1
5124 # endif
5125     (void)fprintf (st, "CHAOSNET");
5126 # if USE_SOCKET_DEV_APPROACH
5127     (void)fprintf (st, "-S");
5128 # endif /* if USE_SOCKET_DEV_APPROACH */
5129 #endif /* if defined(WITH_MGP_DEV) */
5130 
5131 /* DUMA */
5132 #if defined(USE_DUMA)
5133 # if defined(HAVE_DPSOPT)
5134     (void)fprintf (st, ", ");
5135 # else
5136     (void)fprintf (st, "\n   Options: ");
5137 # endif
5138 # if !defined(HAVE_DPSOPT)
5139 #  define HAVE_DPSOPT 1
5140 # endif
5141     (void)fprintf (st, "DUMA");
5142 #endif /* if defined(USE_DUMA) */
5143 
5144 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
5145 # if defined(NO_SUPPORT_VERSION)
5146     (void)fprintf (st, "\n  Modified: %s", VER_H_GIT_DATE);
5147 # else
5148     (void)fprintf (st, "\n  Released: %s", VER_H_GIT_DATE);
5149 # endif
5150 #endif
5151 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
5152     (void)fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
5153 #endif
5154 #if defined(VER_CURRENT_TIME)
5155     (void)fprintf (st, "\n  Compiled: %s", VER_CURRENT_TIME);
5156 #endif
5157     if (dirty) //-V547
5158       {
5159         (void)fprintf (st, "\r\n\r\n ****** THIS%sBUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******", whydirty);
5160       }
5161     (void)fprintf (st, "\r\n\r\n Build Information:");
5162 #if defined(BUILD_PROM_OSV_TEXT) && defined(BUILD_PROM_OSA_TEXT)
5163     char build_os_version_raw[255];
5164     char build_os_arch_raw[255];
5165     (void)sprintf(build_os_version_raw, "%.254s", BUILD_PROM_OSV_TEXT);
5166     (void)sprintf(build_os_arch_raw, "%.254s", BUILD_PROM_OSA_TEXT);
5167     char *build_os_version = strdup(build_os_version_raw);
5168     if (!build_os_version)
5169       {
5170         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5171                        __func__, __FILE__, __LINE__);
5172 # if defined(USE_BACKTRACE)
5173 #  if defined(SIGUSR2)
5174         (void)raise(SIGUSR2);
5175         /*NOTREACHED*/ /* unreachable */
5176 #  endif /* if defined(SIGUSR2) */
5177 # endif /* if defined(USE_BACKTRACE) */
5178         abort();
5179       }
5180     char *build_os_arch = strdup(build_os_arch_raw);
5181     if (!build_os_arch)
5182       {
5183         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5184                        __func__, __FILE__, __LINE__);
5185 # if defined(USE_BACKTRACE)
5186 #  if defined(SIGUSR2)
5187         (void)raise(SIGUSR2);
5188         /*NOTREACHED*/ /* unreachable */
5189 #  endif /* if defined(SIGUSR2) */
5190 # endif /* if defined(USE_BACKTRACE) */
5191         abort();
5192       }
5193     unsigned char SPROM[1024];
5194     setupPROM (0, SPROM);
5195     (void)fprintf (st, "\n    Target: ");
5196     printpq (SPROM, st, 190, 20);
5197     if (SPROM[170] != 20)
5198       {
5199         if (SPROM[170] != 255)
5200           {
5201             (void)fprintf (st, " on ");
5202             printpq (SPROM, st, 170, 20);
5203           }
5204       }
5205     strtrimspace(build_os_version, build_os_version_raw);
5206     strtrimspace(build_os_arch, build_os_arch_raw);
5207     (void)fprintf (st, "\n  Build OS: %s %s", build_os_version, build_os_arch);
5208     FREE(build_os_version);
5209     FREE(build_os_arch);
5210 #endif
5211 #if defined(__VERSION__)
5212     char gnumver[2];
5213     char postver[1024];
5214     (void)sprintf(gnumver, "%.1s", __VERSION__);
5215     (void)sprintf(postver, "%.1023s", __VERSION__);
5216     strremove(postver, "(TM)");
5217     strremove(postver, "(R)");
5218     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
5219     strremove(postver, "https://github.com/OpenIndiana/oi-userland.git ");
5220     strremove(postver, " gcc 4.9 mode");
5221     strremove(postver, "4.2.1 Compatible ");
5222     strremove(postver, "git@github.com:llvm/llvm-project.git ");
5223     strremove(postver, "https://github.com/llvm/llvm-project.git ");
5224     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
5225     strremove(postver, "https://github.com/yrnkrn/zapcc ");
5226     strremove(postver, "(experimental) ");
5227     strremove(postver, ".module+el8.7.0+20823+214a699d");
5228     strremove(postver, "17.1.1 (5725-C72, 5765-J20), version ");
5229     strremove(postver, "17.1.1 (5725-C72, 5765-J18), version ");
5230     strremove(postver, "17.1.2 (5725-C72, 5765-J20), version ");
5231     strremove(postver, "17.1.2 (5725-C72, 5765-J18), version ");
5232     strremove(postver, "llvmorg-16.0.6-0-");
5233     strremove(postver, " Clang 15.0.0 (build 760095e)");
5234     strremove(postver, " Clang 15.0.0 (build 6af5742)");
5235     strremove(postver, " Clang 15.0.0 (build ca7115e)");
5236     strremove(postver, " Clang 15.0.0 (build 232543c)");
5237     strremove(postver, " Clang 17.0.6 (build 19a779f)");
5238     strremove(postver, "CLANG: ");
5239 #endif
5240 #if ( defined(__GNUC__) && defined(__VERSION__) ) && !defined(__EDG__)
5241 # if !defined(__clang_version__)
5242     if (isdigit((unsigned char)gnumver[0])) {
5243         (void)fprintf (st, "\n  Compiler: GCC %s", postver);
5244     } else {
5245         (void)fprintf (st, "\n  Compiler: %s", postver);
5246     }
5247 # endif
5248 # if defined(__clang_analyzer__ )
5249     (void)fprintf (st, "\n  Compiler: Clang C/C++ Static Analyzer");
5250 # elif defined(__clang_version__) && defined(__VERSION__)
5251     char clangllvmver[1024];
5252     (void)sprintf(clangllvmver, "%.1023s", __clang_version__);
5253     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
5254     strremove(clangllvmver, "https://github.com/OpenIndiana/oi-userland.git ");
5255     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git ");
5256     strremove(clangllvmver, "c13b7485b87909fcf739f62cfa382b55407433c0");
5257     strremove(clangllvmver, "e6c3289804a67ea0bb6a86fadbe454dd93b8d855");
5258     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git");
5259     strremove(clangllvmver, " ( )");
5260     strremove(clangllvmver, " ()");
5261     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
5262         (void)fprintf (st, "\n  Compiler: Clang %s", clangllvmver);
5263     } else {
5264         (void)fprintf (st, "\n  Compiler: %s", postver);
5265     }
5266 # elif defined(__clang_version__)
5267     (void)fprintf (st, "\n  Compiler: %s", postver);
5268 # endif
5269 #elif defined(__PGI) && !defined(__NVCOMPILER)
5270     (void)fprintf (st, "\n  Compiler: Portland Group, Inc. (PGI) C Compiler ");
5271 # if defined(__PGIC__)
5272     (void)fprintf (st, "%d", __PGIC__);
5273 #  if defined(__PGIC_MINOR__)
5274     (void)fprintf (st, ".%d", __PGIC_MINOR__);
5275 #   if defined(__PGIC_PATCHLEVEL__)
5276     (void)fprintf (st, ".%d", __PGIC_PATCHLEVEL__);
5277 #   endif
5278 #  endif
5279 # endif
5280 #elif defined(__NVCOMPILER)
5281     (void)fprintf (st, "\n  Compiler: NVIDIA HPC SDK C Compiler ");
5282 # if defined(__NVCOMPILER_MAJOR__)
5283     (void)fprintf (st, "%d", __NVCOMPILER_MAJOR__);
5284 #  if defined(__NVCOMPILER_MINOR__)
5285     (void)fprintf (st, ".%d", __NVCOMPILER_MINOR__);
5286 #   if defined(__NVCOMPILER_PATCHLEVEL__)
5287     (void)fprintf (st, ".%d", __NVCOMPILER_PATCHLEVEL__);
5288 #   endif
5289 #  endif
5290 # endif
5291 #elif defined(_MSC_FULL_VER) && defined(_MSC_BUILD)
5292     (void)fprintf (st, "\n  Compiler: Microsoft C %d.%02d.%05d.%02d",
5293                    _MSC_FULL_VER/10000000,
5294                    (_MSC_FULL_VER/100000)%100,
5295                    _MSC_FULL_VER%100000,
5296                    _MSC_BUILD);
5297 #elif ( defined(__xlc__) && !defined(__clang_version__) )
5298 # if defined(_AIX) && defined(__PASE__)
5299     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
5300 # endif
5301 # if defined(_AIX) && !defined(__PASE__)
5302     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
5303 # endif
5304 # if defined(__linux__) && ( !defined(_AIX) || !defined(__PASE__) )
5305     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
5306 # endif
5307 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(__PASE__) && !defined(__linux__) && defined(__xlc__) )
5308 #  if defined(__PPC__) && defined(__APPLE__)
5309     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s for Mac OS X", __xlc__);
5310 #  else
5311     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s", __xlc__);
5312 #  endif
5313 # endif
5314 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__SUNPRO_CC_COMPAT)
5315 # define VER_ENC(maj, min, rev) \
5316   (((maj) * 1000000) + ((min) * 1000) + (rev))
5317 # define VER_DEC_MAJ(ver) \
5318   ((ver) / 1000000)
5319 # define VER_DEC_MIN(ver) \
5320   (((ver) % 1000000) / 1000)
5321 # define VER_DEC_REV(ver) \
5322   ((ver) % 1000)
5323 # if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
5324 #  define COMP_VER VER_ENC(                                        \
5325    (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
5326    (((__SUNPRO_C >>  8) & 0xf) * 10) + ((__SUNPRO_C >>  4) & 0xf), \
5327      (__SUNPRO_C & 0xf) * 10)
5328 # elif defined(__SUNPRO_C)
5329 #  define COMP_VER VER_ENC(    \
5330      (__SUNPRO_C >>  8) & 0xf, \
5331      (__SUNPRO_C >>  4) & 0xf, \
5332      (__SUNPRO_C) & 0xf)
5333 # elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
5334 #  define COMP_VER VER_ENC(                                          \
5335    (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
5336    (((__SUNPRO_CC >>  8) & 0xf) * 10) + ((__SUNPRO_CC >>  4) & 0xf), \
5337      (__SUNPRO_CC & 0xf) * 10)
5338 # elif defined(__SUNPRO_CC)
5339 #  define COMP_VER VER_ENC(     \
5340      (__SUNPRO_CC >>  8) & 0xf, \
5341      (__SUNPRO_CC >>  4) & 0xf, \
5342      (__SUNPRO_CC) & 0xf)
5343 # endif
5344 # if !defined(COMP_VER)
5345 #  define COMP_VER 0
5346 # endif
5347     (void)fprintf (st, "\n  Compiler: Oracle Developer Studio C/C++ %d.%d.%d",
5348                    VER_DEC_MAJ(COMP_VER),
5349                    VER_DEC_MIN(COMP_VER),
5350                    VER_DEC_REV(COMP_VER));
5351 #elif defined(__DMC__)
5352     (void)fprintf (st, "\n  Compiler: Digital Mars C/C++");
5353 #elif defined(__PCC__)
5354     (void)fprintf (st, "\n  Compiler: Portable C Compiler");
5355 #elif defined(KENC) || defined(KENCC) || defined(__KENC__) || defined(__KENCC__)
5356     (void)fprintf (st, "\n  Compiler: Plan 9 Compiler Suite");
5357 #elif defined(__ACK__)
5358     (void)fprintf (st, "\n  Compiler: Amsterdam Compiler Kit");
5359 #elif defined(__COMO__)
5360     (void)fprintf (st, "\n  Compiler: Comeau C++");
5361 #elif defined(__COMPCERT__)
5362     (void)fprintf (st, "\n  Compiler: CompCert C");
5363 #elif defined(__COVERITY__)
5364     (void)fprintf (st, "\n  Compiler: Coverity C/C++ Static Analyzer");
5365 #elif defined(__LCC__)
5366     (void)fprintf (st, "\n  Compiler: Local C Compiler (lcc)");
5367 #elif defined(sgi) || defined(__sgi) || defined(_sgi) || defined(_SGI_COMPILER_VERSION)
5368     (void)fprintf (st, "\n  Compiler: SGI MIPSpro");
5369 #elif defined(__OPEN64__)
5370     (void)fprintf (st, "\n  Compiler: Open64 %s", __OPEN64__);
5371 #elif defined(__PGI) || defined(__PGIC__)
5372     (void)fprintf (st, "\n  Compiler: Portland Group/PGI C/C++");
5373 #elif defined(__VBCC__)
5374     (void)fprintf (st, "\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
5375 #elif defined(__WATCOMC__)
5376     (void)fprintf (st, "\n  Compiler: Watcom C/C++ %d.%d",
5377                    __WATCOMC__ / 100,
5378                    __WATCOMC__ % 100);
5379 #elif defined(__xlC__)
5380     (void)fprintf (st, "\n  Compiler: IBM XL C/C++");
5381 #elif defined(__INTEL_COMPILER) || defined(__ICC)
5382 # if defined(__INTEL_COMPILER_UPDATE)
5383 #  if defined(__INTEL_COMPILER_BUILD_DATE)
5384     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d (%d)",
5385                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE,
5386                    __INTEL_COMPILER_BUILD_DATE);
5387 #  else
5388     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d",
5389                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE);
5390 #  endif
5391 # else
5392     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d",
5393                    __INTEL_COMPILER);
5394 # endif
5395 #elif defined(SIM_COMPILER)
5396 # define S_xstr(a) S_str(a)
5397 # define S_str(a) #a
5398     (void)fprintf (st, "\n  Compiler: %s", S_xstr(SIM_COMPILER));
5399 # undef S_str
5400 # undef S_xstr
5401 #else
5402     (void)fprintf (st, "\n  Compiler: Unknown");
5403 #endif
5404 
5405 #if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || \
5406     defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
5407 # define SC_IS_PPC64 1
5408 #else
5409 # define SC_IS_PPC64 0
5410 #endif
5411 
5412 #if defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__PPC) || \
5413     defined(__ppc32__) || defined(__PPC32__) || defined(__powerpc32__) || defined(__POWERPC32__) || defined(_M_PPC32) || \
5414     defined(__PPC32)
5415 # define SC_IS_PPC32 1
5416 #else
5417 # define SC_IS_PPC32 0
5418 #endif
5419 
5420 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
5421     arch = " x86_64";
5422 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
5423     arch = " x86";
5424 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
5425     arch = " arm64";
5426 #elif defined(_M_ARM) || defined(__arm__)
5427     arch = " arm";
5428 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
5429     arch = " ia64";
5430 #elif SC_IS_PPC64
5431     arch = " powerpc64";
5432 #elif SC_IS_PPC32
5433     arch = " powerpc";
5434 #elif defined(__s390x__)
5435     arch = " s390x";
5436 #elif defined(__s390__)
5437     arch = " s390";
5438 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
5439     arch = " j2";
5440 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
5441     arch = " sh4";
5442 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
5443     arch = " sh2";
5444 #elif defined(__alpha__)
5445     arch = " alpha";
5446 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
5447     arch = " hppa";
5448 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
5449     arch = " ice9";
5450 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
5451     arch = " mips64";
5452 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
5453     arch = " mips";
5454 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || \
5455       defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
5456     arch = " openrisc";
5457 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
5458     arch = " sparc64";
5459 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
5460     arch = " sparc";
5461 #elif defined(__riscv) || defined(__riscv__)
5462     arch = " riscv";
5463 #elif defined(__myriad2__)
5464     arch = " myriad2";
5465 #elif defined(__loongarch64) || defined(__loongarch__)
5466     arch = " loongarch";
5467 #elif defined(_m68851) || defined(__m68k__) || defined(__m68000__) || defined(__M68K)
5468     arch = " m68k";
5469 #elif defined(__m88k__) || defined(__m88000__) || defined(__M88K)
5470     arch = " m88k";
5471 #elif defined(__VAX__) || defined(__vax__)
5472     arch = " vax";
5473 #elif defined(__NIOS2__) || defined(__nios2__)
5474     arch = " nios2";
5475 #elif defined(__MICROBLAZE__) || defined(__microblaze__)
5476     arch = " microblaze";
5477 #else
5478     arch = " ";
5479 #endif
5480     (void)fprintf (st, "%s", arch);
5481 #if defined(BUILD_BY_USER)
5482         (void)fprintf (st, "\n  Built by: %s", BUILD_BY_USER);
5483 #else
5484 # if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
5485         (void)fprintf (st, "\n  Built by: %s", VER_H_PREP_USER);
5486 # endif
5487 #endif
5488                 (void)fprintf (st, "\n\n Host System Information:");
5489 #if defined(_WIN32)
5490     if (1) {
5491         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
5492         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
5493         char osversion[PATH_MAX+1] = "";
5494         FILE *f;
5495 
5496         if ((f = _popen ("ver", "r"))) {
5497             (void)memset (osversion, 0, sizeof(osversion));
5498             do {
5499                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
5500                     break;
5501                 sim_trim_endspc (osversion);
5502                 } while (osversion[0] == '\0');
5503             _pclose (f);
5504             }
5505         (void)fprintf (st, "\n   Host OS: %s", osversion);
5506         (void)fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
5507         }
5508 #else
5509     if (1) {
5510         char osversion[2*PATH_MAX+1] = "";
5511         FILE *f;
5512 # if !defined(_AIX)
5513         if ((f = popen \
5514              ("uname -mrs 2> /dev/null", "r"))) {
5515 # else
5516         if ((f = popen \
5517              ("sh -c 'echo \"$(command -p env uname -v \
5518                2> /dev/null).$(command -p env uname -r \
5519                2> /dev/null) $(command -p env uname -p \
5520                2> /dev/null)\"' 2> /dev/null", "r"))) {
5521 # endif /* if !defined(_AIX) */
5522             (void)memset (osversion, 0, sizeof(osversion));
5523             do {
5524               if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
5525                     break;
5526               }
5527             sim_trim_endspc (osversion);
5528             } while (osversion[0] == '\0');
5529             pclose (f);
5530             strremove(osversion, "0000000000000000 ");
5531             strremove(osversion, " 0000000000000000");
5532             strremove(osversion, "000000000000 ");
5533             strremove(osversion, " 000000000000");
5534             strremove(osversion, "IBM ");
5535             strremove(osversion, " (emulated by qemu)");
5536             strremove(osversion, " (emulated by QEMU)");
5537         }
5538 # if !defined(_AIX)
5539             (void)fprintf (st, "\n   Host OS: %s", osversion);
5540 # else
5541             strremove(osversion, "AIX ");
5542 #  if !defined(__PASE__)
5543             (void)fprintf (st, "\n   Host OS: IBM AIX %s", osversion);
5544 #  else
5545             (void)fprintf (st, "\n   Host OS: IBM OS/400 (PASE) %s", osversion);
5546 #  endif /* if !defined(__PASE__) */
5547 # endif /* if !defined(_AIX) */
5548     } else {
5549 # if !defined(_AIX)
5550         (void)fprintf (st, "\n   Host OS: Unknown");
5551 # else
5552 #  if !defined(__PASE__)
5553         (void)fprintf (st, "\n   Host OS: IBM AIX");
5554 #  else
5555         (void)fprintf (st, "\n   Host OS: IBM OS/400 (PASE)");
5556 #  endif /* if !defined(__PASE__) */
5557 # endif /* if !defined(_AIX) */
5558     }
5559 #endif
5560 #if defined(__APPLE__)
5561     int isRosetta = processIsTranslated();
5562     if (isRosetta == 1) {
5563         sim_printf ("\n\n  ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
5564     }
5565 #endif
5566     if (nodist)
5567       {
5568         sim_printf ("\n\n ********* LICENSE RESTRICTED BUILD *** NOT FOR REDISTRIBUTION *********\n");
5569       }
5570     else
5571       {
5572         (void)fprintf (st, "\n");
5573         (void)fprintf (st, "\n This software is made available under the terms of the ICU License.");
5574         (void)fprintf (st, "\n For complete license details, see the LICENSE file included with the");
5575         (void)fprintf (st, "\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
5576       }
5577         (void)fprintf (st, "\n");
5578     }
5579 return SCPE_OK;
5580 }
5581 
5582 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5583 {
5584 size_t i;
5585 DEVICE *dptr;
5586 t_bool only_enabled = (sim_switches & SWMASK ('E'));
5587 
5588 if (cptr && (*cptr != 0))
5589     return SCPE_2MARG;
5590 (void)fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
5591 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5592     if (!only_enabled || !qdisable (dptr))
5593         show_device (st, dptr, flag);
5594 return SCPE_OK;
5595 }
5596 
5597 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5598 {
5599 int32 i;
5600 DEVICE *dptr;
5601 
5602 if (cptr && (*cptr != 0))
5603     return SCPE_2MARG;
5604 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5605     show_dev_logicals (st, dptr, NULL, 1, cptr);
5606 return SCPE_OK;
5607 }
5608 
5609 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5610 {
5611 if (dptr->lname)
5612     (void)fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
5613 else if (!flag)
5614     fputs ("no logical name assigned\n", st);
5615 return SCPE_OK;
5616 }
5617 
5618 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5619 {
5620 DEVICE *dptr;
5621 UNIT *uptr;
5622 int32 accum;
5623 
5624 if (cptr && (*cptr != 0))
5625     return SCPE_2MARG;
5626 if (sim_clock_queue == QUEUE_LIST_END)
5627     (void)fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructions/sec\n",
5628                    sim_name, sim_time, sim_timer_inst_per_sec ());
5629 else {
5630     const char *tim;
5631 
5632     (void)fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
5633                    sim_name, sim_time, sim_timer_inst_per_sec ());
5634     accum = 0;
5635     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
5636         if (uptr == &sim_step_unit)
5637             (void)fprintf (st, "  Step timer");
5638         else
5639             if (uptr == &sim_expect_unit)
5640                 (void)fprintf (st, "  Expect fired");
5641             else
5642                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
5643                     (void)fprintf (st, "  %s", sim_dname (dptr));
5644                     if (dptr->numunits > 1)
5645                         (void)fprintf (st, " unit %d", (int32) (uptr - dptr->units));
5646                     }
5647                 else
5648                     (void)fprintf (st, "  Unknown");
5649         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
5650         (void)fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
5651                        (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
5652                        (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
5653         accum = accum + uptr->time;
5654         }
5655     }
5656 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
5657 return SCPE_OK;
5658 }
5659 
5660 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5661 {
5662 if (cptr && (*cptr != 0))
5663     return SCPE_2MARG;
5664 (void)fprintf (st, "Time:\t%.0f\n", sim_gtime());
5665 return SCPE_OK;
5666 }
5667 
5668 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5669 {
5670 t_stat r;
5671 
5672 if (cptr && (*cptr != 0))
5673     r = ssh_break (st, cptr, 1);  /* more? */
5674 else
5675     r = sim_brk_showall (st, (uint32)sim_switches);
5676 return r;
5677 }
5678 
5679 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5680 {
5681 (void)fprintf (st, "Radix=%d\n", dptr->dradix);
5682 return SCPE_OK;
5683 }
5684 
5685 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5686 {
5687 int32 any = 0;
5688 DEBTAB *dep;
5689 
5690 if (dptr->flags & DEV_DEBUG) {
5691     if (dptr->dctrl == 0)
5692         fputs ("Debugging disabled", st);
5693     else if (dptr->debflags == NULL)
5694         fputs ("Debugging enabled", st);
5695     else {
5696         uint32 dctrl = dptr->dctrl;
5697 
5698         fputs ("Debug=", st);
5699         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
5700             if ((dctrl & dep->mask) == dep->mask) {
5701                 dctrl &= ~dep->mask;
5702                 if (any)
5703                     fputc (';', st);
5704                 fputs (dep->name, st);
5705                 any = 1;
5706                 }
5707             }
5708         }
5709     fputc ('\n', st);
5710     return SCPE_OK;
5711     }
5712 else return SCPE_NOFNC;
5713 }
5714 
5715 /* Show On actions */
5716 
5717 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5718 {
5719 int32 lvl, i;
5720 
5721 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
5722 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
5723     if (lvl > 0)
5724         (void)fprintf(st, "On Processing at Do Nest Level: %d", lvl);
5725     else
5726         (void)fprintf(st, "On Processing for input commands");
5727     (void)fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
5728     for (i=1; i<SCPE_BASE; ++i) {
5729         if (sim_on_actions[lvl][i])
5730             (void)fprintf(st, "    on %5d    %s\n", i, sim_on_actions[lvl][i]); }
5731     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
5732         if (sim_on_actions[lvl][i])
5733             (void)fprintf(st, "    on %-5s    %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
5734     if (sim_on_actions[lvl][0])
5735         (void)fprintf(st, "    on ERROR    %s\n", sim_on_actions[lvl][0]);
5736     (void)fprintf(st, "\n");
5737     }
5738 if (sim_on_inherit)
5739     (void)fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
5740 return SCPE_OK;
5741 }
5742 
5743 /* Show modifiers */
5744 
5745 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5746 {
5747 int32 i;
5748 DEVICE *dptr;
5749 
5750 if (cptr && (*cptr != 0))                               /* now eol? */
5751     return SCPE_2MARG;
5752 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5753     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5754 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5755     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5756 return SCPE_OK;
5757 }
5758 
5759 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5760 {
5761 fprint_set_help (st, dptr);
5762 return SCPE_OK;
5763 }
5764 
5765 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
     /* [previous][next][first][last][top][bottom][index][help] */
5766 {
5767 MTAB *mptr;
5768 t_stat r = SCPE_OK;
5769 
5770 if (dptr->modifiers == NULL)
5771     return SCPE_OK;
5772 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
5773     if (mptr->pstring &&
5774         ((mptr->mask & MTAB_XTD)?
5775             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
5776             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
5777         if (*toks > 0) {
5778             (void)fprintf (st, "\n");
5779             *toks = 0;
5780             }
5781         if (r == SCPE_OK)
5782             fprint_sep (st, toks);
5783         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
5784         }
5785     }
5786 return SCPE_OK;
5787 }
5788 
5789 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
     /* [previous][next][first][last][top][bottom][index][help] */
5790     CONST char *cptr, int32 flag)
5791 {
5792 t_stat r = SCPE_OK;
5793 
5794 if (mptr->disp)
5795     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
5796 else
5797     fputs (mptr->pstring, st);
5798 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
5799     fputc ('\n', st);
5800 return r;
5801 }
5802 
5803 /* Show show commands */
5804 
5805 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5806 {
5807 int32 i;
5808 DEVICE *dptr;
5809 
5810 if (cptr && (*cptr != 0))                               /* now eol? */
5811     return SCPE_2MARG;
5812 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5813     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5814 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5815     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5816 return SCPE_OK;
5817 }
5818 
5819 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] */
5820 {
5821 fprint_show_help (st, dptr);
5822 return SCPE_OK;
5823 }
5824 
5825 /* Breakpoint commands */
5826 
5827 t_stat brk_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5828 {
5829 GET_SWITCHES (cptr);                                    /* get switches */
5830 return ssh_break (NULL, cptr, flg);                     /* call common code */
5831 }
5832 
5833 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
     /* [previous][next][first][last][top][bottom][index][help] */
5834 {
5835 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
5836 CONST char *tptr, *t1ptr;
5837 DEVICE *dptr = sim_dflt_dev;
5838 UNIT *uptr;
5839 t_stat r;
5840 t_addr lo, hi, max;
5841 int32 cnt;
5842 
5843 if (sim_brk_types == 0)
5844     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5845 if (dptr == NULL)
5846     return SCPE_IERR;
5847 uptr = dptr->units;
5848 if (uptr == NULL)
5849     return SCPE_IERR;
5850 max = uptr->capac - 1;
5851 abuf[sizeof(abuf)-1] = '\0';
5852 strncpy (abuf, cptr, sizeof(abuf)-1);
5853 cptr = abuf;
5854 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
5855     if (flg != SSH_ST)                                  /* only on SET */
5856         return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
5857     *aptr++ = 0;                                        /* separate strings */
5858     }
5859 if (*cptr == 0) {                                       /* no argument? */
5860     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
5861     return ssh_break_one (st, flg, lo, 0, aptr);
5862     }
5863 while (*cptr) {
5864     cptr = get_glyph (cptr, gbuf, ',');
5865     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
5866     if (tptr == NULL)
5867         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
5868     if (*tptr == '[') {
5869         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
5870         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
5871             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
5872         tptr = t1ptr + 1;
5873         }
5874     else cnt = 0;
5875     if (*tptr != 0)
5876         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
5877     if ((lo == 0) && (hi == max)) {
5878         if (flg == SSH_CL)
5879             sim_brk_clrall (sim_switches);
5880         else
5881             if (flg == SSH_SH)
5882                 sim_brk_showall (st, (uint32)sim_switches);
5883             else
5884                 return SCPE_ARG;
5885         }
5886     else {
5887         for ( ; lo <= hi; lo = lo + 1) {
5888             r = ssh_break_one (st, flg, lo, cnt, aptr);
5889             if (r != SCPE_OK)
5890                 return r;
5891             }
5892         }
5893     }
5894 return SCPE_OK;
5895 }
5896 
5897 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] */
5898 {
5899 if (!sim_brk_types)
5900     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5901 switch (flg) {
5902     case SSH_ST:
5903         return sim_brk_set (lo, sim_switches, cnt, aptr);
5904         /*NOTREACHED*/ /* unreachable */
5905         break;
5906 
5907     case SSH_CL:
5908         return sim_brk_clr (lo, sim_switches);
5909         /*NOTREACHED*/ /* unreachable */
5910         break;
5911 
5912     case SSH_SH:
5913         return sim_brk_show (st, lo, sim_switches);
5914         /*NOTREACHED*/ /* unreachable */
5915         break;
5916 
5917     default:
5918         return SCPE_ARG;
5919     }
5920 }
5921 
5922 /* Reset command and routines */
5923 
5924 static t_bool run_cmd_did_reset = FALSE;
5925 
5926 t_stat reset_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5927 {
5928 char gbuf[CBUFSIZE];
5929 DEVICE *dptr;
5930 
5931 GET_SWITCHES (cptr);                                    /* get switches */
5932 run_cmd_did_reset = FALSE;
5933 if (*cptr == 0)                                         /* reset(cr) */
5934     return (reset_all (0));
5935 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5936 if (*cptr != 0)                                         /* now eol? */
5937     return SCPE_2MARG;
5938 if (strcmp (gbuf, "ALL") == 0)
5939     return (reset_all (0));
5940 dptr = find_dev (gbuf);                                 /* locate device */
5941 if (dptr == NULL)                                       /* found it? */
5942     return SCPE_NXDEV;
5943 if (dptr->reset != NULL)
5944     return dptr->reset (dptr);
5945 else return SCPE_OK;
5946 }
5947 
5948 /* Reset devices start..end
5949 
5950    Inputs:
5951         start   =       number of starting device
5952    Outputs:
5953         status  =       error status
5954 */
5955 
5956 t_stat reset_all (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5957 {
5958 DEVICE *dptr;
5959 uint32 i;
5960 t_stat reason;
5961 
5962 for (i = 0; i < start; i++) {
5963     if (sim_devices[i] == NULL)
5964         return SCPE_IERR;
5965     }
5966 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
5967     if (dptr->reset != NULL) {
5968         reason = dptr->reset (dptr);
5969         if (reason != SCPE_OK)
5970             return reason;
5971         }
5972     }
5973 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
5974     if (dptr->reset != NULL) {
5975         reason = dptr->reset (dptr);
5976         if (reason != SCPE_OK)
5977             return reason;
5978         }
5979     }
5980 return SCPE_OK;
5981 }
5982 
5983 /* Reset to powerup state
5984 
5985    Inputs:
5986         start   =       number of starting device
5987    Outputs:
5988         status  =       error status
5989 */
5990 
5991 t_stat reset_all_p (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5992 {
5993 t_stat r;
5994 int32 old_sw = sim_switches;
5995 
5996 sim_switches = SWMASK ('P');
5997 r = reset_all (start);
5998 sim_switches = old_sw;
5999 return r;
6000 }
6001 
6002 /* Attach command */
6003 
6004 t_stat attach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6005 {
6006 char gbuf[4*CBUFSIZE];
6007 DEVICE *dptr;
6008 UNIT *uptr;
6009 t_stat r;
6010 
6011 GET_SWITCHES (cptr);                                    /* get switches */
6012 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6013     return SCPE_2FARG;
6014 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6015 GET_SWITCHES (cptr);                                    /* get switches */
6016 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* now eol? */
6017     return SCPE_2FARG;
6018 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6019 if (dptr == NULL)                                       /* found dev? */
6020     return SCPE_NXDEV;
6021 if (uptr == NULL)                                       /* valid unit? */
6022     return SCPE_NXUN;
6023 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
6024     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
6025         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
6026         r = scp_detach_unit (dptr, uptr);               /* detach it */
6027         if (r != SCPE_OK)                               /* error? */
6028             return r; }
6029     else {
6030         if (!(uptr->dynflags & UNIT_ATTMULT))
6031             return SCPE_ALATT;                          /* Already attached */
6032         }
6033     }
6034 gbuf[sizeof(gbuf)-1] = '\0';
6035 strncpy (gbuf, cptr, sizeof(gbuf)-1);
6036 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
6037 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
6038 }
6039 
6040 /* Call device-specific or file-oriented attach unit routine */
6041 
6042 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6043 {
6044 if (dptr->attach != NULL)                               /* device routine? */
6045     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
6046 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
6047 }
6048 
6049 /* Attach unit to file */
6050 
6051 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6052 {
6053 DEVICE *dptr;
6054 
6055 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6056     return SCPE_UDIS;
6057 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
6058     return SCPE_NOATT;
6059 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6060     return SCPE_NOATT;
6061 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
6062 if (uptr->filename == NULL)
6063     return SCPE_MEM;
6064 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
6065 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
6066     ((uptr->flags & UNIT_RO) != 0)) {
6067     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
6068         ((uptr->flags & UNIT_RO) == 0))
6069         return attach_err (uptr, SCPE_NORO);            /* no, error */
6070     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
6071     if (uptr->fileref == NULL)                          /* open fail? */
6072         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
6073     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
6074     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6075         sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
6076         }
6077     }
6078 else {
6079     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
6080         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
6081         if (uptr->fileref == NULL)                      /* open fail? */
6082             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
6083         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6084             sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
6085             }
6086         }
6087     else {                                              /* normal */
6088         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
6089         if (uptr->fileref == NULL) {                    /* open fail? */
6090 #if defined(EWOULDBLOCK)
6091             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
6092 #else
6093             if ((errno == EAGAIN))
6094 #endif
6095                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6096 
6097 #if defined(EPERM)
6098             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
6099 #else
6100             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
6101 #endif
6102                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
6103                     return attach_err (uptr, SCPE_NORO);/* no error */
6104                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
6105                 if (uptr->fileref == NULL)              /* open fail? */
6106                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6107                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
6108                 if (!sim_quiet) {
6109                     sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
6110                     }
6111                 }
6112             else {                                      /* doesn't exist */
6113                 if (sim_switches & SWMASK ('E'))        /* must exist? */
6114                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6115                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
6116                 if (uptr->fileref == NULL)              /* open fail? */
6117                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6118                 if (!sim_quiet) {
6119                     sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
6120                     }
6121                 }
6122             }                                           /* end if null */
6123         }                                               /* end else */
6124     }
6125 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
6126     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
6127     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
6128         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
6129     if (uptr->filebuf == NULL)                          /* no buffer? */
6130         return attach_err (uptr, SCPE_MEM);             /* error */
6131     if (!sim_quiet) {
6132         sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
6133         }
6134     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
6135         SZ_D (dptr), cap, uptr->fileref);
6136     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
6137     }
6138 uptr->flags = uptr->flags | UNIT_ATT;
6139 uptr->pos = 0;
6140 return SCPE_OK;
6141 }
6142 
6143 t_stat attach_err (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
6144 {
6145 FREE (uptr->filename);
6146 uptr->filename = NULL;
6147 return stat;
6148 }
6149 
6150 /* Detach command */
6151 
6152 t_stat detach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6153 {
6154 char gbuf[CBUFSIZE];
6155 DEVICE *dptr;
6156 UNIT *uptr;
6157 
6158 GET_SWITCHES (cptr);                                    /* get switches */
6159 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6160     return SCPE_2FARG;
6161 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6162 if (*cptr != 0)                                         /* now eol? */
6163     return SCPE_2MARG;
6164 if (strcmp (gbuf, "ALL") == 0)
6165     return (detach_all (0, FALSE));
6166 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6167 if (dptr == NULL)                                       /* found dev? */
6168     return SCPE_NXDEV;
6169 if (uptr == NULL)                                       /* valid unit? */
6170     return SCPE_NXUN;
6171 return scp_detach_unit (dptr, uptr);                    /* detach */
6172 }
6173 
6174 /* Detach devices start..end
6175 
6176    Inputs:
6177         start   =       number of starting device
6178         shutdown =      TRUE if simulator shutting down
6179    Outputs:
6180         status  =       error status
6181 
6182    Note that during shutdown, detach routines for non-attachable devices
6183    will be called.  These routines can implement simulator shutdown.  Error
6184    returns during shutdown are ignored.
6185 */
6186 
6187 t_stat detach_all (int32 start, t_bool shutdown)
     /* [previous][next][first][last][top][bottom][index][help] */
6188 {
6189 uint32 i, j;
6190 DEVICE *dptr;
6191 UNIT *uptr;
6192 t_stat r;
6193 
6194 if ((start < 0) || (start > 1))
6195     return SCPE_IERR;
6196 if (shutdown)
6197     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
6198 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
6199     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
6200         uptr = (dptr->units) + j;
6201         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
6202             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
6203             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
6204             r = scp_detach_unit (dptr, uptr);           /* detach unit */
6205 
6206             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
6207                 return r;                               /* bail out now with error status */
6208             }
6209         }
6210     }
6211 return SCPE_OK;
6212 }
6213 
6214 /* Call device-specific or file-oriented detach unit routine */
6215 
6216 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6217 {
6218 if (dptr->detach != NULL)                               /* device routine? */
6219     return dptr->detach (uptr);
6220 return detach_unit (uptr);                              /* no, standard */
6221 }
6222 
6223 /* Detach unit from file */
6224 
6225 t_stat detach_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6226 {
6227 DEVICE *dptr;
6228 
6229 if (uptr == NULL)
6230     return SCPE_IERR;
6231 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
6232     return SCPE_NOATT;
6233 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
6234     if (sim_switches & SIM_SW_REST)                     /* restoring? */
6235         return SCPE_OK;                                 /* allow detach */
6236     else
6237         return SCPE_NOTATT;                             /* complain */
6238     }
6239 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6240     return SCPE_OK;
6241 if (uptr->flags & UNIT_BUF) {
6242     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
6243     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
6244         if (!sim_quiet) {
6245             sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
6246             }
6247         rewind (uptr->fileref);
6248         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
6249         if (ferror (uptr->fileref))
6250             sim_printf ("%s: I/O error - %s (Error %d)",
6251                         sim_dname (dptr), xstrerror_l(errno), errno);
6252         }
6253     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
6254         FREE (uptr->filebuf);                           /* free buf */
6255         uptr->filebuf = NULL;
6256         }
6257     uptr->flags = uptr->flags & ~UNIT_BUF;
6258     }
6259 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
6260 FREE (uptr->filename);
6261 uptr->filename = NULL;
6262 if (fclose (uptr->fileref) == EOF)
6263     return SCPE_IOERR;
6264 return SCPE_OK;
6265 }
6266 
6267 /* Get device display name */
6268 
6269 const char *sim_dname (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6270 {
6271 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
6272 }
6273 
6274 /* Get unit display name */
6275 
6276 const char *sim_uname (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6277 {
6278 DEVICE *d = find_dev_from_unit(uptr);
6279 static char uname[CBUFSIZE];
6280 
6281 if (!d)
6282     return "";
6283 if (d->numunits == 1)
6284     return sim_dname (d);
6285 (void)sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
6286 return uname;
6287 }
6288 
6289 /* Run, go, boot, cont, step, next commands */
6290 
6291 t_stat run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6292 {
6293 char gbuf[CBUFSIZE] = "";
6294 CONST char *tptr;
6295 uint32 i, j;
6296 int32 sim_next = 0;
6297 int32 unitno;
6298 t_value pcv, orig_pcv;
6299 t_stat r;
6300 DEVICE *dptr;
6301 UNIT *uptr;
6302 
6303 GET_SWITCHES (cptr);                                    /* get switches */
6304 sim_step = 0;
6305 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
6306     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
6307     if (*cptr != 0) {                                   /* argument? */
6308         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6309         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
6310             if (sim_dflt_dev && sim_vm_parse_addr)      /* address parser? */
6311                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
6312             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
6313             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
6314                 (pcv > width_mask[sim_PC->width]))
6315                 return SCPE_ARG;
6316             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
6317             }
6318         }
6319     if ((flag == RU_RUN) &&                             /* run? */
6320         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
6321         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
6322         return r;
6323         }
6324     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { //-V600 /* should be end */
6325         int32 saved_switches = sim_switches;
6326 
6327         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6328             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
6329         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6330             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
6331                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
6332         sim_switches = 0;
6333         GET_SWITCHES (cptr);
6334         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
6335             r = expect_cmd (1, cptr);
6336             if (r != SCPE_OK)
6337                 return r;
6338             }
6339         else {                                          /* BREAK UNTIL condition */
6340             if (sim_switches == 0)
6341                 sim_switches = sim_brk_dflt;
6342             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
6343             sim_brk_types |= BRK_TYP_TEMP;
6344             r = ssh_break (NULL, cptr, SSH_ST);
6345             if (r != SCPE_OK)
6346                 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
6347             }
6348         sim_switches = saved_switches;
6349         }
6350     }
6351 
6352 else if ((flag == RU_STEP) ||
6353          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
6354     static t_bool not_implemented_message = FALSE;
6355 
6356     if ((!not_implemented_message) && (flag == RU_NEXT)) {
6357         not_implemented_message = TRUE;
6358         flag = RU_STEP;
6359         }
6360     if (*cptr != 0) {                                   /* argument? */
6361         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6362         if (*cptr != 0)                                 /* should be end */
6363             return SCPE_2MARG;
6364         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6365         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
6366             return SCPE_ARG;
6367         }
6368     else sim_step = 1;
6369     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
6370         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
6371     }
6372 else if (flag == RU_NEXT) {                             /* next */
6373     t_addr *addrs;
6374 
6375     if (*cptr != 0) {                                   /* argument? */
6376         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6377         if (*cptr != 0)                                 /* should be end */
6378             return SCPE_2MARG;
6379         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6380         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
6381             return SCPE_ARG;
6382         }
6383     else sim_next = 1;
6384     if (sim_vm_is_subroutine_call(&addrs)) {
6385         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6386         for (i=0; addrs[i]; i++)
6387             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6388         }
6389     else
6390         sim_step = 1;
6391     }
6392 else if (flag == RU_BOOT) {                             /* boot */
6393     if (*cptr == 0)                                     /* must be more */
6394         return SCPE_2FARG;
6395     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
6396     if (*cptr != 0)                                     /* should be end */
6397         return SCPE_2MARG;
6398     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
6399     if (dptr == NULL)                                   /* found dev? */
6400         return SCPE_NXDEV;
6401     if (uptr == NULL)                                   /* valid unit? */
6402         return SCPE_NXUN;
6403     if (dptr->boot == NULL)                             /* can it boot? */
6404         return SCPE_NOFNC;
6405     if (uptr->flags & UNIT_DIS)                         /* disabled? */
6406         return SCPE_UDIS;
6407     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
6408         !(uptr->flags & UNIT_ATT))
6409         return SCPE_UNATT;
6410     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
6411     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
6412         return r;
6413     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
6414         return r;
6415     }
6416 
6417 else
6418     if (flag != RU_CONT)                                /* must be cont */
6419         return SCPE_IERR;
6420     else                                                /* CONTINUE command */
6421         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
6422             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
6423 
6424 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
6425     return SCPE_OK;
6426 
6427 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
6428     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
6429         uptr = dptr->units + j;
6430         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
6431             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
6432         }
6433     }
6434 stop_cpu = 0;
6435 sim_is_running = 1;                                     /* flag running */
6436 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
6437     sim_is_running = 0;                                 /* flag idle */
6438     sim_ttcmd ();
6439     return SCPE_TTYERR;
6440     }
6441 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
6442     sim_is_running = 0;                                 /* flag idle */
6443     sim_ttcmd ();
6444     return r;
6445     }
6446 #if !defined(IS_WINDOWS)
6447 # if defined(SIGINT)
6448 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
6449     sim_is_running = 0;                                 /* flag idle */
6450     sim_ttcmd ();
6451     return SCPE_SIGERR;
6452     }
6453 # endif
6454 #endif
6455 #if !defined(IS_WINDOWS)
6456 # if defined(SIGHUP)
6457 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
6458     sim_is_running = 0;                                 /* flag idle */
6459     sim_ttcmd ();
6460     return SCPE_SIGERR;
6461     }
6462 # endif
6463 #endif
6464 #if !defined(IS_WINDOWS)
6465 # if defined(SIGTERM)
6466 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
6467     sim_is_running = 0;                                 /* flag idle */
6468     sim_ttcmd ();
6469     return SCPE_SIGERR;
6470     }
6471 # endif
6472 #endif
6473 if (sim_step)                                           /* set step timer */
6474     sim_activate (&sim_step_unit, sim_step);
6475 (void)fflush(stdout);                                   /* flush stdout */
6476 if (sim_log)                                            /* flush log if enabled */
6477     (void)fflush (sim_log);
6478 sim_rtcn_init_all ();                                   /* re-init clocks */
6479 sim_start_timer_services ();                            /* enable wall clock timing */
6480 
6481 do {
6482     t_addr *addrs;
6483 
6484     while (1) {
6485         r = sim_instr();
6486         if (r != SCPE_REMOTE)
6487             break;
6488         sim_remote_process_command ();                  /* Process the command and resume processing */
6489         }
6490     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
6491         (--sim_next <=0))
6492         break;
6493     if (sim_step == 0) {                                /* doing a NEXT? */
6494         t_addr val;
6495         BRKTAB *bp;
6496 
6497         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
6498             break;
6499         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
6500             val = (t_addr)(*sim_vm_pc_value)();
6501         else
6502             val = (t_addr)get_rval (sim_PC, 0);
6503         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6504             break;
6505         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
6506         }
6507     else {
6508         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
6509             break;
6510         }
6511     /* setup another next/step */
6512     sim_step = 0;
6513     if (sim_vm_is_subroutine_call(&addrs)) {
6514         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6515         for (i=0; addrs[i]; i++)
6516             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6517         }
6518     else
6519         sim_step = 1;
6520     if (sim_step)                                       /* set step timer */
6521         sim_activate (&sim_step_unit, sim_step);
6522     } while (1);
6523 
6524 sim_is_running = 0;                                     /* flag idle */
6525 sim_stop_timer_services ();                             /* disable wall clock timing */
6526 sim_ttcmd ();                                           /* restore console */
6527 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
6528 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
6529 #if defined(SIGHUP)
6530 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
6531 #endif
6532 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
6533 if (sim_log)                                            /* flush console log */
6534     (void)fflush (sim_log);
6535 if (sim_deb)                                            /* flush debug log */
6536     sim_debug_flush ();
6537 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
6538     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
6539         uptr = dptr->units + j;
6540         if (uptr->flags & UNIT_ATT) {                   /* attached, */
6541             if (uptr->io_flush)                         /* unit specific flush routine */
6542                 uptr->io_flush (uptr);                  /* call it */
6543             else {
6544                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
6545                     (uptr->fileref) &&                  /* real file, */
6546                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
6547                     !(uptr->flags & UNIT_RO))           /* not read only? */
6548                     (void)fflush (uptr->fileref);
6549                 }
6550             }
6551         }
6552     }
6553 sim_cancel (&sim_step_unit);                            /* cancel step timer */
6554 UPDATE_SIM_TIME;                                        /* update sim time */
6555 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6556 }
6557 
6558 /* run command message handler */
6559 
6560 void
6561 run_cmd_message (const char *unechoed_cmdline, t_stat r)
     /* [previous][next][first][last][top][bottom][index][help] */
6562 {
6563 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6564     sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
6565 #if defined(WIN_STDIO)
6566 (void)fflush(stderr);
6567 (void)fflush(stdout);
6568 #endif /* if defined(WIN_STDIO) */
6569 fprint_stopped (stdout, r);                         /* print msg */
6570 if (sim_log && (sim_log != stdout))                 /* log if enabled */
6571     fprint_stopped (sim_log, r);
6572 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6573     fprint_stopped (sim_deb, r);
6574 #if defined(WIN_STDIO)
6575 (void)fflush(stderr);
6576 (void)fflush(stdout);
6577 #endif /* if defined(WIN_STDIO) */
6578 }
6579 
6580 /* Common setup for RUN or BOOT */
6581 
6582 t_stat sim_run_boot_prep (int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
6583 {
6584 UNIT *uptr;
6585 t_stat r;
6586 
6587 sim_interval = 0;                                       /* reset queue */
6588 sim_time = sim_rtime = 0;
6589 noqueue_time = 0;
6590 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6591     sim_clock_queue = uptr->next;
6592     uptr->next = NULL;
6593     }
6594 r = reset_all (0);
6595 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6596     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6597         sim_printf ("Resetting all devices...  This may not have been your intention.\n");
6598         sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
6599         }
6600     run_cmd_did_reset = TRUE;
6601     }
6602 return r;
6603 }
6604 
6605 /* Print stopped message
6606  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6607  * call the indicated routine to print additional information after the message
6608  * and before the PC value is printed.  If the routine returns FALSE, skip
6609  * printing the PC and its related instruction.
6610  */
6611 
6612 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6613 {
6614 int32 i;
6615 t_stat r = 0;
6616 t_addr k;
6617 t_value pcval;
6618 
6619 fputc ('\n', st);                                       /* start on a new line */
6620 
6621 if (v >= SCPE_BASE)                                     /* SCP error? */
6622     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
6623 else {                                                  /* VM error */
6624     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6625 
6626     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6627         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6628         return;                                         /*     we're done */
6629     }
6630 
6631 (void)fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6632 
6633 pcval = get_rval (pc, 0);
6634 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6635     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6636 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6637     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6638 if ((dptr != NULL) && (dptr->examine != NULL)) {
6639     for (i = 0; i < sim_emax; i++)
6640         sim_eval[i] = 0;
6641     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6642         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6643             break;
6644         }
6645     if ((r == SCPE_OK) || (i > 0)) {
6646         (void)fprintf (st, " (");
6647         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6648             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6649         (void)fprintf (st, ")");
6650         }
6651     }
6652 (void)fprintf (st, "\n");
6653 return;
6654 }
6655 
6656 void fprint_stopped (FILE *st, t_stat v)
     /* [previous][next][first][last][top][bottom][index][help] */
6657 {
6658 #if defined(WIN_STDIO)
6659 (void)fflush(stderr);
6660 (void)fflush(stdout);
6661 #endif /* if defined(WIN_STDIO) */
6662 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6663 return;
6664 }
6665 
6666 /* Unit service for step timeout, originally scheduled by STEP n command
6667    Return step timeout SCP code, will cause simulation to stop */
6668 
6669 t_stat step_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6670 {
6671 return SCPE_STEP;
6672 }
6673 
6674 /* Unit service to facilitate expect matching to stop simulation.
6675    Return expect SCP code, will cause simulation to stop */
6676 
6677 t_stat expect_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6678 {
6679 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6680 }
6681 
6682 /* Signal handler for ^C signal - set stop simulation flag */
6683 
6684 void int_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help] */
6685 {
6686 stop_cpu = 1;
6687 return;
6688 }
6689 
6690 /* Examine/deposit commands */
6691 
6692 t_stat exdep_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6693 {
6694 char gbuf[CBUFSIZE];
6695 CONST char *gptr;
6696 CONST char *tptr = NULL;
6697 int32 opt;
6698 t_addr low, high;
6699 t_stat reason = SCPE_IERR;
6700 DEVICE *tdptr;
6701 REG *lowr, *highr;
6702 FILE *ofile;
6703 
6704 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6705 if (flag == EX_E)                                       /* extra for EX */
6706     opt = opt | CMD_OPT_OF;
6707 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6708 if (NULL == cptr)                                       /* error? */
6709     return reason;
6710 if (*cptr == 0)                                         /* must be more */
6711     return SCPE_2FARG;
6712 if (sim_dfunit == NULL)                                 /* got a unit? */
6713     return SCPE_NXUN;
6714 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6715 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6716 
6717     return SCPE_2FARG;
6718 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6719 
6720 for (gptr = gbuf, reason = SCPE_OK;
6721     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6722     tdptr = sim_dfdev;                                  /* working dptr */
6723     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6724         tptr = gptr + strlen ("STATE");
6725         if (*tptr && (*tptr++ != ','))
6726             return SCPE_ARG;
6727         if ((lowr = sim_dfdev->registers) == NULL)
6728             return SCPE_NXREG;
6729         for (highr = lowr; highr->name != NULL; highr++) ;
6730         sim_switches = sim_switches | SIM_SW_HIDE;
6731         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6732             lowr, --highr, 0, 0);
6733         continue;
6734         }
6735 
6736     /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
6737     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6738         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6739         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6740         low = high = 0;
6741         if ((*tptr == '-') || (*tptr == ':')) {
6742             highr = find_reg (tptr + 1, &tptr, tdptr);
6743             if (highr == NULL)
6744                 return SCPE_NXREG;
6745             }
6746         else {
6747             highr = lowr;
6748             if (*tptr == '[') {
6749                 if (lowr->depth <= 1)
6750                     return SCPE_ARG;
6751                 tptr = get_range (NULL, tptr + 1, &low, &high,
6752                     10, lowr->depth - 1, ']');
6753                 if (tptr == NULL)
6754                     return SCPE_ARG;
6755                 }
6756             }
6757         if (*tptr && (*tptr++ != ','))
6758             return SCPE_ARG;
6759         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6760             lowr, highr, (uint32) low, (uint32) high);
6761         continue;
6762         }
6763 
6764     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6765         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6766         sim_dfunit->capac - sim_dfdev->aincr), 0);
6767     if (tptr == NULL)
6768         return SCPE_ARG;
6769     if (*tptr && (*tptr++ != ','))
6770         return SCPE_ARG;
6771     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6772         sim_dfdev, sim_dfunit);
6773     }                                                   /* end for */
6774 if (sim_ofile)                                          /* close output file */
6775     fclose (sim_ofile);
6776 return reason;
6777 }
6778 
6779 /* Loop controllers for examine/deposit
6780 
6781    exdep_reg_loop       examine/deposit range of registers
6782    exdep_addr_loop      examine/deposit range of addresses
6783 */
6784 
6785 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6786     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6787 {
6788 t_stat reason;
6789 uint32 idx, val_start=lows;
6790 t_value val, last_val;
6791 REG *rptr;
6792 
6793 if ((lowr == NULL) || (highr == NULL))
6794     return SCPE_IERR;
6795 if (lowr > highr)
6796     return SCPE_ARG;
6797 for (rptr = lowr; rptr <= highr; rptr++) {
6798     if ((sim_switches & SIM_SW_HIDE) &&
6799         (rptr->flags & REG_HIDDEN))
6800         continue;
6801     val = last_val = 0;
6802     for (idx = lows; idx <= highs; idx++) {
6803         if (idx >= rptr->depth)
6804             return SCPE_SUB;
6805         val = get_rval (rptr, idx);
6806         if (schptr && !test_search (&val, schptr))
6807             continue;
6808         if (flag == EX_E) {
6809             if ((idx > lows) && (val == last_val))
6810                 continue;
6811             if (idx > val_start+1) {
6812                 if (idx-1 == val_start+1) {
6813                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6814                     if (reason != SCPE_OK)
6815                         return reason;
6816                     if (sim_log && (ofile == stdout))
6817                         ex_reg (sim_log, val, flag, rptr, idx-1);
6818                     }
6819                 else {
6820                     if (val_start+1 != idx-1) {
6821                         (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6822                         if (sim_log && (ofile == stdout))
6823                             (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6824                         }
6825                     else {
6826                         (void)Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6827                         if (sim_log && (ofile == stdout))
6828                             (void)Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6829                         }
6830                     }
6831                 }
6832             sim_last_val = last_val = val;
6833             val_start = idx;
6834             reason = ex_reg (ofile, val, flag, rptr, idx);
6835             if (reason != SCPE_OK)
6836                 return reason;
6837             if (sim_log && (ofile == stdout))
6838                 ex_reg (sim_log, val, flag, rptr, idx);
6839             }
6840         if (flag != EX_E) {
6841             reason = dep_reg (flag, cptr, rptr, idx);
6842             if (reason != SCPE_OK)
6843                 return reason;
6844             }
6845         }
6846     if ((flag == EX_E) && (val_start != highs)) {
6847         if (highs == val_start+1) {
6848             reason = ex_reg (ofile, val, flag, rptr, highs);
6849             if (reason != SCPE_OK)
6850                 return reason;
6851             if (sim_log && (ofile == stdout))
6852                 ex_reg (sim_log, val, flag, rptr, highs);
6853             }
6854         else {
6855             if (val_start+1 != highs) {
6856                 (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6857                 if (sim_log && (ofile == stdout))
6858                     (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6859                 }
6860             else {
6861                 (void)Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6862                 if (sim_log && (ofile == stdout))
6863                     (void)Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6864                 }
6865             }
6866         }
6867     }
6868 return SCPE_OK;
6869 }
6870 
6871 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6872     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6873 {
6874 t_addr i, mask;
6875 t_stat reason;
6876 
6877 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6878     return SCPE_UDIS;
6879 mask = (t_addr) width_mask[dptr->awidth];
6880 if ((low > mask) || (high > mask) || (low > high))
6881     return SCPE_ARG;
6882 for (i = low; i <= high; ) {                            /* all paths must incr!! */
6883     reason = get_aval (i, dptr, uptr);                  /* get data */
6884     if (reason != SCPE_OK)                              /* return if error */
6885         return reason;
6886     if (schptr && !test_search (sim_eval, schptr))
6887         i = i + dptr->aincr;                            /* sch fails, incr */
6888     else {                                              /* no sch or success */
6889         if (flag != EX_D) {                             /* ex, ie, or id? */
6890             reason = ex_addr (ofile, flag, i, dptr, uptr);
6891             if (reason > SCPE_OK)
6892                 return reason;
6893             if (sim_log && (ofile == stdout))
6894                 ex_addr (sim_log, flag, i, dptr, uptr);
6895             }
6896         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
6897         if (flag != EX_E) {                             /* ie, id, or d? */
6898             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6899             if (reason > SCPE_OK)
6900                 return reason;
6901             }
6902         i = i + (1 - reason);                           /* incr */
6903         }
6904     }
6905 return SCPE_OK;
6906 }
6907 
6908 /* Examine register routine
6909 
6910    Inputs:
6911         ofile   =       output stream
6912         val     =       current register value
6913         flag    =       type of ex/mod command (ex, iex, idep)
6914         rptr    =       pointer to register descriptor
6915         idx     =       index
6916    Outputs:
6917         return  =       error status
6918 */
6919 
6920 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6921 {
6922 int32 rdx;
6923 
6924 if (rptr == NULL)
6925     return SCPE_IERR;
6926 if (rptr->depth > 1)
6927     (void)Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6928 else
6929     (void)Fprintf (ofile, "%s:\t", rptr->name);
6930 if (!(flag & EX_E))
6931     return SCPE_OK;
6932 GET_RADIX (rdx, rptr->radix);
6933 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6934     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6935 else if (!(rptr->flags & REG_VMFLAGS) ||
6936     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6937                  NULL, sim_switches | SIM_SW_REG) > 0)) {
6938         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6939         if (rptr->fields) {
6940             (void)Fprintf (ofile, "\t");
6941             fprint_fields (ofile, val, val, rptr->fields);
6942             }
6943         }
6944 if (flag & EX_I)
6945     (void)Fprintf (ofile, "\t");
6946 else
6947     (void)Fprintf (ofile, "\n");
6948 return SCPE_OK;
6949 }
6950 
6951 /* Get register value
6952 
6953    Inputs:
6954         rptr    =       pointer to register descriptor
6955         idx     =       index
6956    Outputs:
6957         return  =       register value
6958 */
6959 
6960 t_value get_rval (REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6961 {
6962 size_t sz;
6963 t_value val;
6964 uint32 *ptr;
6965 
6966 sz = SZ_R (rptr);
6967 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6968     idx = idx + rptr->qptr;
6969     if (idx >= rptr->depth) idx = idx - rptr->depth;
6970     }
6971 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6972     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6973     if (sz <= sizeof (uint32))
6974         val = *ptr;
6975     else val = *((t_uint64 *) ptr); //-V1032
6976     }
6977 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6978     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6979     if (sz <= sizeof (uint32))
6980         val = *ptr;
6981     else val = *((t_uint64 *) ptr);
6982     }
6983 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6984     (sz == sizeof (uint8)))
6985     val = *(((uint8 *) rptr->loc) + idx);
6986 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6987     (sz == sizeof (uint16)))
6988     val = *(((uint16 *) rptr->loc) + idx);
6989 else if (sz <= sizeof (uint32))
6990      val = *(((uint32 *) rptr->loc) + idx);
6991 else val = *(((t_uint64 *) rptr->loc) + idx);
6992 val = (val >> rptr->offset) & width_mask[rptr->width];
6993 return val;
6994 }
6995 
6996 /* Deposit register routine
6997 
6998    Inputs:
6999         flag    =       type of deposit (normal/interactive)
7000         cptr    =       pointer to input string
7001         rptr    =       pointer to register descriptor
7002         idx     =       index
7003    Outputs:
7004         return  =       error status
7005 */
7006 
7007 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
7008 {
7009 t_stat r;
7010 t_value val, mask;
7011 int32 rdx;
7012 CONST char *tptr;
7013 char gbuf[CBUFSIZE];
7014 
7015 if ((cptr == NULL) || (rptr == NULL))
7016     return SCPE_IERR;
7017 if (rptr->flags & REG_RO)
7018     return SCPE_RO;
7019 if (flag & EX_I) {
7020     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7021     if (sim_log)
7022         (void)fprintf (sim_log, "%s\n", cptr? cptr: "");
7023     if (cptr == NULL)                                   /* force exit */
7024         return 1;
7025     if (*cptr == 0)                                     /* success */
7026         return SCPE_OK;
7027     }
7028 mask = width_mask[rptr->width];
7029 GET_RADIX (rdx, rptr->radix);
7030 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
7031     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
7032     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
7033         return SCPE_ARG;
7034     }
7035 else
7036     if (!(rptr->flags & REG_VMFLAGS) ||                 /* don't use sym? */
7037         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
7038                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
7039     val = get_uint (cptr, rdx, mask, &r);
7040     if (r != SCPE_OK)
7041         return SCPE_ARG;
7042     }
7043 if ((rptr->flags & REG_NZ) && (val == 0))
7044     return SCPE_ARG;
7045 put_rval (rptr, idx, val);
7046 return SCPE_OK;
7047 }
7048 
7049 /* Put register value
7050 
7051    Inputs:
7052         rptr    =       pointer to register descriptor
7053         idx     =       index
7054         val     =       new value
7055         mask    =       mask
7056    Outputs:
7057         none
7058 */
7059 
7060 void put_rval (REG *rptr, uint32 idx, t_value val)
     /* [previous][next][first][last][top][bottom][index][help] */
7061 {
7062 size_t sz;
7063 t_value mask;
7064 uint32 *ptr;
7065 
7066 #define PUT_RVAL(sz,rp,id,v,m)            \
7067     *(((sz *) rp->loc) + id) =            \
7068         (sz)((*(((sz *) rp->loc) + id) &  \
7069             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
7070 
7071 if (rptr == sim_PC)
7072     sim_brk_npc (0);
7073 sz = SZ_R (rptr);
7074 mask = width_mask[rptr->width];
7075 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
7076     idx = idx + rptr->qptr;
7077     if (idx >= rptr->depth)
7078         idx = idx - rptr->depth;
7079     }
7080 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
7081     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
7082     if (sz <= sizeof (uint32))
7083         *ptr = (*ptr &
7084         ~(((uint32) mask) << rptr->offset)) |
7085         (((uint32) val) << rptr->offset);
7086     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr) //-V1032
7087         & ~(mask << rptr->offset)) | (val << rptr->offset);
7088     }
7089 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
7090     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
7091     if (sz <= sizeof (uint32))
7092         *((uint32 *) ptr) = (*((uint32 *) ptr) &
7093         ~(((uint32) mask) << rptr->offset)) |
7094         (((uint32) val) << rptr->offset);
7095     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
7096         & ~(mask << rptr->offset)) | (val << rptr->offset);
7097     }
7098 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7099     (sz == sizeof (uint8)))
7100     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
7101 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7102     (sz == sizeof (uint16)))
7103     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
7104 else if (sz <= sizeof (uint32))
7105     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
7106 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
7107 return;
7108 }
7109 
7110 /* Examine address routine
7111 
7112    Inputs: (sim_eval is an implicit argument)
7113         ofile   =       output stream
7114         flag    =       type of ex/mod command (ex, iex, idep)
7115         addr    =       address to examine
7116         dptr    =       pointer to device
7117         uptr    =       pointer to unit
7118    Outputs:
7119         return  =       if > 0, error status
7120                         if <= 0,-number of extra addr units retired
7121 */
7122 
7123 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7124 {
7125 t_stat reason;
7126 int32 rdx;
7127 
7128 if (sim_vm_fprint_addr)
7129     sim_vm_fprint_addr (ofile, dptr, addr);
7130 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
7131 (void)Fprintf (ofile, ":\t");
7132 if (!(flag & EX_E))
7133     return (1 - dptr->aincr);
7134 
7135 GET_RADIX (rdx, dptr->dradix);
7136 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
7137     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
7138     reason = 1 - dptr->aincr;
7139     }
7140 if (flag & EX_I)
7141     (void)Fprintf (ofile, "\t");
7142 else
7143     (void)Fprintf (ofile, "\n");
7144 return reason;
7145 }
7146 
7147 /* Get address routine
7148 
7149    Inputs:
7150         flag    =       type of ex/mod command (ex, iex, idep)
7151         addr    =       address to examine
7152         dptr    =       pointer to device
7153         uptr    =       pointer to unit
7154    Outputs: (sim_eval is an implicit output)
7155         return  =       error status
7156 */
7157 
7158 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7159 {
7160 int32 i;
7161 t_value mask;
7162 t_addr j, loc;
7163 size_t sz;
7164 t_stat reason = SCPE_OK;
7165 
7166 if ((dptr == NULL) || (uptr == NULL))
7167     return SCPE_IERR;
7168 mask = width_mask[dptr->dwidth];
7169 for (i = 0; i < sim_emax; i++)
7170     sim_eval[i] = 0;
7171 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
7172     if (dptr->examine != NULL) {
7173         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
7174         if (reason != SCPE_OK)
7175             break;
7176         }
7177     else {
7178         if (!(uptr->flags & UNIT_ATT))
7179             return SCPE_UNATT;
7180         if (uptr->dynflags & UNIT_NO_FIO)
7181             return SCPE_NOFNC;
7182         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
7183             reason = SCPE_NXM;
7184             break;
7185             }
7186         sz = SZ_D (dptr);
7187         loc = j / dptr->aincr;
7188         if (uptr->flags & UNIT_BUF) {
7189             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
7190             }
7191         else {
7192             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7193             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
7194             if ((feof (uptr->fileref)) &&
7195                !(uptr->flags & UNIT_FIX)) {
7196                 reason = SCPE_EOF;
7197                 break;
7198                 }
7199             else if (ferror (uptr->fileref)) {
7200                 clearerr (uptr->fileref);
7201                 reason = SCPE_IOERR;
7202                 break;
7203                 }
7204             }
7205         }
7206     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
7207     }
7208 if ((reason != SCPE_OK) && (i == 0))
7209     return reason;
7210 return SCPE_OK;
7211 }
7212 
7213 /* Deposit address routine
7214 
7215    Inputs:
7216         flag    =       type of deposit (normal/interactive)
7217         cptr    =       pointer to input string
7218         addr    =       address to examine
7219         dptr    =       pointer to device
7220         uptr    =       pointer to unit
7221         dfltinc =       value to return on cr input
7222    Outputs:
7223         return  =       if > 0, error status
7224                         if <= 0, -number of extra address units retired
7225 */
7226 
7227 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
7228     UNIT *uptr, int32 dfltinc)
7229 {
7230 int32 i, count, rdx;
7231 t_addr j, loc;
7232 t_stat r, reason;
7233 t_value mask;
7234 size_t sz;
7235 char gbuf[CBUFSIZE];
7236 
7237 if (dptr == NULL)
7238     return SCPE_IERR;
7239 if (flag & EX_I) {
7240     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7241     if (sim_log)
7242         (void)fprintf (sim_log, "%s\n", cptr? cptr: "");
7243     if (cptr == NULL)                                   /* force exit */
7244         return 1;
7245     if (*cptr == 0)                                     /* success */
7246         return dfltinc;
7247     }
7248 if (uptr->flags & UNIT_RO)                              /* read only? */
7249     return SCPE_RO;
7250 mask = width_mask[dptr->dwidth];
7251 
7252 GET_RADIX (rdx, dptr->dradix);
7253 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
7254     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
7255     if (reason != SCPE_OK)
7256         return reason;
7257     reason = dfltinc;
7258     }
7259 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
7260 
7261 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
7262     sim_eval[i] = sim_eval[i] & mask;
7263     if (dptr->deposit != NULL) {
7264         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
7265         if (r != SCPE_OK)
7266             return r;
7267         }
7268     else {
7269         if (!(uptr->flags & UNIT_ATT))
7270             return SCPE_UNATT;
7271         if (uptr->dynflags & UNIT_NO_FIO)
7272             return SCPE_NOFNC;
7273         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
7274             return SCPE_NXM;
7275         sz = SZ_D (dptr);
7276         loc = j / dptr->aincr;
7277         if (uptr->flags & UNIT_BUF) {
7278             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
7279             if (loc >= uptr->hwmark)
7280                 uptr->hwmark = (uint32) loc + 1;
7281             }
7282         else {
7283             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7284             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
7285             if (ferror (uptr->fileref)) {
7286                 clearerr (uptr->fileref);
7287                 return SCPE_IOERR;
7288                 }
7289             }
7290         }
7291     }
7292 return reason;
7293 }
7294 
7295 /* Evaluate command */
7296 
7297 t_stat eval_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7298 {
7299 if (!sim_dflt_dev)
7300   return SCPE_ARG;
7301 DEVICE *dptr = sim_dflt_dev;
7302 int32 i, rdx, a, lim;
7303 t_stat r;
7304 
7305 GET_SWITCHES (cptr);
7306 GET_RADIX (rdx, dptr->dradix);
7307 for (i = 0; i < sim_emax; i++)
7308 sim_eval[i] = 0;
7309 if (*cptr == 0)
7310     return SCPE_2FARG;
7311 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
7312     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
7313     if (r != SCPE_OK)
7314         return r;
7315     }
7316 lim = 1 - r;
7317 for (i = a = 0; a < lim; ) {
7318     sim_printf ("%d:\t", a);
7319     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7320         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7321     if (sim_log) {
7322         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7323             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7324         }
7325     sim_printf ("\n");
7326     if (r < 0)
7327         a = a + 1 - r;
7328     else a = a + dptr->aincr;
7329     i = a / dptr->aincr;
7330     }
7331 return SCPE_OK;
7332 }
7333 
7334 /* String processing routines
7335 
7336    read_line            read line
7337 
7338    Inputs:
7339         cptr    =       pointer to buffer
7340         size    =       maximum size
7341         stream  =       pointer to input stream
7342    Outputs:
7343         optr    =       pointer to first non-blank character
7344                         NULL if EOF
7345 */
7346 
7347 char *read_line (char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7348 {
7349 return read_line_p (NULL, cptr, size, stream);
7350 }
7351 
7352 /* read_line_p          read line with prompt
7353 
7354    Inputs:
7355         prompt  =       pointer to prompt string
7356         cptr    =       pointer to buffer
7357         size    =       maximum size
7358         stream  =       pointer to input stream
7359    Outputs:
7360         optr    =       pointer to first non-blank character
7361                         NULL if EOF
7362 */
7363 
7364 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7365 {
7366 char *tptr;
7367 
7368 if (prompt) {                                           /* interactive? */
7369 #if defined(HAVE_LINEHISTORY)
7370         char *tmpc = linenoise (prompt);                /* get cmd line */
7371         if (tmpc == NULL)                               /* bad result? */
7372             cptr = NULL;
7373         else {
7374             strncpy (cptr, tmpc, size-1);               /* copy result */
7375             linenoiseHistoryAdd (tmpc);                 /* add to history */
7376             FREE (tmpc);                                /* free temp */
7377             }
7378         }
7379 #else
7380         (void)fflush (stdout);                          /* flush output */
7381         (void)printf ("%s", prompt);                    /* display prompt */
7382         (void)fflush (stdout);                          /* flush output */
7383         cptr = fgets (cptr, size, stream);              /* get cmd line */
7384         }
7385 #endif /* if defined(HAVE_LINEHISTORY) */
7386 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
7387 
7388 if (cptr == NULL) {
7389     clearerr (stream);                                  /* clear error */
7390     return NULL;                                        /* ignore EOF */
7391     }
7392 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
7393     if ((*tptr == '\n') || (*tptr == '\r') ||
7394         (tptr == (cptr + size - 1))) {                  /* str max length? */
7395         *tptr = 0;                                      /* terminate */
7396         break;
7397         }
7398     }
7399 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
7400     memmove (cptr, cptr + 3, strlen (cptr + 3));
7401 while (sim_isspace (*cptr))                             /* trim leading spc */
7402     cptr++;
7403 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
7404     if (sim_do_echo)                                    /* echo comments if -v */
7405         sim_printf("%s> %s\n", do_position(), cptr);
7406     *cptr = 0;
7407     }
7408 
7409 return cptr;
7410 }
7411 
7412 /* get_glyph            get next glyph (force upper case)
7413    get_glyph_nc         get next glyph (no conversion)
7414    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
7415    get_glyph_cmd        get command glyph (force upper case, extract leading !)
7416    get_glyph_gen        get next glyph (general case)
7417 
7418    Inputs:
7419         iptr        =   pointer to input string
7420         optr        =   pointer to output string
7421         mchar       =   optional end of glyph character
7422         uc          =   TRUE for convert to upper case (_gen only)
7423         quote       =   TRUE to allow quote enclosing values (_gen only)
7424         escape_char =   optional escape character within quoted strings (_gen only)
7425 
7426    Outputs
7427         result      =   pointer to next character in input string
7428 */
7429 
7430 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] */
7431 {
7432 t_bool quoting = FALSE;
7433 t_bool escaping = FALSE;
7434 char quote_char = 0;
7435 
7436 while ((*iptr != 0) &&
7437        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
7438     if (quote) {
7439         if (quoting) {
7440             if (!escaping) {
7441                 if (*iptr == escape_char)
7442                     escaping = TRUE;
7443                 else
7444                     if (*iptr == quote_char)
7445                         quoting = FALSE;
7446                 }
7447             else
7448                 escaping = FALSE;
7449             }
7450         else {
7451             if ((*iptr == '"') || (*iptr == '\'')) {
7452                 quoting = TRUE;
7453                 quote_char = *iptr;
7454                 }
7455             }
7456         }
7457     if (sim_islower (*iptr) && uc)
7458         *optr = (char)toupper (*iptr);
7459     else *optr = *iptr;
7460     iptr++; optr++;
7461     }
7462 if (mchar && (*iptr == mchar))              /* skip input terminator */
7463     iptr++;
7464 *optr = 0;                                  /* terminate result string */
7465 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
7466     iptr++;
7467 return iptr;
7468 }
7469 
7470 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7471 {
7472 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7473 }
7474 
7475 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7476 {
7477 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7478 }
7479 
7480 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7481 {
7482 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7483 }
7484 
7485 CONST char *get_glyph_cmd (const char *iptr, char *optr)
     /* [previous][next][first][last][top][bottom][index][help] */
7486 {
7487 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7488 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7489     strcpy (optr, "!");                     /* return ! as command glyph */
7490     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
7491     }
7492 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7493 }
7494 
7495 /* Trim trailing spaces from a string
7496 
7497     Inputs:
7498         cptr    =       pointer to string
7499     Outputs:
7500         cptr    =       pointer to string
7501 */
7502 
7503 char *sim_trim_endspc (char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7504 {
7505 char *tptr;
7506 
7507 tptr = cptr + strlen (cptr);
7508 while ((--tptr >= cptr) && sim_isspace (*tptr))
7509     *tptr = 0;
7510 return cptr;
7511 }
7512 
7513 int sim_isspace (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7514 {
7515 return (c & 0x80) ? 0 : isspace (c);
7516 }
7517 
7518 int sim_islower (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7519 {
7520 return (c & 0x80) ? 0 : islower (c);
7521 }
7522 
7523 int sim_isalpha (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7524 {
7525 return (c & 0x80) ? 0 : isalpha (c);
7526 }
7527 
7528 int sim_isprint (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7529 {
7530 return (c & 0x80) ? 0 : isprint (c);
7531 }
7532 
7533 int sim_isdigit (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7534 {
7535 return (c & 0x80) ? 0 : isdigit (c);
7536 }
7537 
7538 int sim_isgraph (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7539 {
7540 return (c & 0x80) ? 0 : isgraph (c);
7541 }
7542 
7543 int sim_isalnum (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7544 {
7545 return (c & 0x80) ? 0 : isalnum (c);
7546 }
7547 
7548 /* get_uint             unsigned number
7549 
7550    Inputs:
7551         cptr    =       pointer to input string
7552         radix   =       input radix
7553         max     =       maximum acceptable value
7554         *status =       pointer to error status
7555    Outputs:
7556         val     =       value
7557 */
7558 
7559 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
7560 {
7561 t_value val;
7562 CONST char *tptr;
7563 
7564 *status = SCPE_OK;
7565 val = strtotv ((CONST char *)cptr, &tptr, radix);
7566 if ((cptr == tptr) || (val > max))
7567     *status = SCPE_ARG;
7568 else {
7569     while (sim_isspace (*tptr)) tptr++;
7570     if (*tptr != 0)
7571         *status = SCPE_ARG;
7572     }
7573 return val;
7574 }
7575 
7576 /* get_range            range specification
7577 
7578    Inputs:
7579         dptr    =       pointer to device (NULL if none)
7580         cptr    =       pointer to input string
7581         *lo     =       pointer to low result
7582         *hi     =       pointer to high result
7583         aradix  =       radix
7584         max     =       default high value
7585         term    =       terminating character, 0 if none
7586    Outputs:
7587         tptr    =       input pointer after processing
7588                         NULL if error
7589 */
7590 
7591 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
     /* [previous][next][first][last][top][bottom][index][help] */
7592     uint32 rdx, t_addr max, char term)
7593 {
7594 CONST char *tptr;
7595 
7596 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
7597     tptr = cptr + strlen ("ALL");
7598     *lo = 0;
7599     *hi = max;
7600     }
7601 else {
7602     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
7603         ((cptr[1] == '\0') ||
7604          (cptr[1] == '-')  ||
7605          (cptr[1] == ':')  ||
7606          (cptr[1] == '/'))) {
7607         tptr = cptr + strlen (".");
7608         *lo = *hi = sim_last_addr;
7609         }
7610     else {
7611         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
7612             tptr = cptr + strlen ("$");
7613             *hi = *lo = (t_addr)sim_last_val;
7614             }
7615         else {
7616             if (dptr && sim_vm_parse_addr)                      /* get low */
7617                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7618             else
7619                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7620             if (cptr == tptr)                                   /* error? */
7621                     return NULL;
7622             }
7623         }
7624     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
7625         cptr = tptr + 1;
7626         if (dptr && sim_vm_parse_addr)                  /* get high */
7627             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7628         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7629         if (cptr == tptr)
7630             return NULL;
7631         if (*lo > *hi)
7632             return NULL;
7633         }
7634     else if (*tptr == '/') {                            /* relative? */
7635         cptr = tptr + 1;
7636         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7637         if ((cptr == tptr) || (*hi == 0))
7638             return NULL;
7639         *hi = *lo + *hi - 1;
7640         }
7641     else *hi = *lo;
7642     }
7643 sim_last_addr = *hi;
7644 if (term && (*tptr++ != term))
7645     return NULL;
7646 return tptr;
7647 }
7648 
7649 /* sim_decode_quoted_string
7650 
7651    Inputs:
7652         iptr        =   pointer to input string
7653         optr        =   pointer to output buffer
7654                         the output buffer must be allocated by the caller
7655                         and to avoid overrunat it must be at least as big
7656                         as the input string.
7657 
7658    Outputs
7659         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7660         osize       =   size of the data in the optr buffer
7661 
7662    The input string must be quoted.  Quotes may be either single or
7663    double but the opening anc closing quote characters must match.
7664    Within quotes C style character escapes are allowed.
7665 */
7666 
7667 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
     /* [previous][next][first][last][top][bottom][index][help] */
7668 {
7669 char quote_char;
7670 uint8 *ostart = optr;
7671 
7672 *osize = 0;
7673 if ((strlen(iptr) == 1) ||
7674     (iptr[0] != iptr[strlen(iptr)-1]) ||
7675     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7676     return SCPE_ARG;            /* String must be quote delimited */
7677 quote_char = *iptr++;           /* Save quote character */
7678 while (iptr[1]) {               /* Skip trailing quote */
7679     if (*iptr != '\\') {
7680         if (*iptr == quote_char)
7681             return SCPE_ARG;    /* Embedded quotes must be escaped */
7682         *(optr++) = (uint8)(*(iptr++));
7683         continue;
7684         }
7685     ++iptr; /* Skip backslash */
7686     switch (*iptr) {
7687         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7688             *(optr++) = 13; ++iptr;
7689             break;
7690         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7691             *(optr++) = 10; ++iptr;
7692             break;
7693         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7694             *(optr++) = 12; ++iptr;
7695             break;
7696         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7697             *(optr++) = 9; ++iptr;
7698             break;
7699         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7700             *(optr++) = 11; ++iptr;
7701             break;
7702         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7703             *(optr++) = 8; ++iptr;
7704             break;
7705         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7706             *(optr++) = 92; ++iptr;
7707             break;
7708         case 'e':   /* ASCII Escape character (Decimal value 27) */
7709             *(optr++) = 27; ++iptr;
7710             break;
7711         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7712             *(optr++) = 39; ++iptr;
7713             break;
7714         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7715             *(optr++) = 34; ++iptr;
7716             break;
7717         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7718             *(optr++) = 63; ++iptr;
7719             break;
7720         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7721             *optr = *(iptr++) - '0';
7722             if ((*iptr >= '0') && (*iptr <= '7'))
7723                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7724             if ((*iptr >= '0') && (*iptr <= '7'))
7725                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7726             ++optr;
7727             break;
7728         case 'x':
7729             if (1) {
7730                 static const char *hex_digits = "0123456789ABCDEF";
7731                 const char *c;
7732 
7733                 ++iptr;
7734                 *optr = 0;
7735                 c = strchr (hex_digits, toupper(*iptr));
7736                 if (c) {
7737                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7738                     ++iptr;
7739                     }
7740                 c = strchr (hex_digits, toupper(*iptr));
7741                 if (c) {
7742                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7743                     ++iptr;
7744                     }
7745                 ++optr;
7746                 }
7747             break;
7748         default:
7749             return SCPE_ARG;    /* Invalid escape */
7750         }
7751     }
7752 *optr = '\0';
7753 *osize = (uint32)(optr-ostart);
7754 return SCPE_OK;
7755 }
7756 
7757 /* sim_encode_quoted_string
7758 
7759    Inputs:
7760         iptr        =   pointer to input buffer
7761         size        =   number of bytes of data in the buffer
7762 
7763    Outputs
7764         optr        =   pointer to output buffer
7765                         the output buffer must be freed by the caller
7766 
7767    The input data will be encoded into a simply printable form.
7768 */
7769 
7770 char *sim_encode_quoted_string (const uint8 *iptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
7771 {
7772 size_t i;
7773 t_bool double_quote_found = FALSE;
7774 t_bool single_quote_found = FALSE;
7775 char quote = '"';
7776 char *tptr, *optr;
7777 
7778 optr = (char *)malloc (4*size + 3);
7779 if (optr == NULL)
7780     return NULL;
7781 tptr = optr;
7782 for (i=0; i<size; i++)
7783     switch ((char)iptr[i]) {
7784         case '"':
7785             double_quote_found = TRUE;
7786             break;
7787         case '\'':
7788             single_quote_found = TRUE;
7789             break;
7790         }
7791 if (double_quote_found && (!single_quote_found))
7792     quote = '\'';
7793 *tptr++ = quote;
7794 while (size--) {
7795     switch (*iptr) {
7796         case '\r':
7797             *tptr++ = '\\'; *tptr++ = 'r'; break;
7798         case '\n':
7799             *tptr++ = '\\'; *tptr++ = 'n'; break;
7800         case '\f':
7801             *tptr++ = '\\'; *tptr++ = 'f'; break;
7802         case '\t':
7803             *tptr++ = '\\'; *tptr++ = 't'; break;
7804         case '\v':
7805             *tptr++ = '\\'; *tptr++ = 'v'; break;
7806         case '\b':
7807             *tptr++ = '\\'; *tptr++ = 'b'; break;
7808         case '\\':
7809             *tptr++ = '\\'; *tptr++ = '\\'; break;
7810         case '"':
7811         case '\'':
7812             if (quote == *iptr)
7813                 *tptr++ = '\\';
7814         /*FALLTHRU*/ /* fall through */ /* fallthrough */
7815         default:
7816             if (sim_isprint (*iptr))
7817                 *tptr++ = *iptr;
7818             else {
7819                 (void)sprintf (tptr, "\\%03o", *iptr);
7820                 tptr += 4;
7821                 }
7822             break;
7823         }
7824     ++iptr;
7825     }
7826 *tptr++ = quote;
7827 *tptr++ = '\0';
7828 return optr;
7829 }
7830 
7831 void fprint_buffer_string (FILE *st, const uint8 *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
7832 {
7833 char *string;
7834 
7835 string = sim_encode_quoted_string (buf, size);
7836 (void)fprintf (st, "%s", string);
7837 FREE (string);
7838 }
7839 
7840 /* Find_device          find device matching input string
7841 
7842    Inputs:
7843         cptr    =       pointer to input string
7844    Outputs:
7845         result  =       pointer to device
7846 */
7847 
7848 DEVICE *find_dev (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7849 {
7850 int32 i;
7851 DEVICE *dptr;
7852 
7853 if (cptr == NULL)
7854     return NULL;
7855 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7856     if ((strcmp (cptr, dptr->name) == 0) ||
7857         (dptr->lname &&
7858         (strcmp (cptr, dptr->lname) == 0)))
7859         return dptr;
7860     }
7861 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7862     if ((strcmp (cptr, dptr->name) == 0) ||
7863         (dptr->lname &&
7864         (strcmp (cptr, dptr->lname) == 0)))
7865         return dptr;
7866     }
7867 return NULL;
7868 }
7869 
7870 /* Find_unit            find unit matching input string
7871 
7872    Inputs:
7873         cptr    =       pointer to input string
7874         uptr    =       pointer to unit pointer
7875    Outputs:
7876         result  =       pointer to device (null if no dev)
7877         *iptr   =       pointer to unit (null if nx unit)
7878 
7879 */
7880 
7881 DEVICE *find_unit (const char *cptr, UNIT **uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7882 {
7883 uint32 i, u;
7884 const char *nptr;
7885 const char *tptr;
7886 t_stat r;
7887 DEVICE *dptr;
7888 
7889 if (uptr == NULL)                                       /* arg error? */
7890     return NULL;
7891 *uptr = NULL;
7892 if ((dptr = find_dev (cptr))) {                         /* exact match? */
7893     if (qdisable (dptr))                                /* disabled? */
7894         return NULL;
7895     *uptr = dptr->units;                                /* unit 0 */
7896     return dptr;
7897     }
7898 
7899 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
7900     if (qdisable (dptr))                                /* device disabled? */
7901         continue;
7902     if (dptr->numunits && /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/ /* any units? */
7903         (((nptr = dptr->name) &&
7904           (strncmp (cptr, nptr, strlen (nptr)) == 0)) || /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7905          ((nptr = dptr->lname) &&
7906           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7907         tptr = cptr + strlen (nptr);
7908         if (sim_isdigit (*tptr)) {
7909             if (qdisable (dptr))                        /* disabled? */
7910                 return NULL;
7911             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7912             if (r != SCPE_OK)                           /* error? */
7913                 *uptr = NULL;
7914             else
7915                 *uptr = dptr->units + u;
7916             return dptr;
7917             }
7918         }
7919     for (u = 0; u < dptr->numunits; u++) {
7920         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7921             *uptr = &dptr->units[u];
7922             return dptr;
7923             }
7924         }
7925     }
7926 return NULL;
7927 }
7928 
7929 /* sim_register_internal_device   Add device to internal device list
7930 
7931    Inputs:
7932         dptr    =       pointer to device
7933 */
7934 
7935 t_stat sim_register_internal_device (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7936 {
7937 uint32 i;
7938 
7939 for (i = 0; (sim_devices[i] != NULL); i++)
7940     if (sim_devices[i] == dptr)
7941         return SCPE_OK;
7942 for (i = 0; i < sim_internal_device_count; i++)
7943     if (sim_internal_devices[i] == dptr)
7944         return SCPE_OK;
7945 ++sim_internal_device_count;
7946 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7947 if (!sim_internal_devices)
7948   {
7949     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7950                    __func__, __FILE__, __LINE__);
7951 #if defined(USE_BACKTRACE)
7952 # if defined(SIGUSR2)
7953     (void)raise(SIGUSR2);
7954     /*NOTREACHED*/ /* unreachable */
7955 # endif /* if defined(SIGUSR2) */
7956 #endif /* if defined(USE_BACKTRACE) */
7957     abort();
7958   }
7959 sim_internal_devices[sim_internal_device_count-1] = dptr;
7960 sim_internal_devices[sim_internal_device_count] = NULL;
7961 return SCPE_OK;
7962 }
7963 
7964 /* Find_dev_from_unit   find device for unit
7965 
7966    Inputs:
7967         uptr    =       pointer to unit
7968    Outputs:
7969         result  =       pointer to device
7970 */
7971 
7972 DEVICE *find_dev_from_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7973 {
7974 DEVICE *dptr;
7975 uint32 i, j;
7976 
7977 if (uptr == NULL)
7978     return NULL;
7979 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7980     for (j = 0; j < dptr->numunits; j++) {
7981         if (uptr == (dptr->units + j))
7982             return dptr;
7983         }
7984     }
7985 for (i = 0; i<sim_internal_device_count; i++) {
7986     dptr = sim_internal_devices[i];
7987     for (j = 0; j < dptr->numunits; j++) {
7988         if (uptr == (dptr->units + j))
7989             return dptr;
7990         }
7991     }
7992 return NULL;
7993 }
7994 
7995 /* Test for disabled device */
7996 
7997 t_bool qdisable (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7998 {
7999 return (dptr->flags & DEV_DIS? TRUE: FALSE);
8000 }
8001 
8002 /* find_reg_glob        find globally unique register
8003 
8004    Inputs:
8005         cptr    =       pointer to input string
8006         optr    =       pointer to output pointer (can be null)
8007         gdptr   =       pointer to global device
8008    Outputs:
8009         result  =       pointer to register, NULL if error
8010         *optr   =       pointer to next character in input string
8011         *gdptr  =       pointer to device where found
8012 */
8013 
8014 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8015 {
8016 int32 i;
8017 DEVICE *dptr;
8018 REG *rptr, *srptr = NULL;
8019 
8020 *gdptr = NULL;
8021 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
8022     if (dptr->flags & DEV_DIS)                          /* skip disabled */
8023         continue;
8024     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
8025         if (srptr)                                      /* ambig? err */
8026             return NULL;
8027         srptr = rptr;                                   /* save reg */
8028         *gdptr = dptr;                                  /* save unit */
8029         }
8030     }
8031 return srptr;
8032 }
8033 
8034 /* find_reg             find register matching input string
8035 
8036    Inputs:
8037         cptr    =       pointer to input string
8038         optr    =       pointer to output pointer (can be null)
8039         dptr    =       pointer to device
8040    Outputs:
8041         result  =       pointer to register, NULL if error
8042         *optr   =       pointer to next character in input string
8043 */
8044 
8045 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8046 {
8047 CONST char *tptr;
8048 REG *rptr;
8049 size_t slnt;
8050 
8051 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
8052     return NULL;
8053 tptr = cptr;
8054 do {
8055     tptr++;
8056     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
8057 slnt = tptr - cptr;
8058 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
8059     if ((slnt == strlen (rptr->name)) &&
8060         (strncmp (cptr, rptr->name, slnt) == 0)) {
8061         if (optr != NULL)
8062             *optr = tptr;
8063         return rptr;
8064         }
8065     }
8066 return NULL;
8067 }
8068 
8069 /* get_switches         get switches from input string
8070 
8071    Inputs:
8072         cptr    =       pointer to input string
8073    Outputs:
8074         sw      =       switch bit mask
8075                         0 if no switches, -1 if error
8076 */
8077 
8078 int32 get_switches (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8079 {
8080 int32 sw;
8081 
8082 if (*cptr != '-')
8083     return 0;
8084 sw = 0;
8085 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
8086     if (sim_isalpha (*cptr) == 0)
8087         return -1;
8088     sw = sw | SWMASK (toupper (*cptr));
8089     }
8090 return sw;
8091 }
8092 
8093 /* get_sim_sw           accumulate sim_switches
8094 
8095    Inputs:
8096         cptr    =       pointer to input string
8097    Outputs:
8098         ptr     =       pointer to first non-string glyph
8099                         NULL if error
8100 */
8101 
8102 CONST char *get_sim_sw (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8103 {
8104 int32 lsw;
8105 char gbuf[CBUFSIZE];
8106 
8107 while (*cptr == '-') {                                  /* while switches */
8108     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
8109     lsw = get_switches (gbuf);                          /* parse */
8110     if (lsw <= 0)                                       /* invalid? */
8111         return NULL;
8112     sim_switches = sim_switches | lsw;                  /* accumulate */
8113     }
8114 return cptr;
8115 }
8116 
8117 /* get_sim_opt          get simulator command options
8118 
8119    Inputs:
8120         opt     =       command options
8121         cptr    =       pointer to input string
8122    Outputs:
8123         ptr     =       pointer to next glyph, NULL if error
8124         *stat   =       error status
8125 */
8126 
8127 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
     /* [previous][next][first][last][top][bottom][index][help] */
8128 {
8129 int32 t;
8130 char gbuf[CBUFSIZE];
8131 CONST char *svptr;
8132 DEVICE *tdptr;
8133 UNIT *tuptr;
8134 
8135 sim_switches = 0;                                       /* no switches */
8136 sim_ofile = NULL;                                       /* no output file */
8137 sim_schrptr = NULL;                                     /* no search */
8138 sim_schaptr = NULL;                                     /* no search */
8139 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
8140 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
8141 sim_stabr.count = 1;
8142 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
8143 if (!sim_stabr.mask)
8144   {
8145     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8146                    __func__, __FILE__, __LINE__);
8147 #if defined(USE_BACKTRACE)
8148 # if defined(SIGUSR2)
8149     (void)raise(SIGUSR2);
8150     /*NOTREACHED*/ /* unreachable */
8151 # endif /* if defined(SIGUSR2) */
8152 #endif /* if defined(USE_BACKTRACE) */
8153     abort();
8154   }
8155 (void)memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
8156 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
8157 if (!sim_stabr.comp)
8158   {
8159     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8160                    __func__, __FILE__, __LINE__);
8161 #if defined(USE_BACKTRACE)
8162 # if defined(SIGUSR2)
8163     (void)raise(SIGUSR2);
8164     /*NOTREACHED*/ /* unreachable */
8165 # endif /* if defined(SIGUSR2) */
8166 #endif /* if defined(USE_BACKTRACE) */
8167     abort();
8168   }
8169 (void)memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
8170 sim_staba.count = sim_emax;
8171 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
8172 if (!sim_staba.mask)
8173   {
8174     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8175                    __func__, __FILE__, __LINE__);
8176 #if defined(USE_BACKTRACE)
8177 # if defined(SIGUSR2)
8178     (void)raise(SIGUSR2);
8179     /*NOTREACHED*/ /* unreachable */
8180 # endif /* if defined(SIGUSR2) */
8181 #endif /* if defined(USE_BACKTRACE) */
8182     abort();
8183   }
8184 (void)memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
8185 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
8186 if (!sim_staba.comp)
8187   {
8188     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8189                    __func__, __FILE__, __LINE__);
8190 #if defined(USE_BACKTRACE)
8191 # if defined(SIGUSR2)
8192     (void)raise(SIGUSR2);
8193     /*NOTREACHED*/ /* unreachable */
8194 # endif /* if defined(SIGUSR2) */
8195 #endif /* if defined(USE_BACKTRACE) */
8196     abort();
8197   }
8198 (void)memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
8199 if (! sim_dflt_dev)
8200   return NULL;
8201 sim_dfdev = sim_dflt_dev;
8202 sim_dfunit = sim_dfdev->units;
8203 sim_opt_out = 0;                                        /* no options yet */
8204 *st = SCPE_OK;
8205 while (*cptr) {                                         /* loop through modifiers */
8206     svptr = cptr;                                       /* save current position */
8207     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
8208         if (sim_ofile) {                                /* already got one? */
8209             fclose (sim_ofile);                         /* one per customer */
8210             *st = SCPE_ARG;
8211             return NULL;
8212             }
8213         cptr = get_glyph (cptr + 1, gbuf, 0);
8214         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
8215         if (sim_ofile == NULL) {                        /* open failed? */
8216             *st = SCPE_OPENERR;
8217             return NULL;
8218             }
8219         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
8220         continue;
8221         }
8222     cptr = get_glyph (cptr, gbuf, 0);
8223     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
8224         if (t < 0) {                                    /* err if bad switch */
8225             *st = SCPE_INVSW;
8226             return NULL;
8227             }
8228         sim_switches = sim_switches | t;                /* or in new switches */
8229         }
8230     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
8231         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
8232         sim_schrptr = &sim_stabr;                       /* set search */
8233         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
8234         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
8235         }
8236     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
8237         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
8238         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
8239         (tuptr != NULL)) {
8240         sim_dfdev = tdptr;                              /* set as default */
8241         sim_dfunit = tuptr;
8242         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
8243         }
8244     else return svptr;                                  /* not rec, break out */
8245     }
8246 return cptr;
8247 }
8248 
8249 /* put_switches         put switches into string
8250 
8251    Inputs:
8252         buf     =       pointer to string buffer
8253         bufsize =       size of string buffer
8254         sw      =       switch bit mask
8255    Outputs:
8256         buf     =       buffer with switches converted to text
8257 */
8258 
8259 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8260 {
8261 char *optr = buf;
8262 int32 bit;
8263 
8264 (void)memset (buf, 0, bufsize);
8265 if ((sw == 0) || (bufsize < 3))
8266     return buf;
8267 --bufsize;                          /* leave room for terminating NUL */
8268 *optr++ = '-';
8269 for (bit=0; bit <= ('Z'-'A'); bit++)
8270     if (sw & (1 << bit))
8271         if ((size_t)(optr - buf) < bufsize)
8272             *optr++ = 'A' + bit;
8273 return buf;
8274 }
8275 
8276 /* Get register search specification
8277 
8278    Inputs:
8279         cptr    =       pointer to input string
8280         radix   =       radix for numbers
8281         schptr =        pointer to search table
8282    Outputs:
8283         return =        NULL if error
8284                         schptr if valid search specification
8285 */
8286 
8287 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8288 {
8289 int32 c;
8290 size_t logop, cmpop;
8291 t_value logval, cmpval;
8292 const char *sptr;
8293 CONST char *tptr;
8294 const char logstr[] = "|&^", cmpstr[] = "=!><";
8295 /* Using a const instead of a #define. */
8296 const size_t invalid_op = (size_t) -1;
8297 
8298 logval = cmpval = 0;
8299 if (*cptr == 0)                                         /* check for clause */
8300     return NULL;
8301 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8302 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8303     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
8304         logop = sptr - logstr;
8305         logval = strtotv (cptr, &tptr, radix);
8306         if (cptr == tptr)
8307             return NULL;
8308         cptr = tptr;
8309         }
8310     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
8311         cmpop = sptr - cmpstr;
8312         if (*cptr == '=') {
8313             cmpop = cmpop + strlen (cmpstr);
8314             cptr++;
8315             }
8316         cmpval = strtotv (cptr, &tptr, radix);
8317         if (cptr == tptr)
8318             return NULL;
8319         cptr = tptr;
8320         }
8321     else return NULL;
8322     }                                                   /* end for */
8323 if (schptr->count != 1) {
8324     FREE (schptr->mask);
8325     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8326     FREE (schptr->comp);
8327     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8328     }
8329 if (logop != invalid_op) {
8330     schptr->logic = logop;
8331     schptr->mask[0] = logval;
8332     }
8333 if (cmpop != invalid_op) {
8334     schptr->boolop = cmpop;
8335     schptr->comp[0] = cmpval;
8336     }
8337 schptr->count = 1;
8338 return schptr;
8339 }
8340 
8341 /* Get memory search specification
8342 
8343    Inputs:
8344         cptr    =       pointer to input string
8345         radix   =       radix for numbers
8346         schptr =        pointer to search table
8347    Outputs:
8348         return =        NULL if error
8349                         schptr if valid search specification
8350 */
8351 
8352 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8353 {
8354 int32 c;
8355 size_t logop, cmpop;
8356 t_value *logval, *cmpval;
8357 t_stat reason = SCPE_OK;
8358 CONST char *ocptr = cptr;
8359 const char *sptr;
8360 char gbuf[CBUFSIZE];
8361 const char logstr[] = "|&^", cmpstr[] = "=!><";
8362 /* Using a const instead of a #define. */
8363 const size_t invalid_op = (size_t) -1;
8364 
8365 if (*cptr == 0)                                         /* check for clause */
8366     return NULL;
8367 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
8368 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
8369 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8370 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8371     if (NULL != (sptr = strchr (logstr, c))) {          /* check for mask */
8372         logop = sptr - logstr;
8373         cptr = get_glyph (cptr, gbuf, 0);
8374         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
8375         if (reason > 0) {
8376             FREE (logval);
8377             FREE (cmpval);
8378             return get_rsearch (ocptr, radix, schptr);
8379             }
8380         }
8381     else if (NULL != (sptr = strchr (cmpstr, c))) {     /* check for boolop */
8382         cmpop = sptr - cmpstr;
8383         if (*cptr == '=') {
8384             cmpop = cmpop + strlen (cmpstr);
8385             cptr++;
8386             }
8387         cptr = get_glyph (cptr, gbuf, 0);
8388         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
8389         if (reason > 0) {
8390             FREE (logval);
8391             FREE (cmpval);
8392             return get_rsearch (ocptr, radix, schptr);
8393             }
8394         }
8395     else {
8396         FREE (logval);
8397         FREE (cmpval);
8398         return NULL;
8399         }
8400     }                                                   /* end for */
8401 if (schptr->count != (uint32)(1 - reason)) {
8402     schptr->count = 1 - reason;
8403     FREE (schptr->mask);
8404     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8405     FREE (schptr->comp);
8406     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8407     }
8408 if (logop != invalid_op) {
8409     schptr->logic = logop;
8410     FREE (schptr->mask);
8411     schptr->mask = logval;
8412     }
8413 else {
8414     FREE (logval);
8415     }
8416 if (cmpop != invalid_op) {
8417     schptr->boolop = cmpop;
8418     FREE (schptr->comp);
8419     schptr->comp = cmpval;
8420     }
8421 else {
8422     FREE (cmpval);
8423     }
8424 return schptr;
8425 }
8426 
8427 /* Test value against search specification
8428 
8429    Inputs:
8430         val    =        value list to test
8431         schptr =        pointer to search table
8432    Outputs:
8433         return =        1 if value passes search criteria, 0 if not
8434 */
8435 
8436 int32 test_search (t_value *values, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8437 {
8438 t_value *val = NULL;
8439 int32 i, updown;
8440 int32 ret = 0;
8441 
8442 if (schptr == NULL)
8443     return ret;
8444 
8445 val = (t_value *)malloc (schptr->count * sizeof (*values));
8446 if (!val)
8447   {
8448     (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8449                   __func__, __FILE__, __LINE__);
8450 #if defined(USE_BACKTRACE)
8451 # if defined(SIGUSR2)
8452     (void)raise(SIGUSR2);
8453     /*NOTREACHED*/ /* unreachable */
8454 # endif /* if defined(SIGUSR2) */
8455 #endif /* if defined(USE_BACKTRACE) */
8456     abort();
8457   }
8458 for (i=0; i<(int32)schptr->count; i++) {
8459     val[i] = values[i];
8460     switch (schptr->logic) {                            /* case on logical */
8461 
8462         case SCH_OR:
8463             val[i] = val[i] | schptr->mask[i];
8464             break;
8465 
8466         case SCH_AND:
8467             val[i] = val[i] & schptr->mask[i];
8468             break;
8469 
8470         case SCH_XOR:
8471             val[i] = val[i] ^ schptr->mask[i];
8472             break;
8473             }
8474     }
8475 
8476 ret = 1;
8477 if (1) {    /* Little Endian VM */
8478     updown = -1;
8479     i=schptr->count-1;
8480     }
8481 else {      /* Big Endian VM */
8482     updown = 1;
8483     i=0;
8484     }
8485 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
8486     switch (schptr->boolop) {                           /* case on comparison */
8487 
8488         case SCH_E: case SCH_EE:
8489             if (val[i] != schptr->comp[i])
8490                 ret = 0;
8491             break;
8492 
8493         case SCH_N: case SCH_NE:
8494             if (val[i] == schptr->comp[i])
8495                 ret = 0;
8496             break;
8497 
8498         case SCH_G:
8499             if (val[i] <= schptr->comp[i])
8500                 ret = 0;
8501             break;
8502 
8503         case SCH_GE:
8504             if (val[i] < schptr->comp[i])
8505                 ret = 0;
8506             break;
8507 
8508         case SCH_L:
8509             if (val[i] >= schptr->comp[i])
8510                 ret = 0;
8511             break;
8512 
8513         case SCH_LE:
8514             if (val[i] > schptr->comp[i])
8515                 ret = 0;
8516             break;
8517         }
8518     }
8519 FREE (val);
8520 return ret;
8521 }
8522 
8523 /* Radix independent input/output package
8524 
8525    strtotv - general radix input routine
8526 
8527    Inputs:
8528         inptr   =       string to convert
8529         endptr  =       pointer to first unconverted character
8530         radix   =       radix for input
8531    Outputs:
8532         value   =       converted value
8533 
8534    On an error, the endptr will equal the inptr.
8535 */
8536 
8537 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
     /* [previous][next][first][last][top][bottom][index][help] */
8538 {
8539 int32 nodigit;
8540 t_value val;
8541 uint32 c, digit;
8542 
8543 *endptr = inptr;                                        /* assume fails */
8544 if ((radix < 2) || (radix > 36))
8545     return 0;
8546 while (sim_isspace (*inptr))                            /* bypass white space */
8547     inptr++;
8548 val = 0;
8549 nodigit = 1;
8550 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
8551     if (sim_islower (c))
8552         c = toupper (c);
8553     if (sim_isdigit (c))                                /* digit? */
8554         digit = c - (uint32) '0';
8555     else if (radix <= 10)                               /* stop if not expected */
8556         break;
8557     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
8558     if (digit >= radix)                                 /* valid in radix? */
8559         return 0;
8560     val = (val * radix) + digit;                        /* add to value */
8561     nodigit = 0;
8562     }
8563 if (nodigit)                                            /* no digits? */
8564     return 0;
8565 *endptr = inptr;                                        /* result pointer */
8566 return val;
8567 }
8568 
8569 /* fprint_val - general radix printing routine
8570 
8571    Inputs:
8572         stream  =       stream designator
8573         val     =       value to print
8574         radix   =       radix to print
8575         width   =       width to print
8576         format  =       leading zeroes format
8577    Outputs:
8578         status  =       error status
8579         if stream is NULL, returns length of output that would
8580         have been generated.
8581 */
8582 
8583 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8584                    size_t width, uint32 format)
8585 {
8586 #define MAX_WIDTH ((CHAR_BIT * sizeof (t_value) * 4 + 3) / 3)
8587 t_value owtest, wtest;
8588 size_t d;
8589 size_t digit;
8590 size_t ndigits;
8591 size_t commas = 0;
8592 char dbuf[MAX_WIDTH + 1];
8593 
8594 for (d = 0; d < MAX_WIDTH; d++)
8595     dbuf[d] = (format == PV_RZRO)? '0': ' ';
8596 dbuf[MAX_WIDTH] = 0;
8597 d = MAX_WIDTH;
8598 do {
8599     d = d - 1;
8600     digit = val % radix;
8601     val = val / radix;
8602     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8603     } while ((d > 0) && (val != 0));
8604 
8605 switch (format) {
8606     case PV_LEFT:
8607         break;
8608     case PV_RZRO:
8609         wtest = owtest = radix;
8610         ndigits = 1;
8611         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8612             owtest = wtest;
8613             wtest = wtest * radix;
8614             ndigits = ndigits + 1;
8615             }
8616         if ((MAX_WIDTH - (ndigits + commas)) < d)
8617             d = MAX_WIDTH - (ndigits + commas);
8618         break;
8619     }
8620 if (NULL == buffer)                   /* Potentially unsafe wraparound if */
8621     return ((t_stat) strlen(dbuf+d)); /*  sizeof(t_stat) < sizeof(size_t) */
8622 *buffer = '\0';
8623 if (width < strlen(dbuf+d))
8624     return SCPE_IOERR;
8625 strcpy(buffer, dbuf+d);
8626 return SCPE_OK;
8627 }
8628 
8629 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8630     uint32 width, uint32 format)
8631 {
8632 char dbuf[MAX_WIDTH + 1];
8633 
8634 if (!stream)
8635     return sprint_val (NULL, val, radix, width, format);
8636 if (width > MAX_WIDTH)
8637     width = MAX_WIDTH;
8638 sprint_val (dbuf, val, radix, width, format);
8639 if (Fprintf (stream, "%s", dbuf) < 0)
8640     return SCPE_IOERR;
8641 return SCPE_OK;
8642 }
8643 
8644 const char *sim_fmt_secs (double seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
8645 {
8646 static char buf[60];
8647 char frac[16] = "";
8648 const char *sign = "";
8649 double val = seconds;
8650 double days, hours, mins, secs, msecs, usecs;
8651 
8652 if (val == 0.0)
8653     return "";
8654 if (val < 0.0) {
8655     sign = "-";
8656     val = -val;
8657     }
8658 days = floor (val / (24.0*60.0*60.0));
8659 val -= (days * 24.0*60.0*60.0);
8660 hours = floor (val / (60.0*60.0));
8661 val -= (hours * 60.0 * 60.0);
8662 mins = floor (val / 60.0);
8663 val -= (mins * 60.0);
8664 secs = floor (val);
8665 val -= secs;
8666 val *= 1000.0;
8667 msecs = floor (val);
8668 val -= msecs;
8669 val *= 1000.0;
8670 usecs = floor (val+0.5);
8671 if (usecs == 1000.0) {
8672     usecs = 0.0;
8673     msecs += 1;
8674     }
8675 if ((msecs > 0.0) || (usecs > 0.0)) {
8676     (void)sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8677     while (frac[strlen (frac) - 1] == '0') //-V557
8678         frac[strlen (frac) - 1] = '\0'; //-V557
8679     if (strlen (frac) == 1)
8680         frac[0] = '\0';
8681     }
8682 if (days > 0)
8683     (void)sprintf (buf, "%s%.0f day%s %02.0f:%02.0f:%02.0f%s hour%s",
8684                    sign, days, (days != 1) ? "s" : "", hours, mins,
8685                    secs, frac, (days == 1) ? "s" : "");
8686 else
8687     if (hours > 0)
8688         (void)sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour",
8689                        sign, hours, mins, secs, frac);
8690     else
8691         if (mins > 0)
8692             (void)sprintf (buf, "%s%.0f:%02.0f%s minute",
8693                            sign, mins, secs, frac);
8694         else
8695             if (secs > 0)
8696                 (void)sprintf (buf, "%s%.0f%s second",
8697                                sign, secs, frac);
8698             else
8699                 if (msecs > 0) {
8700                     if (usecs > 0)
8701                         (void)sprintf (buf, "%s%.0f.%s msec",
8702                                        sign, msecs, frac+4);
8703                     else
8704                         (void)sprintf (buf, "%s%.0f msec",
8705                                        sign, msecs);
8706                     }
8707                 else
8708                     (void)sprintf (buf, "%s%.0f usec",
8709                                    sign, usecs);
8710 if (0 != strncmp ("1 ", buf, 2))
8711     strcpy (&buf[strlen (buf)], "s");
8712 return buf;
8713 }
8714 
8715 /*
8716  * Event queue package
8717  *
8718  *      sim_activate            add entry to event queue
8719  *      sim_activate_abs        add entry to event queue even if event already scheduled
8720  *      sim_activate_after      add entry to event queue after a specified amount of wall time
8721  *      sim_cancel              remove entry from event queue
8722  *      sim_process_event       process entries on event queue
8723  *      sim_is_active           see if entry is on event queue
8724  *      sim_activate_time       return time until activation
8725  *      sim_atime               return absolute time for an entry
8726  *      sim_gtime               return global time
8727  *      sim_qcount              return event queue entry count
8728  */
8729 
8730 t_stat sim_process_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8731 {
8732 UNIT *uptr;
8733 t_stat reason;
8734 #if defined(TESTING)
8735 cpu_state_t * cpup = _cpup;
8736 #endif
8737 
8738 if (stop_cpu)                                           /* stop CPU? */
8739   {
8740 #if defined(WIN_STDIO)
8741     (void)fflush(stdout);
8742     (void)fflush(stderr);
8743 #endif /* if defined(WIN_STDIO) */
8744     return SCPE_STOP;
8745   }
8746 UPDATE_SIM_TIME;                                        /* update sim time */
8747 
8748 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8749     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8750     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8751                "Queue Empty New Interval = %d\n",
8752                sim_interval);
8753     return SCPE_OK;
8754     }
8755 sim_processing_event = TRUE;
8756 do {
8757     uptr = sim_clock_queue;                             /* get first */
8758     sim_clock_queue = uptr->next;                       /* remove first */
8759     uptr->next = NULL;                                  /* hygiene */
8760     sim_interval -= uptr->time;
8761     uptr->time = 0;
8762     if (sim_clock_queue != QUEUE_LIST_END)
8763         sim_interval = sim_clock_queue->time;
8764     else
8765         sim_interval = noqueue_time = NOQUEUE_WAIT;
8766     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8767                "Processing Event for %s\n", sim_uname (uptr));
8768     if (uptr->action != NULL)
8769         reason = uptr->action (uptr);
8770     else
8771         reason = SCPE_OK;
8772     } while ((reason == SCPE_OK) &&
8773              (sim_interval <= 0) &&
8774              (sim_clock_queue != QUEUE_LIST_END) &&
8775              (!stop_cpu));
8776 
8777 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8778     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8779     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8780                "Processing Queue Complete New Interval = %d\n",
8781                sim_interval);
8782     }
8783 else
8784     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8785                "Processing Queue Complete New Interval = %d(%s)\n",
8786                sim_interval, sim_uname(sim_clock_queue));
8787 
8788 if ((reason == SCPE_OK) && stop_cpu)
8789   {
8790 #if defined(WIN_STDIO)
8791     (void)fflush(stdout);
8792     (void)fflush(stderr);
8793 #endif /* if defined(WIN_STDIO) */
8794     reason = SCPE_STOP;
8795   }
8796 sim_processing_event = FALSE;
8797 return reason;
8798 }
8799 
8800 /* sim_activate - activate (queue) event
8801 
8802    Inputs:
8803         uptr    =       pointer to unit
8804         event_time =    relative timeout
8805    Outputs:
8806         reason  =       result (SCPE_OK if ok)
8807 */
8808 
8809 t_stat sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8810 {
8811 if (uptr->dynflags & UNIT_TMR_UNIT)
8812     return sim_timer_activate (uptr, event_time);
8813 return _sim_activate (uptr, event_time);
8814 }
8815 
8816 t_stat _sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8817 {
8818 UNIT *cptr, *prvptr;
8819 int32 accum;
8820 #if defined(TESTING)
8821 cpu_state_t * cpup = _cpup;
8822 #endif
8823 
8824 if (sim_is_active (uptr))                               /* already active? */
8825     return SCPE_OK;
8826 UPDATE_SIM_TIME;                                        /* update sim time */
8827 
8828 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8829 
8830 prvptr = NULL;
8831 accum = 0;
8832 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8833     if (event_time < (accum + cptr->time))
8834         break;
8835     accum = accum + cptr->time;
8836     prvptr = cptr;
8837     }
8838 if (prvptr == NULL) {                                   /* insert at head */
8839     cptr = uptr->next = sim_clock_queue;
8840     sim_clock_queue = uptr;
8841     }
8842 else {
8843     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
8844     prvptr->next = uptr;
8845     }
8846 uptr->time = event_time - accum;
8847 if (cptr != QUEUE_LIST_END)
8848     cptr->time = cptr->time - uptr->time;
8849 sim_interval = sim_clock_queue->time;
8850 return SCPE_OK;
8851 }
8852 
8853 /* sim_activate_abs - activate (queue) event even if event already scheduled
8854 
8855    Inputs:
8856         uptr    =       pointer to unit
8857         event_time =    relative timeout
8858    Outputs:
8859         reason  =       result (SCPE_OK if ok)
8860 */
8861 
8862 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8863 {
8864 sim_cancel (uptr);
8865 return _sim_activate (uptr, event_time);
8866 }
8867 
8868 /* sim_activate_after - activate (queue) event
8869 
8870    Inputs:
8871         uptr    =       pointer to unit
8872         usec_delay =    relative timeout (in microseconds)
8873    Outputs:
8874         reason  =       result (SCPE_OK if ok)
8875 */
8876 
8877 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8878 {
8879 return _sim_activate_after (uptr, usec_delay);
8880 }
8881 
8882 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8883 {
8884 if (sim_is_active (uptr))                               /* already active? */
8885     return SCPE_OK;
8886 return sim_timer_activate_after (uptr, usec_delay);
8887 }
8888 
8889 /* sim_cancel - cancel (dequeue) event
8890 
8891    Inputs:
8892         uptr    =       pointer to unit
8893    Outputs:
8894         reason  =       result (SCPE_OK if ok)
8895 
8896 */
8897 
8898 t_stat sim_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8899 {
8900 UNIT *cptr, *nptr;
8901 #if defined(TESTING)
8902 cpu_state_t * cpup = _cpup;
8903 #endif
8904 
8905 if (sim_clock_queue == QUEUE_LIST_END)
8906     return SCPE_OK;
8907 if (!sim_is_active (uptr))
8908     return SCPE_OK;
8909 UPDATE_SIM_TIME;                                        /* update sim time */
8910 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8911 nptr = QUEUE_LIST_END;
8912 
8913 if (sim_clock_queue == uptr) {
8914     nptr = sim_clock_queue = uptr->next;
8915     uptr->next = NULL;                                  /* hygiene */
8916     }
8917 else {
8918     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8919         if (cptr->next == uptr) {
8920             nptr = cptr->next = uptr->next;
8921             uptr->next = NULL;                          /* hygiene */
8922             break;                                      /* end queue scan */
8923             }
8924         }
8925     }
8926 if (nptr != QUEUE_LIST_END)
8927     nptr->time += (uptr->next) ? 0 : uptr->time;
8928 if (!uptr->next)
8929     uptr->time = 0;
8930 if (sim_clock_queue != QUEUE_LIST_END)
8931     sim_interval = sim_clock_queue->time;
8932 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8933 if (uptr->next) {
8934     sim_printf ("\rCancel failed for '%s'!\r\n", sim_uname(uptr));
8935     if (sim_deb)
8936         fclose(sim_deb);
8937     (void)fprintf (stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
8938                    __func__, __FILE__, __LINE__);
8939     abort ();
8940     }
8941 return SCPE_OK;
8942 }
8943 
8944 /* sim_is_active - test for entry in queue
8945 
8946    Inputs:
8947         uptr    =       pointer to unit
8948    Outputs:
8949         result =        TRUE if unit is busy, FALSE inactive
8950 */
8951 
8952 t_bool sim_is_active (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8953 {
8954 if (uptr->next == NULL)
8955   return FALSE;
8956 else
8957 return TRUE;
8958 }
8959 
8960 /* sim_activate_time - return activation time
8961 
8962    Inputs:
8963         uptr    =       pointer to unit
8964    Outputs:
8965         result =        absolute activation time + 1, 0 if inactive
8966 */
8967 
8968 int32 sim_activate_time (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8969 {
8970 UNIT *cptr;
8971 int32 accum = 0;
8972 
8973 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8974     if (cptr == sim_clock_queue) {
8975         if (sim_interval > 0)
8976             accum = accum + sim_interval;
8977         }
8978     else
8979         accum = accum + cptr->time;
8980     if (cptr == uptr)
8981         return accum + 1;
8982     }
8983 return 0;
8984 }
8985 
8986 /* sim_gtime - return global time
8987 
8988    Inputs: none
8989    Outputs:
8990         time    =       global time
8991 */
8992 
8993 double sim_gtime (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8994 {
8995 return sim_time;
8996 }
8997 
8998 /* sim_qcount - return queue entry count
8999 
9000    Inputs: none
9001    Outputs:
9002         count   =       number of entries on the queue
9003 */
9004 
9005 int32 sim_qcount (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9006 {
9007 int32 cnt;
9008 UNIT *uptr;
9009 
9010 cnt = 0;
9011 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
9012     cnt++;
9013 return cnt;
9014 }
9015 
9016 /* Breakpoint package.  This module replaces the VM-implemented one
9017    instruction breakpoint capability.
9018 
9019    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
9020    efficient binary searching.  A breakpoint consists of a six entry structure:
9021 
9022         addr                    address of the breakpoint
9023         type                    types of breakpoints set on the address
9024                                 a bit mask representing letters A-Z
9025         cnt                     number of iterations before breakp is taken
9026         action                  pointer command string to be executed
9027                                 when break is taken
9028         next                    list of other breakpoints with the same addr specifier
9029         time_fired              array of when this breakpoint was fired for each class
9030 
9031    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
9032    is the bitwise OR of all the type fields).  A simulator need only check for
9033    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
9034 
9035    The package contains the following public routines:
9036 
9037         sim_brk_init            initialize
9038         sim_brk_set             set breakpoint
9039         sim_brk_clr             clear breakpoint
9040         sim_brk_clrall          clear all breakpoints
9041         sim_brk_show            show breakpoint
9042         sim_brk_showall         show all breakpoints
9043         sim_brk_test            test for breakpoint
9044         sim_brk_npc             PC has been changed
9045         sim_brk_getact          get next action
9046         sim_brk_clract          clear pending actions
9047 
9048    Initialize breakpoint system.
9049 */
9050 
9051 t_stat sim_brk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9052 {
9053 int32 i;
9054 
9055 for (i=0; i<sim_brk_lnt; i++) {
9056     BRKTAB *bp;
9057     if (sim_brk_tab)
9058       {
9059         bp = sim_brk_tab[i];
9060 
9061         while (bp)
9062           {
9063             BRKTAB *bpt = bp->next;
9064 
9065             FREE (bp->act);
9066             FREE (bp);
9067             bp = bpt;
9068           }
9069       }
9070 }
9071 if (sim_brk_tab != NULL)
9072     (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9073 sim_brk_lnt = SIM_BRK_INILNT;
9074 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
9075 if (sim_brk_tab == NULL)
9076     return SCPE_MEM;
9077 (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9078 sim_brk_ent = sim_brk_ins = 0;
9079 sim_brk_clract ();
9080 sim_brk_npc (0);
9081 return SCPE_OK;
9082 }
9083 
9084 /* Search for a breakpoint in the sorted breakpoint table */
9085 
9086 BRKTAB *sim_brk_fnd (t_addr loc)
     /* [previous][next][first][last][top][bottom][index][help] */
9087 {
9088 int32 lo, hi, p;
9089 BRKTAB *bp;
9090 
9091 if (sim_brk_ent == 0) {                                 /* table empty? */
9092     sim_brk_ins = 0;                                    /* insrt at head */
9093     return NULL;                                        /* sch fails */
9094     }
9095 lo = 0;                                                 /* initial bounds */
9096 hi = sim_brk_ent - 1;
9097 do {
9098     p = (lo + hi) >> 1;                                 /* probe */
9099     bp = sim_brk_tab[p];                                /* table addr */
9100     if (loc == bp->addr) {                              /* match? */
9101         sim_brk_ins = p;
9102         return bp;
9103         }
9104     else if (loc < bp->addr)                            /* go down? p is upper */
9105         hi = p - 1;
9106     else lo = p + 1;                                    /* go up? p is lower */
9107     } while (lo <= hi);
9108 if (loc < bp->addr)                                     /* insrt before or */
9109     sim_brk_ins = p;
9110 else sim_brk_ins = p + 1;                               /* after last sch */
9111 return NULL;
9112 }
9113 
9114 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
     /* [previous][next][first][last][top][bottom][index][help] */
9115 {
9116 BRKTAB *bp = sim_brk_fnd (loc);
9117 
9118 while (bp) {
9119     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
9120                   (bp->typ == btyp))
9121         return bp;
9122     bp = bp->next;
9123     }
9124 return bp;
9125 }
9126 
9127 /* Insert a breakpoint */
9128 
9129 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9130 {
9131 int32 i, t;
9132 BRKTAB *bp, **newp;
9133 
9134 if (sim_brk_ins < 0)
9135     return NULL;
9136 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
9137     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
9138     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
9139     if (newp == NULL)                                   /* can't extend */
9140         return NULL;
9141     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
9142     (void)memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
9143     FREE (sim_brk_tab);                                 /* free old table */
9144     sim_brk_tab = newp;                                 /* new base, lnt */
9145     sim_brk_lnt = t;
9146     }
9147 if ((sim_brk_ins == sim_brk_ent) ||
9148     ((sim_brk_ins != sim_brk_ent) && //-V728
9149      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
9150     for (i = sim_brk_ent; i > sim_brk_ins; --i)
9151         sim_brk_tab[i] = sim_brk_tab[i - 1];
9152     sim_brk_tab[sim_brk_ins] = NULL;
9153     }
9154 bp = (BRKTAB *)calloc (1, sizeof (*bp));
9155 if (!bp)
9156   {
9157     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9158                    __func__, __FILE__, __LINE__);
9159 #if defined(USE_BACKTRACE)
9160 # if defined(SIGUSR2)
9161     (void)raise(SIGUSR2);
9162     /*NOTREACHED*/ /* unreachable */
9163 # endif /* if defined(SIGUSR2) */
9164 #endif /* if defined(USE_BACKTRACE) */
9165     abort();
9166   }
9167 bp->next = sim_brk_tab[sim_brk_ins];
9168 sim_brk_tab[sim_brk_ins] = bp;
9169 if (bp->next == NULL)
9170     sim_brk_ent += 1;
9171 bp->addr = loc;
9172 bp->typ = btyp;
9173 bp->cnt = 0;
9174 bp->act = NULL;
9175 for (i = 0; i < SIM_BKPT_N_SPC; i++)
9176     bp->time_fired[i] = -1.0;
9177 return bp;
9178 }
9179 
9180 /* Set a breakpoint of type sw */
9181 
9182 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
9183 {
9184 BRKTAB *bp;
9185 
9186 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
9187     sw |= sim_brk_dflt;
9188 if (~sim_brk_types & sw) {
9189     char gbuf[CBUFSIZE];
9190 
9191     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
9192     }
9193 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
9194     return SCPE_ARG;
9195 bp = sim_brk_fnd (loc);                                 /* loc present? */
9196 if (!bp)                                                /* no, allocate */
9197     bp = sim_brk_new (loc, sw);
9198 else {
9199     while (bp && (bp->typ != (uint32)sw))
9200         bp = bp->next;
9201     if (!bp)
9202         bp = sim_brk_new (loc, sw);
9203     }
9204 if (!bp)                                                /* still no? mem err */
9205     return SCPE_MEM;
9206 bp->cnt = ncnt;                                         /* set count */
9207 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
9208     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
9209     FREE (bp->act);                                     /* deallocate */
9210     bp->act = NULL;                                     /* now no action */
9211     }
9212 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9213     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
9214     if (newp == NULL)                                   /* mem err? */
9215         return SCPE_MEM;
9216     strncpy (newp, act, CBUFSIZE);                      /* copy action */
9217     bp->act = newp;                                     /* set pointer */
9218     }
9219 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
9220 return SCPE_OK;
9221 }
9222 
9223 /* Clear a breakpoint */
9224 
9225 t_stat sim_brk_clr (t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9226 {
9227 BRKTAB *bpl = NULL;
9228 BRKTAB *bp = sim_brk_fnd (loc);
9229 int32 i;
9230 
9231 if (!bp)                                                /* not there? ok */
9232     return SCPE_OK;
9233 if (sw == 0)
9234     sw = SIM_BRK_ALLTYP;
9235 
9236 #if !defined(__clang_analyzer__)
9237 while (bp) {
9238     if (bp->typ == (bp->typ & sw)) {
9239         FREE (bp->act);                                 /* deallocate action */
9240         if (bp == sim_brk_tab[sim_brk_ins])
9241             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
9242         else
9243           {
9244             if (bpl)
9245               bpl->next = bp->next;
9246           }
9247         FREE (bp);
9248         bp = bpl;
9249         }
9250     else {
9251         bpl = bp;
9252         bp = bp->next;
9253         }
9254     }
9255 #endif /* if !defined(__clang_analyzer__) */
9256 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
9257     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
9258     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
9259         sim_brk_tab[i] = sim_brk_tab[i+1];
9260     }
9261 sim_brk_summ = 0;                                       /* recalc summary */
9262 for (i = 0; i < sim_brk_ent; i++) {
9263     bp = sim_brk_tab[i];
9264     while (bp) {
9265         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
9266         bp = bp->next;
9267         }
9268     }
9269 return SCPE_OK;
9270 }
9271 
9272 /* Clear all breakpoints */
9273 
9274 t_stat sim_brk_clrall (int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9275 {
9276 int32 i;
9277 
9278 if (sw == 0)
9279     sw = SIM_BRK_ALLTYP;
9280 for (i = 0; i < sim_brk_ent;) {
9281     t_addr loc = sim_brk_tab[i]->addr;
9282     sim_brk_clr (loc, sw);
9283     if ((i < sim_brk_ent) &&
9284         (loc == sim_brk_tab[i]->addr))
9285         ++i;
9286     }
9287 return SCPE_OK;
9288 }
9289 
9290 /* Show a breakpoint */
9291 
9292 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9293 {
9294 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
9295 DEVICE *dptr;
9296 uint32 i, any;
9297 
9298 if ((sw == 0) || (sw == SWMASK ('C'))) {
9299     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0); }
9300 if (!bp || (!(bp->typ & sw))) {
9301     return SCPE_OK; }
9302 dptr = sim_dflt_dev;
9303 if (dptr == NULL) {
9304     return SCPE_OK; }
9305 if (sw & SWMASK ('C')) {
9306     if (st != NULL) {
9307         (void)fprintf (st, "SET BREAK "); }
9308 } else {
9309     if (sim_vm_fprint_addr) {
9310         sim_vm_fprint_addr
9311             (st, dptr, loc);
9312     } else {
9313         fprint_val
9314             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9315     if (st != NULL) {
9316         (void)fprintf (st, ":\t"); }
9317     }
9318 for (i = any = 0; i < 26; i++) {
9319     if ((bp->typ >> i) & 1) {
9320         if ((sw & SWMASK ('C')) == 0) {
9321             if (any) {
9322                 if (st != NULL) {
9323                     (void)fprintf (st, ", "); } }
9324             if (st != NULL) {
9325                 fputc (i + 'A', st); }
9326             }
9327         } else {
9328             if (st != NULL) {
9329                 (void)fprintf (st, "-%c", i + 'A'); }
9330         any = 1;
9331         }
9332     }
9333 if (sw & SWMASK ('C')) {
9334     if (st != NULL) {
9335         (void)fprintf (st, " "); }
9336     if (sim_vm_fprint_addr) {
9337         if (st != NULL) {
9338             sim_vm_fprint_addr (st, dptr, loc); }
9339     } else {
9340         fprint_val
9341             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9342     }
9343 if (bp->cnt > 0) {
9344     if (st != NULL) {
9345         (void)fprintf (st, "[%d]", bp->cnt); } }
9346 if (bp->act != NULL) {
9347     if (st != NULL) {
9348         (void)fprintf (st, "; %s", bp->act); } }
9349 (void)fprintf (st, "\n");
9350 return SCPE_OK;
9351 }
9352 
9353 /* Show all breakpoints */
9354 
9355 t_stat sim_brk_showall (FILE *st, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9356 {
9357 int32 bit, mask, types;
9358 BRKTAB **bpt;
9359 
9360 if ((sw == 0) || (sw == SWMASK ('C')))
9361     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
9362 for (types=bit=0; bit <= ('Z'-'A'); bit++)
9363     if (sim_brk_types & (1 << bit))
9364         ++types;
9365 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
9366     (void)fprintf (st, "Supported Breakpoint Types:");
9367     for (bit=0; bit <= ('Z'-'A'); bit++)
9368         if (sim_brk_types & (1 << bit))
9369             (void)fprintf (st, " -%c", 'A' + bit);
9370     (void)fprintf (st, "\n");
9371     }
9372 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
9373     mask = (sw & sim_brk_types);
9374     (void)fprintf (st, "Displaying Breakpoint Types:");
9375     for (bit=0; bit <= ('Z'-'A'); bit++)
9376         if (mask & (1 << bit))
9377             (void)fprintf (st, " -%c", 'A' + bit);
9378     (void)fprintf (st, "\n");
9379     }
9380 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9381     BRKTAB *prev = NULL;
9382     BRKTAB *cur = *bpt;
9383     BRKTAB *next;
9384     /* First reverse the list */
9385     while (cur) {
9386         next = cur->next;
9387         cur->next = prev;
9388         prev = cur;
9389         cur = next;
9390         }
9391     /* save reversed list in the head pointer so lookups work */
9392     *bpt = prev;
9393     /* Walk the reversed list and print it in the order it was defined in */
9394     cur = prev;
9395     while (cur) {
9396         if (cur->typ & sw)
9397             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
9398         cur = cur->next;
9399         }
9400     /* reversing the list again */
9401     cur = prev;
9402     prev = NULL;
9403     while (cur) {
9404         next = cur->next;
9405         cur->next = prev;
9406         prev = cur;
9407         cur = next;
9408         }
9409     /* restore original list */
9410     *bpt = prev;
9411     }
9412 return SCPE_OK;
9413 }
9414 
9415 /* Test for breakpoint */
9416 
9417 uint32 sim_brk_test (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9418 {
9419 BRKTAB *bp;
9420 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
9421 
9422 if (sim_brk_summ & BRK_TYP_DYN_ALL)
9423     btyp |= BRK_TYP_DYN_ALL;
9424 
9425 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
9426     double s_gtime = sim_gtime ();                      /* get time now */
9427 
9428     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
9429         return 0;
9430     bp->time_fired[spc] = s_gtime;                      /* remember match time */
9431     if (--bp->cnt > 0)                                  /* count > 0? */
9432         return 0;
9433     bp->cnt = 0;                                        /* reset count */
9434     sim_brk_setact (bp->act);                           /* set up actions */
9435     sim_brk_match_type = btyp & bp->typ;                /* set return value */
9436     if (bp->typ & BRK_TYP_TEMP)
9437         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
9438     sim_brk_match_addr = loc;
9439     return sim_brk_match_type;
9440     }
9441 return 0;
9442 }
9443 
9444 /* Get next pending action, if any */
9445 
9446 CONST char *sim_brk_getact (char *buf, int32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
9447 {
9448 char *ep;
9449 size_t lnt;
9450 
9451 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
9452     return NULL;
9453 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
9454     sim_brk_act[sim_do_depth]++;
9455 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
9456     return sim_brk_clract ();
9457     }
9458 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
9459     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
9460     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
9461     buf[lnt] = 0;                                       /* erase ; */
9462     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
9463     }
9464 else {
9465     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
9466     sim_brk_clract ();                                  /* no more */
9467     }
9468 return buf;
9469 }
9470 
9471 /* Clear pending actions */
9472 
9473 char *sim_brk_clract (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9474 {
9475 FREE (sim_brk_act_buf[sim_do_depth]);
9476 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
9477 }
9478 
9479 /* Set up pending actions */
9480 
9481 void sim_brk_setact (const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
9482 {
9483 if (action) {
9484     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
9485     if (!sim_brk_act_buf[sim_do_depth])
9486       {
9487         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9488                        __func__, __FILE__, __LINE__);
9489 #if defined(USE_BACKTRACE)
9490 # if defined(SIGUSR2)
9491         (void)raise(SIGUSR2);
9492         /*NOTREACHED*/ /* unreachable */
9493 # endif /* if defined(SIGUSR2) */
9494 #endif /* if defined(USE_BACKTRACE) */
9495         abort();
9496       }
9497     strcpy (sim_brk_act_buf[sim_do_depth], action);
9498     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
9499     }
9500 else
9501     sim_brk_clract ();
9502 }
9503 
9504 /* New PC */
9505 
9506 void sim_brk_npc (uint32 cnt)
     /* [previous][next][first][last][top][bottom][index][help] */
9507 {
9508 uint32 spc;
9509 BRKTAB **bpt, *bp;
9510 
9511 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
9512     cnt = SIM_BKPT_N_SPC;
9513 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9514     for (bp = *bpt; bp; bp = bp->next) {
9515         for (spc = 0; spc < cnt; spc++)
9516             bp->time_fired[spc] = -1.0;
9517         }
9518     }
9519 }
9520 
9521 /* Clear breakpoint space */
9522 
9523 void sim_brk_clrspc (uint32 spc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9524 {
9525 BRKTAB **bpt, *bp;
9526 
9527 if (spc < SIM_BKPT_N_SPC) {
9528     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9529         for (bp = *bpt; bp; bp = bp->next) {
9530             if (bp->typ & btyp)
9531                 bp->time_fired[spc] = -1.0;
9532             }
9533         }
9534     }
9535 }
9536 
9537 const char *sim_brk_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
9538 {
9539 static char msg[256];
9540 char addr[65] = "";
9541 char buf[32];
9542 
9543 msg[0] = '\0';
9544 if (sim_dflt_dev) {
9545   if (sim_vm_sprint_addr)
9546     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
9547   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
9548 }
9549 if (sim_brk_type_desc) {
9550     BRKTYPTAB *brk = sim_brk_type_desc;
9551 
9552     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
9553         if (brk->btyp == sim_brk_match_type) {
9554             (void)sprintf (msg, "%s: %s", brk->desc, addr);
9555             break;
9556             }
9557         brk++;
9558         }
9559     }
9560 if (!msg[0])
9561     (void)sprintf (msg, "%s Breakpoint at: %s\n",
9562                    put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9563 
9564 return msg;
9565 }
9566 
9567 /* Expect package.  This code provides a mechanism to stop and control simulator
9568    execution based on traffic coming out of simulated ports and as well as a means
9569    to inject data into those ports.  It can conceptually viewed as a string
9570    breakpoint package.
9571 
9572    Expect rules are stored in tables associated with each port which can use this
9573    facility.  An expect rule consists of a five entry structure:
9574 
9575         match                   the expect match string
9576         size                    the number of bytes in the match string
9577         match_pattern           the expect match string in display format
9578         cnt                     number of iterations before match is declared
9579         action                  command string to be executed when match occurs
9580 
9581    All active expect rules are contained in an expect match context structure.
9582 
9583         rules                   the match rules
9584         size                    the count of match rules
9585         buf                     the buffer of output data which has been produced
9586         buf_ins                 the buffer insertion point for the next output data
9587         buf_size                the buffer size
9588 
9589    The package contains the following public routines:
9590 
9591         sim_set_expect          expect command parser and initializer
9592         sim_set_noexpect        noexpect command parser
9593         sim_exp_set             set or add an expect rule
9594         sim_exp_clr             clear or delete an expect rule
9595         sim_exp_clrall          clear all expect rules
9596         sim_exp_show            show an expect rule
9597         sim_exp_showall         show all expect rules
9598         sim_exp_check           test for rule match
9599 */
9600 
9601 /* Set expect */
9602 
9603 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9604 {
9605 char gbuf[CBUFSIZE];
9606 CONST char *tptr;
9607 CONST char *c1ptr;
9608 t_bool after_set = FALSE;
9609 uint32 after;
9610 int32 cnt = 0;
9611 t_stat r;
9612 
9613 if (exp == NULL)
9614     return sim_messagef (SCPE_ARG, "Null exp!\n");
9615 after = exp->after;
9616 
9617 if ((cptr == NULL) || (*cptr == 0))
9618     return SCPE_2FARG;
9619 if (*cptr == '[') {
9620     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9621     if ((cptr == c1ptr) || (*c1ptr != ']'))
9622         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
9623     cptr = c1ptr + 1;
9624     while (sim_isspace(*cptr))
9625         ++cptr;
9626     }
9627 tptr = get_glyph (cptr, gbuf, ',');
9628 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9629     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9630     if (r != SCPE_OK)
9631         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
9632     after_set = TRUE;
9633     cptr = tptr;
9634     }
9635 if ((*cptr != '"') && (*cptr != '\''))
9636     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9637 cptr = get_glyph_quoted (cptr, gbuf, 0);
9638 
9639 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9640 }
9641 
9642 /* Clear expect */
9643 
9644 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9645 {
9646 char gbuf[CBUFSIZE];
9647 
9648 if (NULL == cptr || !*cptr)
9649     return sim_exp_clrall (exp);                    /* clear all rules */
9650 if ((*cptr != '"') && (*cptr != '\''))
9651     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9652 cptr = get_glyph_quoted (cptr, gbuf, 0);
9653 if (*cptr != '\0')
9654     return SCPE_2MARG;                              /* No more arguments */
9655 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9656 }
9657 
9658 /* Search for an expect rule in an expect context */
9659 
9660 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, size_t start_rule)
     /* [previous][next][first][last][top][bottom][index][help] */
9661 {
9662 size_t i;
9663 
9664 if (NULL == exp->rules)
9665     return NULL;
9666 for (i=start_rule; i<exp->size; i++)
9667     if (!strcmp (exp->rules[i].match_pattern, match))
9668         return &exp->rules[i];
9669 return NULL;
9670 }
9671 
9672 /* Clear (delete) an expect rule */
9673 
9674 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9675 {
9676 size_t i;
9677 
9678 if (NULL == ep)                                         /* not there? ok */
9679     return SCPE_OK;
9680 FREE (ep->match);                                       /* deallocate match string */
9681 FREE (ep->match_pattern);                               /* deallocate the display format match string */
9682 FREE (ep->act);                                         /* deallocate action */
9683 exp->size -= 1;                                         /* decrement count */
9684 #if !defined(__clang_analyzer__)
9685 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9686     exp->rules[i] = exp->rules[i+1];
9687 if (exp->size == 0) {                                   /* No rules left? */
9688     FREE (exp->rules);
9689     exp->rules = NULL;
9690     }
9691 #endif /* if !defined(__clang_analyzer__) */
9692 return SCPE_OK;
9693 }
9694 
9695 t_stat sim_exp_clr (EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9696 {
9697 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9698 
9699 while (ep) {
9700     sim_exp_clr_tab (exp, ep);
9701     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9702     }
9703 return SCPE_OK;
9704 }
9705 
9706 /* Clear all expect rules */
9707 
9708 t_stat sim_exp_clrall (EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9709 {
9710 int32 i;
9711 
9712 for (i=0; i<exp->size; i++) {
9713     FREE (exp->rules[i].match);                         /* deallocate match string */
9714     FREE (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9715     FREE (exp->rules[i].act);                           /* deallocate action */
9716     }
9717 FREE (exp->rules);
9718 exp->rules = NULL;
9719 exp->size = 0;
9720 FREE (exp->buf);
9721 exp->buf = NULL;
9722 exp->buf_size = 0;
9723 exp->buf_ins = 0;
9724 return SCPE_OK;
9725 }
9726 
9727 /* Set/Add an expect rule */
9728 
9729 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] */
9730 {
9731 EXPTAB *ep;
9732 uint8 *match_buf;
9733 uint32 match_size;
9734 size_t i;
9735 
9736 /* Validate the match string */
9737 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9738 if (!match_buf)
9739     return SCPE_MEM;
9740 if (switches & EXP_TYP_REGEX) {
9741     FREE (match_buf);
9742     return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9743     }
9744 else {
9745     if (switches & EXP_TYP_REGEX_I) {
9746         FREE (match_buf);
9747         return sim_messagef (SCPE_ARG, "Case independent matching is only valid for RegEx expect rules\n");
9748         }
9749     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9750     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9751         FREE (match_buf);
9752         return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9753         }
9754     }
9755 FREE (match_buf);
9756 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9757     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9758         (exp->rules[i].switches & EXP_TYP_PERSIST))
9759         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9760     }
9761 if (after && exp->size)
9762     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9763 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9764 if (!exp->rules)
9765   {
9766     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9767                    __func__, __FILE__, __LINE__);
9768 #if defined(USE_BACKTRACE)
9769 # if defined(SIGUSR2)
9770     (void)raise(SIGUSR2);
9771     /*NOTREACHED*/ /* unreachable */
9772 # endif /* if defined(SIGUSR2) */
9773 #endif /* if defined(USE_BACKTRACE) */
9774     abort();
9775   }
9776 ep = &exp->rules[exp->size];
9777 exp->size += 1;
9778 exp->after = after;                                     /* set halt after value */
9779 (void)memset (ep, 0, sizeof(*ep));
9780 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9781 if (ep->match_pattern)
9782     strcpy (ep->match_pattern, match);
9783 ep->cnt = cnt;                                          /* set proceed count */
9784 ep->switches = switches;                                /* set switches */
9785 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9786 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9787     sim_exp_clr_tab (exp, ep);                          /* clear it */
9788     FREE (match_buf);                                   /* release allocation */
9789     return SCPE_MEM;
9790     }
9791 if (switches & EXP_TYP_REGEX) {
9792     FREE (match_buf);
9793     match_buf = NULL;
9794     }
9795 else {
9796     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9797     sim_decode_quoted_string (match, match_buf, &match_size);
9798     ep->match = match_buf;
9799     ep->size = match_size;
9800     }
9801 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9802 if (!ep->match_pattern)
9803   {
9804     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9805                    __func__, __FILE__, __LINE__);
9806 #if defined(USE_BACKTRACE)
9807 # if defined(SIGUSR2)
9808     (void)raise(SIGUSR2);
9809     /*NOTREACHED*/ /* unreachable */
9810 # endif /* if defined(SIGUSR2) */
9811 #endif /* if defined(USE_BACKTRACE) */
9812     abort();
9813   }
9814 strcpy (ep->match_pattern, match);
9815 if (ep->act) {                                          /* replace old action? */
9816     FREE (ep->act);                                     /* deallocate */
9817     ep->act = NULL;                                     /* now no action */
9818     }
9819 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
9820 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9821     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9822     if (newp == NULL)                                   /* mem err? */
9823         return SCPE_MEM;
9824     strcpy (newp, act);                                 /* copy action */
9825     ep->act = newp;                                     /* set pointer */
9826     }
9827 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9828 for (i=0; i<exp->size; i++) {
9829     size_t compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9830     if (compare_size >= exp->buf_size) {
9831         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9832         exp->buf_size = compare_size + 1;
9833         }
9834     }
9835 return SCPE_OK;
9836 }
9837 
9838 /* Show an expect rule */
9839 
9840 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9841 {
9842 if (!ep)
9843     return SCPE_OK;
9844 (void)fprintf (st, "EXPECT");
9845 if (ep->switches & EXP_TYP_PERSIST)
9846     (void)fprintf (st, " -p");
9847 if (ep->switches & EXP_TYP_CLEARALL)
9848     (void)fprintf (st, " -c");
9849 if (ep->switches & EXP_TYP_REGEX)
9850     (void)fprintf (st, " -r");
9851 if (ep->switches & EXP_TYP_REGEX_I)
9852     (void)fprintf (st, " -i");
9853 (void)fprintf (st, " %s", ep->match_pattern);
9854 if (ep->cnt > 0)
9855     (void)fprintf (st, " [%d]", ep->cnt);
9856 if (ep->act)
9857     (void)fprintf (st, " %s", ep->act);
9858 (void)fprintf (st, "\n");
9859 return SCPE_OK;
9860 }
9861 
9862 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9863 {
9864 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9865 
9866 if (exp->buf_size) {
9867     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9868 
9869     (void)fprintf (st, "Match Buffer Size: %lld\n",
9870                    (long long)exp->buf_size);
9871     (void)fprintf (st, "Buffer Insert Offset: %lld\n",
9872                    (long long)exp->buf_ins);
9873     (void)fprintf (st, "Buffer Contents: %s\n",
9874                    bstr);
9875     FREE (bstr);
9876     }
9877 if (exp->after)
9878     (void)fprintf (st, "Halt After: %lld instructions\n",
9879                    (long long)exp->after);
9880 if (exp->dptr && exp->dbit)
9881     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\n",
9882                    sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "",
9883                    exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
9884 (void)fprintf (st, "Match Rules:\n");
9885 if (!*match)
9886     return sim_exp_showall (st, exp);
9887 if (!ep) {
9888     (void)fprintf (st, "No Rules match '%s'\n", match);
9889     return SCPE_ARG;
9890     }
9891 do {
9892     sim_exp_show_tab (st, exp, ep);
9893     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9894     } while (ep);
9895 return SCPE_OK;
9896 }
9897 
9898 /* Show all expect rules */
9899 
9900 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9901 {
9902 size_t i;
9903 
9904 for (i=0; i < exp->size; i++)
9905     sim_exp_show_tab (st, exp, &exp->rules[i]);
9906 return SCPE_OK;
9907 }
9908 
9909 /* Test for expect match */
9910 
9911 t_stat sim_exp_check (EXPECT *exp, uint8 data)
     /* [previous][next][first][last][top][bottom][index][help] */
9912 {
9913 size_t i;
9914 EXPTAB *ep = NULL;
9915 char *tstr = NULL;
9916 #if defined(TESTING)
9917 cpu_state_t * cpup = _cpup;
9918 #endif
9919 
9920 if ((!exp) || (!exp->rules))                            /* Anything to check? */
9921     return SCPE_OK;
9922 
9923 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
9924 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
9925 
9926 for (i=0; i < exp->size; i++) {
9927     ep = &exp->rules[i];
9928     if (ep == NULL)
9929         break;
9930     if (ep->switches & EXP_TYP_REGEX) {
9931         }
9932     else {
9933         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
9934             /*
9935              * First compare the newly deposited data at the beginning
9936              * of buffer with the end of the match string
9937              */
9938             if (exp->buf_ins > 0) {
9939                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9940                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9941                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9942 
9943                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%lld]: %s\n",
9944                                (long long)exp->buf_ins, estr);
9945                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9946                     FREE (estr);
9947                     FREE (mstr);
9948                     }
9949                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9950                     continue;
9951                 }
9952             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9953                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9954                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9955 
9956                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\n",
9957                            (long long)exp->buf_size-(ep->size-exp->buf_ins),
9958                            (long long)ep->size-exp->buf_ins, estr);
9959                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9960                 FREE (estr);
9961                 FREE (mstr);
9962                 }
9963             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9964                 continue;
9965             break;
9966             }
9967         else {
9968             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9969                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9970                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9971 
9972                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\n",
9973                            (long long)exp->buf_ins-ep->size,
9974                            (long long)ep->size, estr);
9975                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9976                 FREE (estr);
9977                 FREE (mstr);
9978                 }
9979             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9980                 continue;
9981             break;
9982             }
9983         }
9984     }
9985 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
9986         exp->buf_ins = 0;                               /* wrap around to beginning */
9987         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9988     }
9989 if ((ep != NULL) && (i != exp->size)) {                 /* Found? */
9990     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern!\n");
9991     if (ep->cnt > 0) {
9992         ep->cnt -= 1;
9993         sim_debug (exp->dbit, exp->dptr, "Waiting for %lld more match%s before stopping\n",
9994                    (long long)ep->cnt, (ep->cnt == 1) ? "" : "es");
9995         }
9996     else {
9997         uint32 after   = exp->after;
9998         int32 switches = ep->switches;
9999         if (ep->act && *ep->act) {
10000             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
10001             }
10002         else {
10003             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
10004             }
10005         sim_brk_setact (ep->act);                       /* set up actions */
10006         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
10007             sim_exp_clrall (exp);                       /* delete all rules */
10008         else {
10009             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
10010                 sim_exp_clr_tab (exp, ep);              /* delete it */
10011             }
10012         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
10013                       (switches & EXP_TYP_TIME) ?
10014                             (uint32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
10015                              after);
10016         }
10017     /* Matched data is no longer available for future matching */
10018     exp->buf_ins = 0;
10019     }
10020 if (tstr)  //-V547
10021   FREE (tstr);
10022 return SCPE_OK;
10023 }
10024 
10025 /* Queue input data for sending */
10026 
10027 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
     /* [previous][next][first][last][top][bottom][index][help] */
10028 {
10029 if (snd->extoff != 0) {
10030     if (snd->insoff > snd->extoff)
10031         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10032     snd->insoff -= snd->extoff;
10033     snd->extoff  = 0;
10034     }
10035 if (snd->insoff+size > snd->bufsize) {
10036     snd->bufsize = snd->insoff+size;
10037     snd->buffer  = (uint8 *)realloc(snd->buffer, snd->bufsize);
10038     if (!snd->buffer)
10039       {
10040         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10041                        __func__, __FILE__, __LINE__);
10042 #if defined(USE_BACKTRACE)
10043 # if defined(SIGUSR2)
10044         (void)raise(SIGUSR2);
10045         /*NOTREACHED*/ /* unreachable */
10046 # endif /* if defined(SIGUSR2) */
10047 #endif /* if defined(USE_BACKTRACE) */
10048         abort();
10049       }
10050     }
10051 memcpy(snd->buffer+snd->insoff, data, size);
10052 snd->insoff += size;
10053 if (delay)
10054     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
10055 if (after)
10056     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
10057 if (snd->after == 0)
10058     snd->after = snd->delay;
10059 snd->next_time = sim_gtime() + snd->after;
10060 return SCPE_OK;
10061 }
10062 
10063 /* Cancel Queued input data */
10064 t_stat sim_send_clear (SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10065 {
10066 snd->insoff = 0;
10067 snd->extoff = 0;
10068 return SCPE_OK;
10069 }
10070 
10071 /* Display console Queued input data status */
10072 
10073 t_stat sim_show_send_input (FILE *st, const SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10074 {
10075 if (snd->extoff < snd->insoff) {
10076     (void)fprintf (st, "%lld bytes of pending input Data:\n    ",
10077                    (long long)snd->insoff-snd->extoff);
10078     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10079     (void)fprintf (st, "\n");
10080     }
10081 else
10082     (void)fprintf (st, "No Pending Input Data\n");
10083 if ((snd->next_time - sim_gtime()) > 0) {
10084     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
10085         (void)fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n",
10086                        (int)(snd->next_time - sim_gtime()),
10087         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
10088     else
10089         (void)fprintf (st, "Minimum of %d instructions before sending first character\n",
10090                        (int)(snd->next_time - sim_gtime()));
10091     }
10092 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
10093     (void)fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n",
10094                    (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
10095 else
10096     (void)fprintf (st, "Minimum of %d instructions between characters\n",
10097                    (int)snd->delay);
10098 if (snd->dptr && snd->dbit)
10099     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\n",
10100                    sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "",
10101                    snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
10102 return SCPE_OK;
10103 }
10104 
10105 /* Poll for Queued input data */
10106 
10107 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10108 {
10109 #if defined(TESTING)
10110 cpu_state_t * cpup = _cpup;
10111 #endif
10112 if ((NULL != snd) && (snd->extoff < snd->insoff)) {     /* pending input characters available? */
10113     if (sim_gtime() < snd->next_time) {                 /* too soon? */
10114         *stat = SCPE_OK;
10115         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
10116         }
10117     else {
10118         char dstr[8] = "";
10119         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
10120         snd->next_time = sim_gtime() + snd->delay;
10121         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
10122             (void)sprintf (dstr, " '%c'", *stat & 0xFF);
10123         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
10124         }
10125     return TRUE;
10126     }
10127 return FALSE;
10128 }
10129 
10130 /* Message Text */
10131 
10132 const char *sim_error_text (t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10133 {
10134 static char msgbuf[64];
10135 
10136 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
10137 if (stat == SCPE_OK)
10138     return "No Error";
10139 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
10140     return scp_errors[stat-SCPE_BASE].message;
10141 (void)sprintf(msgbuf, "Error %d", stat);
10142 return msgbuf;
10143 }
10144 
10145 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10146 {
10147 char gbuf[CBUFSIZE];
10148 size_t cond;
10149 
10150 *stat = SCPE_ARG;
10151 cptr = get_glyph (cptr, gbuf, 0);
10152 if (0 == memcmp("SCPE_", gbuf, 5))
10153     memmove (gbuf, gbuf + 5, 1 + strlen (gbuf + 5));  /* skip leading SCPE_ */
10154 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
10155     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
10156         cond += SCPE_BASE;
10157         break;
10158         }
10159 if (0 == strcmp(gbuf, "OK"))
10160     cond = SCPE_OK;
10161 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
10162     unsigned long numeric_cond = strtol(gbuf, NULL, 0);
10163     if (0 == numeric_cond)                    /* try explicit number */
10164         return SCPE_ARG;
10165     cond = (t_stat) numeric_cond;
10166     }
10167 if (cond > SCPE_MAX_ERR)
10168     return SCPE_ARG;
10169 *stat = cond;
10170 return SCPE_OK;
10171 }
10172 
10173 /* Debug printout routines, from Dave Hittner */
10174 
10175 const char* debug_bstates = "01_^";
10176 char debug_line_prefix[256];
10177 int32 debug_unterm  = 0;
10178 
10179 /* Finds debug phrase matching bitmask from from device DEBTAB table */
10180 
10181 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10182 {
10183 static const char *debtab_none    = "DEBTAB_ISNULL";
10184 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
10185 const char *some_match = NULL;
10186 int32 offset = 0;
10187 
10188 if (dptr->debflags == 0)
10189     return debtab_none;
10190 
10191 dbits &= dptr->dctrl;                           /* Look for just the bits that matched */
10192 
10193 /* Find matching words for bitmask */
10194 
10195 while ((offset < 32) && dptr->debflags[offset].name) {
10196     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
10197         return dptr->debflags[offset].name;
10198     if (dptr->debflags[offset].mask & dbits)
10199         some_match = dptr->debflags[offset].name;
10200     offset++;
10201     }
10202 return some_match ? some_match : debtab_nomatch;
10203 }
10204 
10205 /* Prints standard debug prefix unless previous call unterminated */
10206 
10207 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10208 {
10209 const char* debug_type = get_dbg_verb (dbits, dptr);
10210 char tim_t[32] = "";
10211 char tim_a[32] = "";
10212 char  pc_s[64] = "";
10213 struct timespec time_now;
10214 
10215 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
10216     clock_gettime(CLOCK_REALTIME, &time_now);
10217     if (sim_deb_switches & SWMASK ('R'))
10218         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
10219     if (sim_deb_switches & SWMASK ('T')) {
10220         time_t tnow = (time_t)time_now.tv_sec;
10221         struct tm *now = gmtime(&tnow);
10222         (void)sprintf(tim_t, "%02d:%02d:%02d.%03ld ",
10223                       (int)now->tm_hour,
10224                       (int)now->tm_min,
10225                       (int)now->tm_sec,
10226                       (long)(time_now.tv_nsec / 1000000));
10227         }
10228     if (sim_deb_switches & SWMASK ('A')) {
10229         (void)sprintf(tim_t, "%d.%03ld ",
10230                       (int)(time_now.tv_sec),
10231                       (long)(time_now.tv_nsec / 1000000));
10232         }
10233     }
10234 if (sim_deb_switches & SWMASK ('P')) {
10235     t_value val;
10236 
10237     if (sim_vm_pc_value)
10238         val = (*sim_vm_pc_value)();
10239     else
10240         val = get_rval (sim_PC, 0);
10241     (void)sprintf(pc_s, "-%s:", sim_PC->name);
10242     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
10243     }
10244 (void)sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ",
10245               tim_t, tim_a, sim_gtime(), pc_s,
10246               "", dptr->name, debug_type);
10247 return debug_line_prefix;
10248 }
10249 
10250 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
     /* [previous][next][first][last][top][bottom][index][help] */
10251 {
10252 int32 i, fields, offset;
10253 uint32 value, beforevalue, mask;
10254 
10255 for (fields=offset=0; bitdefs[fields].name; ++fields) {
10256     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
10257         bitdefs[fields].offset = offset;
10258     offset += bitdefs[fields].width;
10259     }
10260 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
10261     if (bitdefs[i].name[0] == '\0')
10262         continue;
10263     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
10264         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
10265         (void)Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
10266         }
10267     else {
10268         const char *delta = "";
10269         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
10270         value = (uint32)((after >> bitdefs[i].offset) & mask);
10271         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
10272         if (value < beforevalue)
10273             delta = "_";
10274         if (value > beforevalue)
10275             delta = "^";
10276         if (bitdefs[i].valuenames)
10277             (void)Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
10278         else
10279             if (bitdefs[i].format) {
10280                 (void)Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
10281                 (void)Fprintf(stream, bitdefs[i].format, value);
10282                 (void)Fprintf(stream, " ");
10283                 }
10284             else
10285                 (void)Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
10286         }
10287     }
10288 }
10289 
10290 /* Prints state of a register: bit translation + state (0,1,_,^)
10291    indicating the state and transition of the bit and bitfields. States:
10292    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
10293 
10294 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
     /* [previous][next][first][last][top][bottom][index][help] */
10295     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
10296 {
10297 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
10298     if (!debug_unterm)
10299         (void)fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
10300     if (header)
10301         (void)fprintf(sim_deb, "%s: ", header);
10302     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
10303     if (terminate)
10304         (void)fprintf(sim_deb, "\r\n");
10305     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
10306     }
10307 }
10308 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
     /* [previous][next][first][last][top][bottom][index][help] */
10309     uint32 before, uint32 after, int terminate)
10310 {
10311 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
10312 }
10313 
10314 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10315 void sim_printf (const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10316 {
10317 char stackbuf[STACKBUFSIZE];
10318 int32 bufsize = sizeof(stackbuf);
10319 char *buf = stackbuf;
10320 int32 len;
10321 va_list arglist;
10322 
10323 while (1) {                                         /* format passed string, args */
10324     va_start (arglist, fmt);
10325     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10326     va_end (arglist);
10327 
10328 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10329 
10330     if ((len < 0) || (len >= bufsize-1)) {
10331         if (buf != stackbuf)
10332             FREE (buf);
10333         if (bufsize >= (INT_MAX / 2))
10334             return;                                 /* too big */
10335         bufsize = bufsize * 2;
10336         if (bufsize < len + 2)
10337             bufsize = len + 2;
10338         buf = (char *) malloc (bufsize);
10339         if (buf == NULL)                            /* out of memory */
10340             return;
10341         buf[bufsize-1] = '\0';
10342         continue;
10343         }
10344     break;
10345     }
10346 
10347 if (sim_is_running) {
10348     char *c, *remnant = buf;
10349     while ((c = strchr(remnant, '\n'))) {
10350         if ((c != buf) && (*(c - 1) != '\r'))
10351             (void)printf("%.*s\r\n", (int)(c-remnant), remnant);
10352         else
10353             (void)printf("%.*s\n", (int)(c-remnant), remnant);
10354         remnant = c + 1;
10355         }
10356     (void)printf("%s", remnant);
10357     }
10358 else
10359     (void)printf("%s", buf);
10360 if (sim_log && (sim_log != stdout))
10361     (void)fprintf (sim_log, "%s", buf);
10362 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
10363     (void)fprintf (sim_deb, "%s", buf);
10364 
10365 if (buf != stackbuf)
10366     FREE (buf);
10367 }
10368 
10369 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10370 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10371 {
10372 char stackbuf[STACKBUFSIZE];
10373 size_t bufsize = sizeof(stackbuf);
10374 char *buf = stackbuf;
10375 size_t len;
10376 va_list arglist;
10377 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
10378 
10379 while (1) {                                         /* format passed string, args */
10380     va_start (arglist, fmt);
10381     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10382     va_end (arglist);
10383 
10384 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10385 
10386     if (len >= bufsize - 1) {
10387         if (buf != stackbuf)
10388             FREE (buf);
10389         bufsize = bufsize * 2;
10390         if (bufsize < len + 2)
10391             bufsize = len + 2;
10392         buf = (char *) malloc (bufsize);
10393         if (buf == NULL)                            /* out of memory */
10394             return SCPE_MEM;
10395         buf[bufsize-1] = '\0';
10396         continue;
10397         }
10398     break;
10399     }
10400 
10401 if (sim_do_ocptr[sim_do_depth]) {
10402     if (!sim_do_echo && !sim_quiet && !inhibit_message)
10403         sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
10404     else {
10405         if (sim_deb)                        /* Always put context in debug output */
10406             (void)fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
10407         }
10408     }
10409 if (sim_is_running && !inhibit_message) {
10410     char *c, *remnant = buf;
10411     while ((c = strchr(remnant, '\n'))) {
10412         if ((c != buf) && (*(c - 1) != '\r'))
10413             (void)printf("%.*s\r\n", (int)(c-remnant), remnant);
10414         else
10415             (void)printf("%.*s\n", (int)(c-remnant), remnant);
10416         remnant = c + 1;
10417         }
10418     (void)printf("%s", remnant);
10419     }
10420 else {
10421     if (!inhibit_message)
10422         (void)printf("%s", buf);
10423     }
10424 if (sim_log && (sim_log != stdout) && !inhibit_message)
10425     (void)fprintf (sim_log, "%s", buf);
10426 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
10427     (void)fprintf (sim_deb, "%s", buf);
10428 
10429 if (buf != stackbuf)
10430     FREE (buf);
10431 return stat | SCPE_NOMESSAGE;
10432 }
10433 
10434 /* Inline debugging - will print debug message if debug file is
10435    set and the bitmask matches the current device debug options.
10436    Extra returns are added for un*x systems, since the output
10437    device is set into 'raw' mode when the cpu is booted,
10438    and the extra returns don't hurt any other systems.
10439    Callers should be calling sim_debug() which is a macro
10440    defined in scp.h which evaluates the action condition before
10441    incurring call overhead. */
10442 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10443 {
10444 DEVICE *dptr = (DEVICE *)vdptr;
10445 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
10446     char stackbuf[STACKBUFSIZE];
10447     int32 bufsize = sizeof(stackbuf);
10448     char *buf = stackbuf;
10449     va_list arglist;
10450     int32 i, j, len;
10451     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
10452 
10453     buf[bufsize-1] = '\0';
10454     while (1) {                                         /* format passed string, args */
10455         va_start (arglist, fmt);
10456         len = vsnprintf (buf, bufsize-1, fmt, arglist);
10457         va_end (arglist);
10458 
10459 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10460 
10461         if ((len < 0) || (len >= bufsize-1)) {
10462             if (buf != stackbuf)
10463                 FREE (buf);
10464             if (bufsize >= (INT_MAX / 2))
10465                 return;                                 /* too big */
10466             bufsize = bufsize * 2;
10467             if (bufsize < len + 2)
10468                 bufsize = len + 2;
10469             buf = (char *) malloc (bufsize);
10470             if (buf == NULL)                            /* out of memory */
10471                 return;
10472             buf[bufsize-1] = '\0';
10473             continue;
10474             }
10475         break;
10476         }
10477 
10478 /* Output the formatted data expanding newlines where they exist */
10479 
10480     for (i = j = 0; i < len; ++i) {
10481         if ('\n' == buf[i]) {
10482             if (i >= j) {
10483                 if ((i != j) || (i == 0)) {
10484                     if (debug_unterm)
10485                         (void)fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
10486                     else                                /* print prefix when required */
10487                         (void)fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
10488                     }
10489                 debug_unterm = 0;
10490                 }
10491             j = i + 1;
10492             }
10493         }
10494     if (i > j) {
10495         if (debug_unterm)
10496             (void)fprintf (sim_deb, "%.*s", i-j, &buf[j]);
10497         else                                        /* print prefix when required */
10498             (void)fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
10499         }
10500 
10501 /* Set unterminated flag for next time */
10502 
10503     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
10504     if (buf != stackbuf)
10505         FREE (buf);
10506     }
10507 return;
10508 }
10509 
10510 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] */
10511 {
10512 #if defined(TESTING)
10513 cpu_state_t * cpup = _cpup;
10514 #endif
10515 if (sim_deb && (dptr->dctrl & reason)) {
10516     sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
10517     if (data && len) {
10518         size_t i, same, group, sidx, oidx, ridx, eidx, soff;
10519         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
10520         static char hex[] = "0123456789ABCDEF";
10521         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
10522         static unsigned char ebcdic2ascii[] = {
10523             0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
10524             0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
10525             0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
10526             0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
10527             0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
10528             0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
10529             0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
10530             0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
10531             0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
10532             0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041,
10533             0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
10534             0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136,
10535             0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
10536             0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077,
10537             0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
10538             0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
10539             0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
10540             0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
10541             0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
10542             0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320,
10543             0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170,
10544             0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327,
10545             0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
10546             0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
10547             0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
10548             0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
10549             0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
10550             0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
10551             0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
10552             0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
10553             0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
10554             0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377,
10555             };
10556 
10557         for (i=same=0; i<len; i += 16) {
10558             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
10559                 ++same;
10560                 continue;
10561                 }
10562             if (same > 0) {
10563                 sim_debug (reason, dptr, "%04lx thru %04lx same as above\n",
10564                            i - (16*same),
10565                            i - 1);
10566                 same = 0;
10567                 }
10568             group = (((len - i) > 16) ? 16 : (len - i));
10569             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10570             eidx = strlen(ebcdicbuf);
10571             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10572             ridx = strlen(rad50buf);
10573             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10574             soff = strlen(strbuf);
10575             for (sidx=oidx=0; sidx<group; ++sidx) {
10576                 outbuf[oidx++] = ' ';
10577                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10578                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10579                 if (sim_isprint (data[i+sidx]))
10580                     strbuf[soff+sidx] = data[i+sidx];
10581                 else
10582                     strbuf[soff+sidx] = '.';
10583                 if (ridx && ((sidx&1) == 0)) {
10584                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10585 
10586                     if (word >= 64000) {
10587                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10588                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10589                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10590                         }
10591                     else {
10592                         rad50buf[ridx++] = rad50[word/1600];
10593                         rad50buf[ridx++] = rad50[(word/40)%40];
10594                         rad50buf[ridx++] = rad50[word%40];
10595                         }
10596                     }
10597                 if (eidx) {
10598                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10599                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10600                     else
10601                         ebcdicbuf[eidx++] = '.';
10602                     }
10603                 }
10604             outbuf[oidx] = '\0';
10605             strbuf[soff+sidx] = '\0';
10606             ebcdicbuf[eidx] = '\0';
10607             rad50buf[ridx] = '\0';
10608             sim_debug (reason, dptr, "%04lx%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
10609             }
10610         if (same > 0) {
10611             sim_debug (reason, dptr, "%04lx thru %04lx same as above\n", i-(16*same), (long unsigned int)(len-1));
10612             }
10613         }
10614     }
10615 }
10616 
10617 int Fprintf (FILE *f, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10618 {
10619 int ret = 0;
10620 va_list args;
10621 
10622 va_start (args, fmt);
10623     ret = vfprintf (f, fmt, args);
10624 va_end (args);
10625 return ret;
10626 }
10627 
10628 /* Hierarchical help presentation
10629  *
10630  * Device help can be presented hierarchically by calling
10631  *
10632  * t_stat scp_help (FILE *st, DEVICE *dptr,
10633  *                  UNIT *uptr, int flag, const char *help, char *cptr)
10634  *
10635  * or one of its three cousins from the device HELP routine.
10636  *
10637  * *help is the pointer to the structured help text to be displayed.
10638  *
10639  * The format and usage, and some helper macros can be found in scp_help.h
10640  * If you don't use the macros, it is not necessary to #include "scp_help.h".
10641  *
10642  * Actually, if you don't specify a DEVICE pointer and don't include
10643  * other device references, it can be used for non-device help.
10644  */
10645 
10646 #define blankch(x) ((x) == ' ' || (x) == '\t')
10647 
10648 typedef struct topic {
10649     size_t         level;
10650     char          *title;
10651     char          *label;
10652     struct topic  *parent;
10653     struct topic **children;
10654     uint32         kids;
10655     char          *text;
10656     size_t         len;
10657     uint32         flags;
10658     size_t         kidwid;
10659 #define HLP_MAGIC_TOPIC  1
10660     } TOPIC;
10661 
10662 static volatile struct {
10663     const char *error;
10664     const char *prox;
10665     size_t block;
10666     size_t line;
10667     } help_where = { "", NULL, 0, 0 };
10668 jmp_buf help_env;
10669 
10670 #define FAIL(why,text,here)        \
10671   {                                \
10672     help_where.error = #text;      \
10673     help_where.prox = here;        \
10674     longjmp ( help_env, (why) );   \
10675     /*LINTED E_STMT_NOT_REACHED*/  \
10676   }
10677 
10678 /*
10679  * Add to topic text.
10680  * Expands text buffer as necessary.
10681  */
10682 
10683 static void appendText (TOPIC *topic, const char *text, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
10684 {
10685 char *newt;
10686 
10687 if (!len)
10688     return;
10689 
10690 newt = (char *)realloc (topic->text, topic->len + len +1);
10691 if (!newt) {
10692 #if !defined(SUNLINT)
10693     FAIL (SCPE_MEM, No memory, NULL);
10694 #endif /* if !defined(SUNLINT) */
10695     }
10696 topic->text = newt;
10697 memcpy (newt + topic->len, text, len);
10698 topic->len +=len;
10699 newt[topic->len] = '\0';
10700 return;
10701 }
10702 
10703 /* Release memory held by a topic and its children.
10704  */
10705 static void cleanHelp (TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10706 {
10707 TOPIC *child;
10708 size_t i;
10709 
10710 FREE (topic->title);
10711 FREE (topic->text);
10712 FREE (topic->label);
10713 for (i = 0; i < topic->kids; i++) {
10714     child = topic->children[i];
10715     cleanHelp (child);
10716     FREE (child);
10717     }
10718 FREE (topic->children);
10719 return;
10720 }
10721 
10722 /* Build a help tree from a string.
10723  * Handles substitutions, formatting.
10724  */
10725 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10726                          UNIT *uptr, const char *htext, va_list ap)
10727 {
10728 char *end;
10729 size_t n, ilvl;
10730 #define VSMAX 100
10731 char *vstrings[VSMAX];
10732 size_t vsnum = 0;
10733 char * astrings[VSMAX+1];
10734 size_t asnum = 0;
10735 char *const *hblock;
10736 const char *ep;
10737 t_bool excluded = FALSE;
10738 
10739 /* variable arguments consumed table.
10740  * The scheme used allows arguments to be accessed in random
10741  * order, but for portability, all arguments must be char *.
10742  * If you try to violate this, there ARE machines that WILL break.
10743  */
10744 
10745 (void)memset (vstrings, 0, sizeof (vstrings));
10746 (void)memset (astrings, 0, sizeof (astrings));
10747 astrings[asnum++] = (char *) htext;
10748 
10749 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10750     help_where.block = hblock - astrings;
10751     help_where.line = 0;
10752     while (*htext) {
10753         const char *start;
10754 
10755         help_where.line++;
10756         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10757             if (excluded) {                     /* Excluded topic text */
10758                 while (*htext && *htext != '\n')
10759                     htext++;
10760                 if (*htext)
10761                     ++htext;
10762                 continue;
10763                 }
10764             ilvl = 1;
10765             appendText (topic, "    ", 4);      /* Basic indentation */
10766             if (*htext == '+') {                /* More for each + */
10767                 while (*htext == '+') {
10768                     ilvl++;
10769                     appendText (topic, "    ", 4);
10770                     htext++;
10771                     }
10772                 }
10773             while (*htext && *htext != '\n' && sim_isspace (*htext))
10774                 htext++;
10775             if (!*htext)                        /* Empty after removing leading spaces */
10776                 break;
10777             start = htext;
10778             while (*htext) {                    /* Process line for substitutions */
10779                 if (*htext == '%') {
10780                     appendText (topic, start, htext - start); /* Flush up to escape */
10781                     switch (*++htext) {         /* Evaluate escape */
10782                         case 'U':
10783                             if (dptr) {
10784                                 char buf[129];
10785                                 n = uptr? uptr - dptr->units: 0;
10786                                 (void)sprintf (buf, "%s%u", dptr->name, (int)n);
10787                                 appendText (topic, buf, strlen (buf));
10788                                 }
10789                             break;
10790                         case 'D':
10791                             if (dptr != NULL)
10792                                 appendText (topic, dptr->name, strlen (dptr->name));
10793                             break;
10794                         case 'S':
10795                             appendText (topic, sim_name, strlen (sim_name));
10796                             break;
10797                         case '%':
10798                             appendText (topic, "%", 1);
10799                             break;
10800                         case '+':
10801                             appendText (topic, "+", 1);
10802                             break;
10803                         default:                    /* Check for vararg # */
10804                             if (sim_isdigit (*htext)) {
10805                                 n = 0;
10806                                 while (sim_isdigit (*htext))
10807                                     n += (n * 10) + (*htext++ - '0');
10808                                 if (( *htext != 'H' && *htext != 's') ||
10809                                     n == 0 || n >= VSMAX) {
10810 #if !defined(SUNLINT)
10811                                     FAIL (SCPE_ARG, Invalid escape, htext);
10812 #endif /* if !defined(SUNLINT) */
10813                                     }
10814                                 while (n > vsnum)   /* Get arg pointer if not cached */
10815                                     vstrings[vsnum++] = va_arg (ap, char *);
10816                                 start = vstrings[n-1]; /* Insert selected string */
10817                                 if (*htext == 'H') {   /* Append as more input */
10818                                     if (asnum >= VSMAX) {
10819 #if !defined(SUNLINT)
10820                                         FAIL (SCPE_ARG, Too many blocks, htext);
10821 #endif /* if !defined(SUNLINT) */
10822                                         }
10823                                     astrings[asnum++] = (char *)start;
10824                                     break;
10825                                     }
10826                                 ep = start;
10827                                 while (*ep) {
10828                                     if (*ep == '\n') {
10829                                         ep++;       /* Segment to \n */
10830                                         appendText (topic, start, ep - start);
10831                                         if (*ep) {  /* More past \n, indent */
10832                                             size_t i;
10833                                             for (i = 0; i < ilvl; i++)
10834                                                 appendText (topic, "    ", 4);
10835                                             }
10836                                         start = ep;
10837                                         }
10838                                     else
10839                                         ep++;
10840                                     }
10841                                 appendText (topic, start, ep-start);
10842                                 break;
10843                                 }
10844 #if !defined(SUNLINT)
10845                             FAIL (SCPE_ARG, Invalid escape, htext);
10846 #endif /* if !defined(SUNLINT) */
10847                         } /* switch (escape) */
10848                     start = ++htext;
10849                     continue;                   /* Current line */
10850                     } /* if (escape) */
10851                 if (*htext == '\n') {           /* End of line, append last segment */
10852                     htext++;
10853                     appendText (topic, start, htext - start);
10854                     break;                      /* To next line */
10855                     }
10856                 htext++;                        /* Regular character */
10857                 }
10858             continue;
10859             } /* topic text line */
10860         if (sim_isdigit (*htext)) {             /* Topic heading */
10861             TOPIC **children;
10862             TOPIC *newt;
10863             char nbuf[100];
10864 
10865             n = 0;
10866             start = htext;
10867             while (sim_isdigit (*htext))
10868                 n += (n * 10) + (*htext++ - '0');
10869             if ((htext == start) || !n) {
10870 #if !defined(SUNLINT)
10871                 FAIL (SCPE_ARG, Invalid topic heading, htext);
10872 #endif /* if !defined(SUNLINT) */
10873                 }
10874             if (n <= topic->level) {            /* Find level for new topic */
10875                 while (n <= topic->level)
10876                     topic = topic->parent;
10877                 }
10878             else {
10879                 if (n > topic->level + 1) {     /* Skipping down more than 1 */
10880 #if !defined(SUNLINT)
10881                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10882 #endif /* if !defined(SUNLINT) */
10883                     }
10884                 }
10885             while (*htext && (*htext != '\n') && sim_isspace (*htext))
10886                 htext++;
10887             if (!*htext || (*htext == '\n')) {  /* Name missing */
10888 #if !defined(SUNLINT)
10889                 FAIL (SCPE_ARG, Missing topic name, htext);
10890 #endif /* if !defined(SUNLINT) */
10891                 }
10892             start = htext;
10893             while (*htext && (*htext != '\n'))
10894                 htext++;
10895             if (start == htext) {               /* Name NULL */
10896 #if !defined(SUNLINT)
10897                 FAIL (SCPE_ARG, Null topic name, htext);
10898 #endif /* if !defined(SUNLINT) */
10899                 }
10900             excluded = FALSE;
10901             if (*start == '?') {                /* Conditional topic? */
10902                 size_t n = 0;
10903                 start++;
10904                 while (sim_isdigit (*start))    /* Get param # */
10905                     n += (n * 10) + (*start++ - '0');
10906                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX) {
10907 #if !defined(SUNLINT)
10908                     FAIL (SCPE_ARG, Invalid parameter number, start);
10909 #endif /* if !defined(SUNLINT) */
10910                     }
10911                 while (n > vsnum)               /* Get arg pointer if not cached */
10912                     vstrings[vsnum++] = va_arg (ap, char *);
10913                 end = vstrings[n-1];            /* Check for True */
10914                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10915                     excluded = TRUE;            /* False, skip topic this time */
10916                     if (*htext)
10917                         htext++;
10918                     continue;
10919                     }
10920                 }
10921             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10922             if (!newt) {
10923 #if !defined(SUNLINT)
10924                 FAIL (SCPE_MEM, No memory, NULL);
10925 #endif /* if !defined(SUNLINT) */
10926                 }
10927             newt->title = (char *) malloc ((htext - start)+1);
10928             if (!newt->title) {
10929                 FREE (newt);
10930 #if !defined(SUNLINT)
10931                 FAIL (SCPE_MEM, No memory, NULL);
10932 #endif /* if !defined(SUNLINT) */
10933                 }
10934             memcpy (newt->title, start, htext - start);
10935             newt->title[htext - start] = '\0';
10936             if (*htext)
10937                 htext++;
10938 
10939             if (newt->title[0] == '$')
10940                 newt->flags |= HLP_MAGIC_TOPIC;
10941 
10942             children = (TOPIC **) realloc (topic->children,
10943                                            (topic->kids +1) * sizeof (TOPIC *));
10944             if (NULL == children) {
10945                 FREE (newt->title);
10946                 FREE (newt);
10947 #if !defined(SUNLINT)
10948                 FAIL (SCPE_MEM, No memory, NULL);
10949 #endif /* if !defined(SUNLINT) */
10950                 }
10951             topic->children = children;
10952             topic->children[topic->kids++] = newt;
10953             newt->level = n;
10954             newt->parent = topic;
10955             n = strlen (newt->title);
10956             if (n > topic->kidwid)
10957                 topic->kidwid = n;
10958             (void)sprintf (nbuf, ".%u", topic->kids);
10959             n = strlen (topic->label) + strlen (nbuf) + 1;
10960             newt->label = (char *) malloc (n);
10961             if (NULL == newt->label) {
10962                 FREE (newt->title);
10963                 topic->children[topic->kids -1] = NULL;
10964                 FREE (newt);
10965 #if !defined(SUNLINT)
10966                 FAIL (SCPE_MEM, No memory, NULL);
10967 #endif /* if !defined(SUNLINT) */
10968                 }
10969             (void)sprintf (newt->label, "%s%s", topic->label, nbuf);
10970             topic = newt;
10971             continue;
10972             } /* digits introducing a topic */
10973         if (*htext == ';') {                    /* Comment */
10974             while (*htext && *htext != '\n')
10975                 htext++;
10976             continue;
10977             }
10978 #if !defined(SUNLINT)
10979         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
10980 #endif /* if !defined(SUNLINT) */
10981         } /* htext not at end */
10982     (void)memset (vstrings, 0, VSMAX * sizeof (char *));
10983     vsnum = 0;
10984     } /* all strings */
10985 
10986 return topic;
10987 }
10988 
10989 /*
10990  * Create prompt string - top thru current topic
10991  * Add prompt at end.
10992  */
10993 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
     /* [previous][next][first][last][top][bottom][index][help] */
10994 {
10995 char *prefix;
10996 char *newp, *newt;
10997 
10998 if (topic->level == 0) {
10999     prefix = (char *) calloc (2,1);
11000     if (!prefix) {
11001 #if !defined(SUNLINT)
11002         FAIL (SCPE_MEM, No memory, NULL);
11003 #endif /* if !defined(SUNLINT) */
11004         }
11005     prefix[0] = '\n';
11006     }
11007 else
11008     prefix = helpPrompt (topic->parent, "", oneword);
11009 
11010 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
11011                         strlen (pstring) +1);
11012 if (!newp) {
11013     FREE (prefix);
11014 #if !defined(SUNLINT)
11015     FAIL (SCPE_MEM, No memory, NULL);
11016 #endif /* if !defined(SUNLINT) */
11017     }
11018 strcpy (newp, prefix);
11019 if (topic->children) {
11020     if (topic->level != 0)
11021         strcat (newp, " ");
11022     newt = (topic->flags & HLP_MAGIC_TOPIC)?
11023             topic->title+1: topic->title;
11024     if (oneword) {
11025         char *np = newp + strlen (newp);
11026         while (*newt) {
11027             *np++ = blankch (*newt)? '_' : *newt;
11028             newt++;
11029             }
11030         *np = '\0';
11031         }
11032     else
11033         strcat (newp, newt);
11034     if (*pstring && *pstring != '?')
11035         strcat (newp, " ");
11036     }
11037 strcat (newp, pstring);
11038 FREE (prefix);
11039 return newp;
11040 }
11041 
11042 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
11043 {
11044 char tbuf[CBUFSIZE];
11045 size_t i, skiplines;
11046 #if defined(_WIN32)
11047 FILE *tmp;
11048 char *tmpnam;
11049 
11050 do {
11051     int fd;
11052     tmpnam = _tempnam (NULL, "simh");
11053     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
11054     if (fd != -1) {
11055         tmp = _fdopen (fd, "w+");
11056         break;
11057         }
11058     } while (1);
11059 #else
11060 FILE *tmp = tmpfile();
11061 #endif /* if defined(_WIN32) */
11062 
11063 if (!tmp) {
11064     (void)fprintf (st, "Unable to create temporary file: %s (Error %d)\n",
11065                    xstrerror_l(errno), errno);
11066     return;
11067     }
11068 
11069 if (topic->title)
11070     (void)fprintf (st, "%s\n", topic->title+1);
11071 
11072 skiplines = 0;
11073 if (topic->title) {
11074   if (!strcmp (topic->title+1, "Registers")) {
11075       fprint_reg_help (tmp, dptr) ;
11076       skiplines = 1;
11077       }
11078   else
11079       if (!strcmp (topic->title+1, "Set commands")) {
11080           fprint_set_help (tmp, dptr);
11081           skiplines = 3;
11082           }
11083       else
11084           if (!strcmp (topic->title+1, "Show commands")) {
11085               fprint_show_help (tmp, dptr);
11086               skiplines = 3;
11087               }
11088   }
11089 rewind (tmp);
11090 if (errno) {
11091     (void)fprintf (st, "rewind: error %d\r\n", errno);
11092 }
11093 
11094 /* Discard leading blank lines/redundant titles */
11095 
11096 for (i =0; i < skiplines; i++)
11097     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
11098 
11099 while (fgets (tbuf, sizeof (tbuf), tmp)) {
11100     if (tbuf[0] != '\n')
11101         fputs ("    ", st);
11102     fputs (tbuf, st);
11103     }
11104 fclose (tmp);
11105 #if defined(_WIN32)
11106 remove (tmpnam);
11107 FREE (tmpnam);
11108 #endif /* if defined(_WIN32) */
11109 return;
11110 }
11111 /* Flatten and display help for those who say they prefer it. */
11112 
11113 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11114                                UNIT *uptr, int32 flag,
11115                                TOPIC *topic, va_list ap )
11116 {
11117 size_t i;
11118 
11119 if (topic->flags & HLP_MAGIC_TOPIC) {
11120     (void)fprintf (st, "\n%s ", topic->label);
11121     displayMagicTopic (st, dptr, topic);
11122     }
11123 else
11124     (void)fprintf (st, "\n%s %s\n", topic->label, topic->title);
11125 
11126 /*
11127  * Topic text (for magic topics, follows for explanations)
11128  * It's possible/reasonable for a magic topic to have no text.
11129  */
11130 
11131 if (topic->text)
11132     fputs (topic->text, st);
11133 
11134 for (i = 0; i < topic->kids; i++)
11135     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
11136 
11137 return SCPE_OK;
11138 }
11139 
11140 #define HLP_MATCH_AMBIGUOUS (~0u)
11141 #define HLP_MATCH_WILDCARD  (~1U)
11142 #define HLP_MATCH_NONE      0
11143 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
     /* [previous][next][first][last][top][bottom][index][help] */
11144 {
11145 size_t i, match;
11146 char cbuf[CBUFSIZE], *cptr;
11147 
11148 if (!strcmp (token, "*"))
11149     return HLP_MATCH_WILDCARD;
11150 
11151 match = 0;
11152 for (i = 0; i < topic->kids; i++) {
11153     strcpy (cbuf,topic->children[i]->title +
11154             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11155     cptr = cbuf;
11156     while (*cptr) {
11157         if (blankch (*cptr)) {
11158             *cptr++ = '_';
11159             }
11160         else {
11161             *cptr = (char)toupper (*cptr);
11162             cptr++;
11163             }
11164         }
11165     if (!strcmp (cbuf, token))      /* Exact Match */
11166         return i+1;
11167     if (!strncmp (cbuf, token, strlen (token))) {
11168         if (match)
11169             return HLP_MATCH_AMBIGUOUS;
11170         match = i+1;
11171         }
11172     }
11173 return match;
11174 }
11175 
11176 /* Main help routine */
11177 
11178 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11179                   UNIT *uptr, int32 flag,
11180                   const char *help, const char *cptr, va_list ap)
11181 {
11182 TOPIC top;
11183 TOPIC *topic = &top;
11184 int failed;
11185 size_t match;
11186 size_t i;
11187 const char *p;
11188 t_bool flat_help = FALSE;
11189 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
11190 
11191 static const char attach_help[] = { " ATTACH" };
11192 static const char  brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help.\n" };
11193 static const char onecmd_help[] = { "%s help.\n" };
11194 static const char   help_help[] = {
11195     /****|***********************80 column width guide********************************/
11196     "    To see more HELP information, type the listed subtopic name.  To move\n"
11197     "    up a level, just type <CR>.  To review the current subtopic, type \"?\".\n"
11198     "    To view all subtopics, type \"*\".  To exit type \"EXIT\", \"^C\", or \"^D\".\n\n"
11199     };
11200 
11201 (void)memset (&top, 0, sizeof(top));
11202 top.parent = &top;
11203 if ((failed = setjmp (help_env)) != 0) {
11204     (void)fprintf (stderr, "\nHELP was unable to process HELP for this device.\n"
11205                            "Error in block %u line %u: %s\n"
11206                            "%s%*.*s%s\n",
11207                    (int)help_where.block, (int)help_where.line, help_where.error,
11208                    help_where.prox ? "Near '" : "",
11209                    help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
11210                    help_where.prox ? help_where.prox : "",
11211                    help_where.prox ? "'" : "");
11212     cleanHelp (&top);
11213     return failed;
11214     }
11215 
11216 /* Compile string into navigation tree */
11217 
11218 /* Root */
11219 
11220 if (dptr) {
11221     p = dptr->name;
11222     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
11223     }
11224 else
11225     p = sim_name;
11226 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
11227 if (!top.title)
11228   {
11229     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11230                    __func__, __FILE__, __LINE__);
11231 #if defined(USE_BACKTRACE)
11232 # if defined(SIGUSR2)
11233     (void)raise(SIGUSR2);
11234     /*NOTREACHED*/ /* unreachable */
11235 # endif /* if defined(SIGUSR2) */
11236 #endif /* if defined(USE_BACKTRACE) */
11237     abort();
11238   }
11239 for (i = 0; p[i]; i++ )
11240     top.title[i] = (char)toupper (p[i]);
11241 top.title[i] = '\0';
11242 if (flag & SCP_HELP_ATTACH)
11243     strcpy (top.title+i, attach_help);
11244 
11245 top.label = (char *) malloc (sizeof ("1"));
11246 if (!top.label)
11247   {
11248     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11249                    __func__, __FILE__, __LINE__);
11250 #if defined(USE_BACKTRACE)
11251 # if defined(SIGUSR2)
11252     (void)raise(SIGUSR2);
11253     /*NOTREACHED*/ /* unreachable */
11254 # endif /* if defined(SIGUSR2) */
11255 #endif /* if defined(USE_BACKTRACE) */
11256     abort();
11257   }
11258 strcpy (top.label, "1");
11259 
11260 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
11261 
11262 if (flat_help) {
11263     flag |= SCP_HELP_FLAT;
11264     if (sim_ttisatty())
11265         (void)fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
11266     else
11267         (void)fprintf (st, "%s help.\n", top.title);
11268     }
11269 else
11270     (void)fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
11271 
11272 /* Add text and subtopics */
11273 
11274 (void) buildHelp (&top, dptr, uptr, help, ap);
11275 
11276 /* Go to initial topic if provided */
11277 
11278 while (cptr && *cptr) {
11279     cptr = get_glyph (cptr, gbuf, 0);
11280     if (!gbuf[0])
11281         break;
11282     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11283         (void)fprintf (st, "\n");
11284         fputs (help_help, st);
11285         break;
11286         }
11287     match =  matchHelpTopicName (topic, gbuf);
11288     if (match == HLP_MATCH_WILDCARD) {
11289         if (dptr)
11290             displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11291         cleanHelp (&top);
11292         return SCPE_OK;
11293         }
11294     if (match == HLP_MATCH_AMBIGUOUS) {
11295         (void)fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
11296         break;
11297         }
11298     if (match == HLP_MATCH_NONE) {
11299         (void)fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
11300         break;
11301         }
11302     topic = topic->children[match-1];
11303     }
11304 cptr = NULL;
11305 
11306 if (flat_help) {
11307     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11308     cleanHelp (&top);
11309     return SCPE_OK;
11310     }
11311 
11312 /* Interactive loop displaying help */
11313 
11314 while (TRUE) {
11315     char *pstring;
11316     const char *prompt[2] = {"? ", "Subtopic? "};
11317 
11318     /* Some magic topic names for help from data structures */
11319 
11320     if (topic->flags & HLP_MAGIC_TOPIC) {
11321         fputc ('\n', st);
11322         displayMagicTopic (st, dptr, topic);
11323         }
11324     else
11325         (void)fprintf (st, "\n%s\n", topic->title);
11326 
11327     /* Topic text (for magic topics, follows for explanations)
11328      * It's possible/reasonable for a magic topic to have no text.
11329      */
11330 
11331     if (topic->text)
11332         fputs (topic->text, st);
11333 
11334     if (topic->kids) {
11335         size_t w = 0;
11336         char *p;
11337         char tbuf[CBUFSIZE];
11338 
11339         (void)fprintf (st, "\n    Additional information available:\n\n");
11340         for (i = 0; i < topic->kids; i++) {
11341             strcpy (tbuf, topic->children[i]->title +
11342                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11343             for (p = tbuf; *p; p++) {
11344                 if (blankch (*p))
11345                     *p = '_';
11346                 }
11347             w += 4 + topic->kidwid;
11348             if (w > 80) {
11349                 w = 4 + topic->kidwid;
11350                 fputc ('\n', st);
11351                 }
11352             (void)fprintf (st, "    %-*s", (int32_t)topic->kidwid, tbuf);
11353             }
11354         (void)fprintf (st, "\n\n");
11355         if (flag & SCP_HELP_ONECMD) {
11356             pstring = helpPrompt (topic, "", TRUE);
11357             (void)fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
11358             FREE (pstring);
11359             break;
11360             }
11361         }
11362 
11363     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
11364         break;
11365 
11366   reprompt:
11367     if (NULL == cptr || !*cptr) {
11368         if (topic->kids == 0)
11369             topic = topic->parent;
11370         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
11371 
11372         cptr = read_line_p (pstring+1, cbuf, sizeof (cbuf), stdin);
11373         FREE (pstring);
11374         if ((cptr != NULL) &&                   /* Got something? */
11375             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
11376              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
11377             cptr = NULL;                        /* These are EOF synonyms */
11378         }
11379 
11380     if (NULL == cptr)                           /* EOF, exit help */
11381         break;
11382 
11383     cptr = get_glyph (cptr, gbuf, 0);
11384     if (!strcmp (gbuf, "*")) {              /* Wildcard */
11385         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11386         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
11387         }
11388     if (!gbuf[0]) {                         /* Blank, up a level */
11389         if (topic->level == 0)
11390             break;
11391         topic = topic->parent;
11392         continue;
11393         }
11394     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
11395         continue;
11396     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11397         fputs (help_help, st);
11398         goto reprompt;
11399         }
11400     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
11401         break;
11402 
11403     /* String - look for that topic */
11404 
11405     if (!topic->kids) {
11406         (void)fprintf (st, "No additional help at this level.\n");
11407         cptr = NULL;
11408         goto reprompt;
11409         }
11410     match = matchHelpTopicName (topic, gbuf);
11411     if (match == HLP_MATCH_AMBIGUOUS) {
11412         (void)fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
11413         cptr = NULL;
11414         goto reprompt;
11415         }
11416 
11417     if (match == HLP_MATCH_NONE) {
11418         (void)fprintf (st, "Help for %s is not available\n", gbuf);
11419         cptr = NULL;
11420         goto reprompt;
11421         }
11422     /* Found, display subtopic */
11423 
11424     topic = topic->children[match-1];
11425     }
11426 
11427 /* Free structures and return */
11428 
11429 cleanHelp (&top);
11430 
11431 return SCPE_OK;
11432 }
11433 
11434 /* variable argument list shell - most commonly used */
11435 
11436 t_stat scp_help (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11437                  UNIT *uptr, int32 flag,
11438                  const char *help, const char *cptr, ...)
11439 {
11440 t_stat r;
11441 va_list ap;
11442 
11443 va_start (ap, cptr);
11444 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
11445 va_end (ap);
11446 
11447 return r;
11448 }
11449 
11450 #if defined(_MSC_VER)
11451 # pragma warning(pop)
11452 #endif

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