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. strremove
  5. strtrimspace
  6. allowCores
  7. CleanDUMA
  8. dl_iterate_phdr_callback
  9. GetUCRTVersion
  10. dps8_sir_report_error
  11. main
  12. process_stdin_commands
  13. set_prompt
  14. find_cmd
  15. exit_cmd
  16. _cmd_name_compare
  17. fprint_help
  18. fprint_header
  19. fprint_reg_help_ex
  20. fprint_reg_help
  21. fprint_attach_help_ex
  22. fprint_set_help_ex
  23. fprint_set_help
  24. fprint_show_help_ex
  25. fprint_show_help
  26. fprint_brk_help_ex
  27. help_dev_help
  28. help_cmd_output
  29. help_cmd
  30. spawn_cmd
  31. echo_cmd
  32. do_cmd
  33. do_position
  34. do_cmd_label
  35. sim_sub_args
  36. sim_cmp_string
  37. assert_cmd
  38. send_cmd
  39. sim_show_send
  40. expect_cmd
  41. sim_show_expect
  42. goto_cmd
  43. return_cmd
  44. shift_cmd
  45. call_cmd
  46. on_cmd
  47. noop_cmd
  48. set_on
  49. set_verify
  50. set_message
  51. set_localopc
  52. set_quiet
  53. sim_set_environment
  54. set_cmd
  55. find_ctab
  56. find_c1tab
  57. set_dev_radix
  58. set_dev_enbdis
  59. set_unit_enbdis
  60. set_dev_debug
  61. show_cmd
  62. show_cmd_fi
  63. find_shtab
  64. show_device
  65. fprint_sep
  66. show_unit
  67. sprint_capac
  68. fprint_capac
  69. show_default_base_system_script
  70. printp
  71. strip_spaces
  72. printpq
  73. show_prom
  74. show_buildinfo
  75. removehex
  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) 2026 Eric Swenson
  12  * Copyright (c) 2021-2026 Jeffrey H. Johnson
  13  * Copyright (c) 2006-2026 The DPS8M Development Team
  14  *
  15  * Permission is hereby granted, free of charge, to any person obtaining a
  16  * copy of this software and associated documentation files (the "Software"),
  17  * to deal in the Software without restriction, including without limitation
  18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  19  * and/or sell copies of the Software, and to permit persons to whom the
  20  * Software is furnished to do so, subject to the following conditions:
  21  *
  22  * The above copyright notice and this permission notice shall be included in
  23  * all copies or substantial portions of the Software.
  24  *
  25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  28  * ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  29  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  30  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31  * SOFTWARE.
  32  *
  33  * Except as contained in this notice, the name of Robert M. Supnik shall not
  34  * be used in advertising or otherwise to promote the sale, use or other
  35  * dealings in this Software without prior written authorization from
  36  * Robert M. Supnik.
  37  *
  38  * ---------------------------------------------------------------------------
  39  */
  40 
  41 //-V::701
  42 
  43 #if !defined(__EXTENSIONS__)
  44 # define __EXTENSIONS__
  45 #endif
  46 
  47 #if !defined(__STDC_WANT_LIB_EXT1__)
  48 # define __STDC_WANT_LIB_EXT1__ 1
  49 #endif
  50 
  51 #if !defined(_GNU_SOURCE)
  52 # define _GNU_SOURCE
  53 #endif
  54 
  55 /* Macros and data structures */
  56 
  57 #include "sim_defs.h"
  58 #include "sim_disk.h"
  59 #include "sim_hints.h"
  60 #include "sim_tape.h"
  61 #include "sim_sock.h"
  62 #include "sim_os_mem.h"
  63 
  64 #include <signal.h>
  65 #include <ctype.h>
  66 #include <time.h>
  67 #include <math.h>
  68 #include <stddef.h>
  69 #if defined(_WIN32)
  70 # if !defined(WIN32_LEAN_AND_MEAN)
  71 #  define WIN32_LEAN_AND_MEAN
  72 # endif /* if !defined(WIN32_LEAN_AND_MEAN) */
  73 # if defined(_MSC_VER)
  74 #  pragma warning(push, 3)
  75 # endif
  76 # include <direct.h>
  77 # include <io.h>
  78 # include <fcntl.h>
  79 #else
  80 # include <unistd.h>
  81 # define HAVE_UNISTD 1
  82 #endif
  83 #include <sys/stat.h>
  84 #include <sys/types.h>
  85 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  86 # include <sys/resource.h>
  87 #endif
  88 #include <setjmp.h>
  89 #include <stdint.h>
  90 #include <limits.h>
  91 #if defined(__APPLE__)
  92 # include <xlocale.h>
  93 #endif
  94 #include <locale.h>
  95 
  96 #include "linehistory.h"
  97 
  98 #if defined(__APPLE__)
  99 # include <sys/sysctl.h>
 100 #endif /* if defined(_APPLE_) */
 101 
 102 #if ( defined(__linux__) || defined(__linux) || defined(_linux) || defined(linux) ) //-V1040
 103 # include <sys/sysinfo.h>
 104 # define LINUX_OS
 105 #endif
 106 
 107 #include <uv.h>
 108 
 109 #if !defined(HAVE_UNISTD)
 110 # undef USE_BACKTRACE
 111 #endif /* if !defined(HAVE_UNISTD) */
 112 
 113 #if defined(USE_BACKTRACE)
 114 # include <string.h>
 115 # include <signal.h>
 116 #endif /* if defined(USE_BACKTRACE) */
 117 
 118 #if defined(__HAIKU__)
 119 # include <OS.h>
 120 #endif /* if defined(__HAIKU__) */
 121 
 122 #if !defined(__CYGWIN__)
 123 # if !defined(__APPLE__)
 124 #  if !defined(_AIX)
 125 #   if !defined(__MINGW32__)
 126 #    if !defined(__MINGW64__)
 127 #     if !defined(CROSS_MINGW32)
 128 #      if !defined(CROSS_MINGW64)
 129 #       if !defined(_WIN32)
 130 #        if !defined(__HAIKU__)
 131 #         if !defined(__QNX__)
 132 #          include <link.h>
 133 #         endif
 134 #        endif
 135 #       endif
 136 #      endif
 137 #     endif
 138 #    endif
 139 #   endif
 140 #  endif
 141 # endif
 142 #endif
 143 
 144 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
 145 # include <windows.h>
 146 #endif
 147 
 148 #if defined(__CYGWIN__)
 149 # include <windows.h>
 150 # include <sys/utsname.h>
 151 # include <sys/cygwin.h>
 152 # include <cygwin/version.h>
 153 #endif
 154 
 155 #define DBG_CTR 0
 156 
 157 #include "../dps8/dps8.h"
 158 #include "../dps8/dps8_cpu.h"
 159 #include "../dps8/dps8_rt.h"
 160 #include "../dps8/dps8_priv.h"
 161 #include "../dps8/dps8_topo.h"
 162 #include "../dps8/ver.h"
 163 
 164 #include "../dps8/dps8_iom.h"
 165 #include "../dps8/dps8_fnp2.h"
 166 
 167 #include "../decNumber/decContext.h"
 168 #include "../decNumber/decNumberLocal.h"
 169 
 170 #include "../dps8/dps8_math128.h"
 171 
 172 #include "../dps8/dps8_sir.h"
 173 
 174 #if !defined(__CYGWIN__)
 175 # if !defined(__APPLE__)
 176 #  if !defined(_AIX)
 177 #   if !defined(__MINGW32__)
 178 #    if !defined(__MINGW64__)
 179 #     if !defined(CROSS_MINGW32)
 180 #      if !defined(CROSS_MINGW64)
 181 #       if !defined(_WIN32)
 182 #        if !defined(__HAIKU__)
 183 #         if !defined(__QNX__)
 184 #          if !defined(__FILC__)
 185 static unsigned int dl_iterate_phdr_callback_called = 0;
 186 #          endif
 187 #         endif
 188 #        endif
 189 #       endif
 190 #      endif
 191 #     endif
 192 #    endif
 193 #   endif
 194 #  endif
 195 # endif
 196 #endif
 197 
 198 #if defined(MAX)
 199 # undef MAX
 200 #endif /* if defined(MAX) */
 201 #define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
 202 
 203 #if defined(FREE)
 204 # undef FREE
 205 #endif /* if defined(FREE) */
 206 #define FREE(p) do  \
 207   {                 \
 208     free((p));      \
 209     (p) = NULL;     \
 210   } while(0)
 211 
 212 /* search logical and boolean ops */
 213 
 214 #define SCH_OR          0                               /* search logicals */
 215 #define SCH_AND         1
 216 #define SCH_XOR         2
 217 #define SCH_E           0                               /* search booleans */
 218 #define SCH_N           1
 219 #define SCH_G           2
 220 #define SCH_L           3
 221 #define SCH_EE          4
 222 #define SCH_NE          5
 223 #define SCH_GE          6
 224 #define SCH_LE          7
 225 
 226 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
 227 #define SRBSIZ          1024                            /* save/restore buffer */
 228 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
 229 #define SIM_BRK_ALLTYP  0xFFFFFFFB
 230 
 231 #define UPDATE_SIM_TIME                                         \
 232     if (1) {                                                    \
 233         int32 _x;                                               \
 234         if (sim_clock_queue == QUEUE_LIST_END)                  \
 235             _x = noqueue_time;                                  \
 236         else                                                    \
 237             _x = sim_clock_queue->time;                         \
 238         sim_time = sim_time + (_x - sim_interval);              \
 239         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
 240         if (sim_clock_queue == QUEUE_LIST_END)                  \
 241             noqueue_time = sim_interval;                        \
 242         else                                                    \
 243             sim_clock_queue->time = sim_interval;               \
 244         }                                                       \
 245     else                                                        \
 246         (void)0
 247 
 248 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
 249 
 250 #define SZ_R(rp) \
 251     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
 252 
 253 #define SZ_LOAD(sz,v,mb,j)                                                 \
 254     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j));        \
 255     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
 256     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
 257     else v = *(((t_uint64 *) mb) + ((uint32) j));
 258 
 259 #define SZ_STORE(sz,v,mb,j)                                                         \
 260     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v;                    \
 261     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
 262     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
 263     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
 264 
 265 #define GET_SWITCHES(cp) \
 266     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
 267 
 268 #define GET_RADIX(val,dft)                          \
 269     if (sim_switches & SWMASK ('O')) val = 8;       \
 270     else if (sim_switches & SWMASK ('D')) val = 10; \
 271     else if (sim_switches & SWMASK ('H')) val = 16; \
 272     else val = dft;
 273 
 274 /*
 275  * The per-simulator init routine is a weak global that defaults to NULL
 276  * The other per-simulator pointers can be overridden by the init routine
 277  */
 278 
 279 t_bool sim_asynch_enabled = FALSE;
 280 t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
 281 t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
 282 extern void (*sim_vm_init) (void);
 283 extern void (*sim_vm_exit) (void);
 284 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
 285 void (*sim_vm_post) (t_bool from_scp) = NULL;
 286 CTAB *sim_vm_cmd = NULL;
 287 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
 288 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
 289 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
 290 t_value (*sim_vm_pc_value) (void) = NULL;
 291 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
 292 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
 293 unsigned int nprocs;
 294 unsigned int ncores;
 295 bool mlock_failure = false;
 296 char* sim_appfilename;
 297 
 298 /* Prototypes */
 299 
 300 /* Set and show command processors */
 301 
 302 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 303 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 304 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 305 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 306 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
 307 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
 308 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 309 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 310 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 311 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 312 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 313 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 314 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 315 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 316 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 317 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 318 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 319 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 320 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
 321 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 322 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 323 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 324 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 325 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 326 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 327 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 328 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
 329 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
 330 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
 331 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
 332 t_stat sim_save (FILE *sfile);
 333 t_stat sim_rest (FILE *rfile);
 334 
 335 /* Breakpoint package */
 336 
 337 t_stat sim_brk_init (void);
 338 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
 339 t_stat sim_brk_clr (t_addr loc, int32 sw);
 340 t_stat sim_brk_clrall (int32 sw);
 341 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
 342 t_stat sim_brk_showall (FILE *st, uint32 sw);
 343 CONST char *sim_brk_getact (char *buf, int32 size);
 344 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
 345 char *sim_brk_clract (void);
 346 
 347 FILE *stdnul;
 348 
 349 /* Command support routines */
 350 
 351 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 352 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 353 int32 test_search (t_value *val, SCHTAB *schptr);
 354 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
 355 int32 get_switches (const char *cptr);
 356 CONST char *get_sim_sw (CONST char *cptr);
 357 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
 358 t_value get_rval (REG *rptr, uint32 idx);
 359 void put_rval (REG *rptr, uint32 idx, t_value val);
 360 void fprint_help (FILE *st);
 361 void fprint_stopped (FILE *st, t_stat r);
 362 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
 363 void fprint_sep (FILE *st, int32 *tokens);
 364 char *read_line (char *ptr, int32 size, FILE *stream);
 365 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
 366 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
 367 char *sim_trim_endspc (char *cptr);
 368 
 369 /* Forward references */
 370 
 371 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
 372 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
 373 t_bool qdisable (DEVICE *dptr);
 374 t_stat attach_err (UNIT *uptr, t_stat stat);
 375 t_stat detach_all (int32 start_device, t_bool shutdown);
 376 t_stat assign_device (DEVICE *dptr, const char *cptr);
 377 t_stat deassign_device (DEVICE *dptr);
 378 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
 379 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
 380     REG *lowr, REG *highr, uint32 lows, uint32 highs);
 381 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
 382 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
 383 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
 384     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
 385 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
 386 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
 387     UNIT *uptr, int32 dfltinc);
 388 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
 389 t_stat step_svc (UNIT *ptr);
 390 t_stat expect_svc (UNIT *ptr);
 391 t_stat set_on (int32 flag, CONST char *cptr);
 392 t_stat set_verify (int32 flag, CONST char *cptr);
 393 t_stat set_message (int32 flag, CONST char *cptr);
 394 t_stat set_quiet (int32 flag, CONST char *cptr);
 395 t_stat set_localopc (int32 flag, CONST char *cptr);
 396 t_stat set_asynch (int32 flag, CONST char *cptr);
 397 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 398 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
 399 void int_handler (int signal);
 400 t_stat set_prompt (int32 flag, CONST char *cptr);
 401 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
 402 t_stat sim_set_environment (int32 flag, CONST char *cptr);
 403 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
 404 
 405 /* Global data */
 406 
 407 DEVICE *sim_dflt_dev             = NULL;
 408 UNIT *sim_clock_queue            = QUEUE_LIST_END;
 409 int32 sim_interval               = 0;
 410 int32 sim_switches               = 0;
 411 FILE *sim_ofile                  = NULL;
 412 SCHTAB *sim_schrptr              = NULL;
 413 SCHTAB *sim_schaptr              = NULL;
 414 DEVICE *sim_dfdev                = NULL;
 415 UNIT *sim_dfunit                 = NULL;
 416 DEVICE **sim_internal_devices    = NULL;
 417 uint32 sim_internal_device_count = 0;
 418 int32 sim_opt_out                = 0;
 419 int32 sim_is_running             = 0;
 420 t_bool sim_processing_event      = FALSE;
 421 uint32 sim_brk_summ              = 0;
 422 uint32 sim_brk_types             = 0;
 423 BRKTYPTAB *sim_brk_type_desc     = NULL;         /* type descriptions */
 424 uint32 sim_brk_dflt              = 0;
 425 uint32 sim_brk_match_type;
 426 t_addr sim_brk_match_addr;
 427 char *sim_brk_act[MAX_DO_NEST_LVL];
 428 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
 429 BRKTAB **sim_brk_tab             = NULL;
 430 int32 sim_brk_ent                = 0;
 431 int32 sim_brk_lnt                = 0;
 432 int32 sim_brk_ins                = 0;
 433 int32 sim_iglock                 = 0;
 434 int32 sim_nolock                 = 0;
 435 int32 sim_quiet                  = 0;
 436 int32 sim_localopc               = 1;
 437 int32 sim_randompst              = 0;
 438 int32 sim_randstate              = 0;
 439 int32 sim_step                   = 0;
 440 int nodist                       = 0;
 441 #if defined(PERF_STRIP)
 442 int32 sim_nostate                = 1;
 443 #else
 444 int32 sim_nostate                = 0;
 445 #endif /* if defined(PERF_STRIP) */
 446 static double sim_time;
 447 static uint32 sim_rtime;
 448 static int32 noqueue_time;
 449 volatile int32 stop_cpu          = 0;
 450 t_value *sim_eval                = NULL;
 451 static t_value sim_last_val;
 452 static t_addr sim_last_addr;
 453 FILE *sim_log                    = NULL;         /* log file */
 454 FILEREF *sim_log_ref             = NULL;         /* log file file reference */
 455 FILE *sim_deb                    = NULL;         /* debug file */
 456 FILEREF *sim_deb_ref             = NULL;         /* debug file file reference */
 457 int32 sim_deb_switches           = 0;            /* debug switches */
 458 struct timespec sim_deb_basetime;                /* debug timestamp relative base time */
 459 char *sim_prompt                 = NULL;         /* prompt string */
 460 static FILE *sim_gotofile;                       /* the currently open do file */
 461 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];   /* the current line number in the currently open do file */
 462 static int32 sim_do_echo         = 0;            /* the echo status of the currently open do file */
 463 static int32 sim_show_message    = 1;            /* the message display status of the currently open do file */
 464 static int32 sim_on_inherit      = 0;            /* the inherit status of on state and conditions when executing do files */
 465 #if !defined(PERF_STRIP)
 466 static int32 sim_realtime        = 0;            /* user requested real-time mode */
 467 #endif
 468 static int32 sim_do_depth        = 0;
 469 uint64_t sim_free_memory         = 0;
 470 
 471 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
 472 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
 473 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
 474 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
 475 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
 476 
 477 t_stat sim_last_cmd_stat;                        /* Command Status */
 478 
 479 static SCHTAB sim_stabr;                         /* Register search specifier */
 480 static SCHTAB sim_staba;                         /* Memory search specifier */
 481 
 482 static UNIT sim_step_unit   = { UDATA (&step_svc, 0, 0)  };
 483 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
 484 
 485 pthread_t main_thread_id;
 486 
 487 /* Tables and strings */
 488 
 489 const char save_vercur[] = "V4.1";
 490 const char save_ver40[]  = "V4.0";
 491 const char save_ver35[]  = "V3.5";
 492 const char save_ver32[]  = "V3.2";
 493 const char save_ver30[]  = "V3.0";
 494 const struct scp_error {
 495     const char *code;
 496     const char *message;
 497     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
 498         {{"NXM",     "Address space exceeded"},
 499          {"UNATT",   "Unit not attached"},
 500          {"IOERR",   "I/O error"},
 501          {"CSUM",    "Checksum error"},
 502          {"FMT",     "Format error"},
 503          {"NOATT",   "Unit not attachable"},
 504          {"OPENERR", "File open error"},
 505          {"MEM",     "Memory exhausted"},
 506          {"ARG",     "Invalid argument"},
 507          {"STEP",    "Step expired"},
 508          {"UNK",     "Unknown command"},
 509          {"RO",      "Read only argument"},
 510          {"INCOMP",  "Command not completed"},
 511          {"STOP",    "Simulation stopped"},
 512          {"EXIT",    "Goodbye"},
 513          {"TTIERR",  "Console input I/O error"},
 514          {"TTOERR",  "Console output I/O error"},
 515          {"EOF",     "End of file"},
 516          {"REL",     "Relocation error"},
 517          {"NOPARAM", "No settable parameters"},
 518          {"ALATT",   "Unit already attached"},
 519          {"TIMER",   "Hardware timer error"},
 520          {"SIGERR",  "Signal handler setup error"},
 521          {"TTYERR",  "Console terminal setup error"},
 522          {"SUB",     "Subscript out of range"},
 523          {"NOFNC",   "Command not allowed"},
 524          {"UDIS",    "Unit disabled"},
 525          {"NORO",    "Read only operation not allowed"},
 526          {"INVSW",   "Invalid switch"},
 527          {"MISVAL",  "Missing value"},
 528          {"2FARG",   "Too few arguments"},
 529          {"2MARG",   "Too many arguments"},
 530          {"NXDEV",   "Non-existent device"},
 531          {"NXUN",    "Non-existent unit"},
 532          {"NXREG",   "Non-existent register"},
 533          {"NXPAR",   "Non-existent parameter"},
 534          {"NEST",    "Nested DO command limit exceeded"},
 535          {"IERR",    "Internal error"},
 536          {"MTRLNT",  "Invalid magtape record length"},
 537          {"LOST",    "Console Telnet connection lost"},
 538          {"TTMO",    "Console Telnet connection timed out"},
 539          {"STALL",   "Console Telnet output stall"},
 540          {"AFAIL",   "Assertion failed"},
 541          {"INVREM",  "Invalid remote console command"},
 542          {"NOTATT",  "Not attached"},
 543          {"EXPECT",  "Expect matched"},
 544          {"REMOTE",  "Remote console command"},
 545     };
 546 
 547 const size_t size_map[] = { sizeof (int8),
 548     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
 549     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
 550 };
 551 
 552 const t_value width_mask[] = { 0,
 553     0x1, 0x3, 0x7, 0xF,
 554     0x1F, 0x3F, 0x7F, 0xFF,
 555     0x1FF, 0x3FF, 0x7FF, 0xFFF,
 556     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
 557     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
 558     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
 559     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
 560     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
 561     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
 562     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
 563     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
 564     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
 565     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
 566     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
 567     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
 568     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
 569     };
 570 
 571 static const char simh_help[] =
 572        /***************** 80 character line width template *************************/
 573       "1Commands\n" //-NLOK
 574 #define HLP_RESET       "*Commands Resetting Devices"
 575        /***************** 80 character line width template *************************/
 576       "2Resetting Devices\n" //-NLOK
 577       " The `RESET` command (*abbreviated* `RE`) resets a device or the entire\r\n"
 578       " simulator to a predefined condition.  If the switch \"`-p`\" is specified,\r\n"
 579       " the device is reset to its initial power-on state:\r\n\r\n"
 580       "++RESET                  resets all devices\r\n"
 581       "++RESET -p               power-cycle all devices\r\n"
 582       "++RESET ALL              resets all devices\r\n"
 583       "++RESET <device>         resets the specified <device>\r\n\r\n"
 584       " * Typically, `RESET` *aborts* in-progress I/O operations, *clears* any\r\n"
 585       " interrupt requests, and returns the device to a quiescent state.\r\n\r\n"
 586       " * It does **NOT** clear the main memory or affect associated I/O\r\n"
 587       " connections.\r\n"
 588 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
 589 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
 590 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
 591 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
 592        /***************** 80 character line width template *************************/
 593       "2Examining and Changing State\n" //-NLOK
 594       " There are four commands to examine and change state:\r\n\r\n"
 595       " * `EXAMINE` (*abbreviated* `E`) examines state\r\n"
 596       " * `DEPOSIT` (*abbreviated* `D`) changes state\r\n"
 597       " * `IEXAMINE` (\"interactive examine\", *abbreviated* `IE`) examines state\r\n"
 598       "    and allows the user to interactively change it\r\n"
 599       " * `IDEPOSIT` (interactive deposit, *abbreviated* `ID`) allows the user to\r\n"
 600       "    interactively change state\r\n\r\n"
 601       " All four commands take the form:\r\n\r\n"
 602       "++command {modifiers} <object list>\r\n\r\n"
 603       " The `DEPOSIT` command requires the deposit value at the end of the command.\r\n\r\n"
 604       " There are four kinds of modifiers: **switches**, **device/unit name**,\r\n"
 605       " **search specifier**, and for `EXAMINE`, **output file**.\r\n\r\n"
 606       " * **Switches** have been described previously.\r\n"
 607       " * A **device/unit name** identifies the device and unit whose address\r\n"
 608       " space is to be examined or modified. If no device is specified, the CPU\r\n"
 609       " main memory is selected. If a device but no unit is specified, unit `0`\r\n"
 610       " of the specified device is selected automatically.\r\n"
 611       " * The **search specifier** provides criteria for testing addresses or\r\n"
 612       " registers to see if they should be processed.  The search specifier\r\n"
 613       " consists of a \"<`logical operator`>\", a \"<`relational operator`>\", or\r\n"
 614       " both, optionally separated by spaces:\r\n\r\n"
 615       "++{ < logical op >  < value > }  < relational op >  < value >\r\n\r\n"
 616        /***************** 80 character line width template *************************/
 617       " * * The \"<`logical operator`>\" may be \"`&`\" (*and*), \"`|`\" (*or*),\r\n"
 618       " or \"`^`\" (*exclusive or*), and the \"<`relational operator`>\" may\r\n"
 619       " be \"`=`\" or \"`==`\" (*equal*), \"`!`\" or \"`!=`\" (*not\r\n"
 620       " equal*), \">=\" (*greater than or equal*), \">\" (*greater\r\n"
 621       " than*), \"<=\" (*less than or equal*), or \"<\" (*less than*).\r\n"
 622       " * * If any \"<`logical operator`>\" is specified without\r\n"
 623       " a \"<`relational operator`>\", it is ignored.\r\n"
 624       " * * If any \"<`relational operator`>\" is specified without\r\n"
 625       " a \"<`logical operator`>\", no logical operation is performed.\r\n"
 626       " * * All comparisons are unsigned.\r\n\r\n"
 627       " * The **output file** modifier redirects the command output to a file\r\n"
 628       " instead of the console.  The **output file** modifier is specified with\r\n"
 629       " the \"`@`\" (*commercial-at*) character, followed by a valid file name.\r\n\r\n"
 630       " **NOTE**: Modifiers may be specified in any order.  If multiple\r\n"
 631       " modifiers of the same type are specified, later modifiers override earlier\r\n"
 632       " modifiers. If the **device/unit name** comes *after* the search specifier,\r\n"
 633       " the search values will interpreted in the *radix of the CPU*, rather than\r\n"
 634       " of the device/unit.\r\n\r\n"
 635       " The \"<`object list`>\" argument consists of one or more of the following,\r\n"
 636       " separated by commas:\r\n\r\n"
 637        /***************** 80 character line width template *************************/
 638       "++register                the specified register\r\n"
 639       "++register[sub1-sub2]     the specified register array locations,\r\n"
 640       "++++++++                  starting at location sub1 up to and\r\n"
 641       "++++++++                  including location sub2\r\n"
 642       "++register[sub1/length]   the specified register array locations,\r\n"
 643       "++++++++                  starting at location sub1 up to but\r\n"
 644       "++++++++                  not including sub1+length\r\n"
 645       "++register[ALL]           all locations in the specified register\r\n"
 646       "++++++++                  array\r\n"
 647       "++register1-register2     all the registers starting at register1\r\n"
 648       "++++++++                  up to and including register2\r\n"
 649       "++address                 the specified location\r\n"
 650       "++address1-address2       all locations starting at address1 up to\r\n"
 651       "++++++++                  and including address2\r\n"
 652       "++address/length          all location starting at address up to\r\n"
 653       "++++++++                  but not including address+length\r\n"
 654       "++STATE                   all registers in the device\r\n"
 655       "++ALL                     all locations in the unit\r\n"
 656       "++$                       the last value displayed by an EXAMINE\r\n"
 657       "++++++++                  command interpreted as an address\r\n"
 658       "3Switches\n" //-NLOK
 659       "4Formatting Control\n" //-NLOK
 660       " Switches can be used to control the format of the displayed information:\r\n\r\n"
 661        /***************** 80 character line width template *************************/
 662       "5`-a`\n" //-NLOK
 663       " display as ASCII\r\n"
 664       "5`-c`\n" //-NLOK
 665       " display as character string\r\n"
 666       "5`-m`\n" //-NLOK
 667       " display as instruction mnemonics\r\n"
 668       "5`-o`\n" //-NLOK
 669       " display as octal\r\n"
 670       "5`-d`\n" //-NLOK
 671       " display as decimal\r\n"
 672       "5`-h`\n" //-NLOK
 673       " display as hexadecimal\r\n\r\n"
 674       "3Examples\n" //-NLOK
 675       "++ex 1000-1100                examine 1000 to 1100\r\n"
 676       "++de PC 1040                  set PC to 1040\r\n"
 677       "++ie 40-50                    interactively examine 40:50\r\n"
 678       "++ie >1000 40-50              interactively examine the subset\r\n"
 679       "+++++++++                     of locations 40:50 that are >1000\r\n"
 680       "++ex rx0 50060                examine 50060, RX unit 0\r\n"
 681       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\r\n"
 682       "++de all 0                    set main memory to 0\r\n"
 683       "++de &77>0 0                  set all addresses whose low order\r\n"
 684       "+++++++++                     bits are non-zero to 0\r\n"
 685       "++ex -m @memdump.txt 0-7777   dump memory to file\r\n\r\n"
 686       " * **NOTE**: To terminate an interactive command, simply type any bad value\r\n"
 687       "           (*e.g.* `XYZ`) when input is requested.\r\n"
 688 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
 689        /***************** 80 character line width template *************************/
 690       "2Evaluating Instructions\n" //-NLOK
 691       " The `EVAL` command evaluates a symbolic expression and returns the\r\n"
 692       " equivalent numeric value.\r\n\r\n"
 693        /***************** 80 character line width template *************************/
 694       "2Running A Simulated Program\n" //-NLOK
 695 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
 696       "3RUN\n" //-NLOK
 697       " The `RUN` command (*abbreviated* `RU`) resets all devices, deposits its\r\n"
 698       " argument, if given, in the PC (program counter), and starts execution.\r\n"
 699       " If no argument is given execution starts at the current PC.\r\n"
 700 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
 701       "3GO\n" //-NLOK
 702       " The `GO` command does *not* reset devices, deposits its argument (if\r\n"
 703       " given) in the PC, and starts execution.  If no argument is given,\r\n"
 704       " execution starts at the current PC (program counter).\r\n"
 705 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program Continuing_Execution"
 706       "3Continuing Execution\n" //-NLOK
 707       " The `CONTINUE` command (*abbreviated* `CONT` or `CO`) resumes execution\r\n"
 708       " (if execution was stopped, possibly due to hitting a breakpoint) at the\r\n"
 709       " current program counter without resetting any devices.\r\n"
 710 #define HLP_STEP        "*Commands Running_A_Simulated_Program Step_Execution"
 711       "3Step Execution\n" //-NLOK
 712       " The `STEP` command (*abbreviated* `S`) resumes execution at the current\r\n"
 713       " PC for the number of instructions given by its argument.  If no argument\r\n"
 714       " is supplied, one instruction is executed.\r\n"
 715       "4Switches\n" //-NLOK
 716       "5`-T`\n" //-NLOK
 717       " If the `STEP` command is invoked with the \"`-T`\" switch, the step\r\n"
 718       " command will cause execution to run for *microseconds* rather than\r\n"
 719       " instructions.\r\n"
 720 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
 721       "3NEXT\n" //-NLOK
 722       " The `NEXT` command (*abbreviated* `N`) resumes execution at the current PC\r\n"
 723       " for one instruction, attempting to execute *through* subroutine calls.\r\n"
 724       " If the next instruction to be executed is *not* a subroutine call, then\r\n"
 725       " one instruction is executed.\r\n"
 726 #define HLP_BOOT        "*Commands Running_A_Simulated_Program Booting_the_system"
 727       "3Booting the system\n" //-NLOK
 728       " The `BOOT` command (*abbreviated* `BO`) resets all devices and bootstraps\r\n"
 729       " the device and unit given by its argument. If no unit is supplied,\r\n"
 730       " unit `0` is bootstrapped.  The specified unit must be `ATTACH`'ed.\r\n\r\n"
 731       " When booting Multics, the boot device should always be `iom0`.\r\n"
 732       " Assuming a tape is attached to the `tape0` device, it will be bootstrapped\r\n"
 733       " into memory and the system will transfer control to the boot record.\r\n\r\n"
 734       " **Example**\r\n\r\n"
 735       "++; Boot Multics using iom0\r\n"
 736       "++boot iom0\r\n\r\n"
 737        /***************** 80 character line width template *************************/
 738       "2Stopping The Simulator\n" //-NLOK
 739       " The simulator runs until the simulated hardware encounters an error, or\r\n"
 740       " until the user forces a stop condition.\r\n"
 741       "3Simulator Detected Stop Conditions\n" //-NLOK
 742       " These simulator-detected conditions stop simulation:\r\n\r\n"
 743       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\r\n\r\n"
 744       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\r\n"
 745       "+++operation, and the device stop-on-I/O-error flag is set, simulation\r\n"
 746       "+++usually stops.\r\n\r\n"
 747       "++-  Processor condition.  Certain processor conditions can stop\r\n"
 748       "+++the simulation.\r\n"
 749       "3User Specified Stop Conditions\n" //-NLOK
 750       " Typing the interrupt character stops simulation.  The interrupt character\r\n"
 751       " is defined by the `WRU` (*Where aRe yoU*) console option, and is initially\r\n"
 752       " set to `005` (`^E`).\r\n\r\n"
 753        /***************** 80 character line width template *************************/
 754 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 755 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 756       "4Breakpoints\n" //-NLOK
 757       " The simulator offers breakpoint capability for debugging. Users may define\r\n"
 758       " breakpoints of different types, identified by letter (for example, `E`\r\n"
 759       " for *execution*, `R` for *read*, `W` for *write*, etc).\r\n\r\n"
 760       " Associated with each breakpoint is a count and, optionally, one or more\r\n"
 761       " actions.  Each time a breakpoint occurs, the associated count\r\n"
 762       " is *decremented*.  If the count is less than or equal to `0`, the breakpoint\r\n"
 763       " occurs; otherwise, it is deferred.  When the breakpoint occurs, any\r\n"
 764       " optional actions are automatically executed.\r\n\r\n"
 765       " A breakpoint is set by the `BREAK` (or `SET BREAK`) command:\r\n\r\n"
 766       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\r\n\r\n"
 767       " If no type is specified, the default breakpoint type (`E`, *execution*) is\r\n"
 768       " used.  If no address range is specified, the current PC is used.  As\r\n"
 769       " with `EXAMINE` and `DEPOSIT`, an address range may be a single address, a\r\n"
 770       " range of addresses low-high, or a relative range of address/length.\r\n"
 771        /***************** 80 character line width template *************************/
 772       "5Displaying Breakpoints\n" //-NLOK
 773       " Currently set breakpoints can be displayed with the `SHOW BREAK` command:\r\n\r\n"
 774       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\r\n\r\n"
 775       " Locations with breakpoints of the specified type are displayed.\r\n\r\n"
 776       " The \"`-C`\" switch displays the selected breakpoint(s) formatted as\r\n"
 777       " commands which may be subsequently used to establish the same\r\n"
 778       " breakpoint(s).\r\n\r\n"
 779       "5Removing Breakpoints\n" //-NLOK
 780       " Breakpoints can be cleared by the `NOBREAK` or the `SET NOBREAK` commands.\r\n"
 781       "5Examples\n" //-NLOK
 782       " The following examples illustrate breakpoint usage:\r\n\r\n"
 783       "++BREAK                      set E break at current PC\r\n"
 784       "++BREAK -e 200               set E break at 200\r\n"
 785       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\r\n"
 786       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\r\n"
 787       "+++++++++D MQ 0\r\n"
 788       "++BREAK 100;                 delete action on break at 100\r\n\r\n"
 789        /***************** 80 character line width template *************************/
 790       "2Connecting and Disconnecting Devices\n" //-NLOK
 791       " Units are simulated as files on the host file system.  Before using any\r\n"
 792       " simulated unit, the user must specify the file to be accessed by that unit.\r\n"
 793 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices Attaching_devices"
 794       "3Attaching devices\n" //-NLOK
 795       " The `ATTACH` (*abbreviation* `AT`) command associates a unit and a file:\r\n\r\n"
 796       "++ATTACH <unit> <filename>\r\n\r\n"
 797       " Some devices have more detailed or specific help available with:\r\n\r\n"
 798       "++HELP <device> ATTACH\r\n\r\n"
 799       "4Switches\n" //-NLOK
 800       "5`-n`\n" //-NLOK
 801       " If the \"`-n`\" switch is specified when `ATTACH` is executed, a new\r\n"
 802       " file will be created when the filename specified does not exist, or an\r\n"
 803       " existing file will have it's size truncated to zero, and an appropriate\r\n"
 804       " message is printed.\r\n"
 805       "5`-e`\n" //-NLOK
 806       " If the file does not exist, and the \"`-e`\" switch *was not* specified,\r\n"
 807       " a new file is created, and an appropriate message is printed.  If\r\n"
 808       " the \"`-e`\" switch *was* specified, a new file is *not* created, and an\r\n"
 809       " error message is printed.\r\n"
 810       "5`-r`\n" //-NLOK
 811       " If the \"`-r`\" switch is specified, or the file is write protected by\r\n"
 812       " host operating system, `ATTACH` tries to open the file in read only mode.\r\n"
 813       " If the file does not exist, or the unit does not support read only\r\n"
 814       " operation, an error occurs.  Input-only devices, such as card readers, or\r\n"
 815       " storage devices with write locking switches, such as disks or tapes,\r\n"
 816       " support read only operation - other devices do not.  If a file is\r\n"
 817       " attached read only, its contents can be examined but not modified.\r\n"
 818       "5`-q`\n" //-NLOK
 819       " If the \"`-q`\" switch is specified when creating a new file (\"`-n`\")\r\n"
 820       " or opening one read only (\"`-r`\"), the message announcing this fact\r\n"
 821       " is suppressed.\r\n"
 822       "5`-f`\n" //-NLOK
 823       " For simulated magnetic tapes, the `ATTACH` command can specify the format\r\n"
 824       " of the attached tape image file:\r\n\r\n"
 825       "++ATTACH -f <tape_unit> <format> <filename>\r\n\r\n"
 826       " * The currently supported magnetic tape image file formats are:\r\n\r\n"
 827       " |                  |                                                      |\r\n"
 828       " | ----------------:|:---------------------------------------------------- |\r\n"
 829       " | \"**`SIMH`**\"   | The **SIMH** / **DPS8M** native portable tape format |\r\n"
 830       " | \"**`E11`**\"    | The *D Bit* **Ersatz-11** simulator format           |\r\n"
 831       " | \"**`TPC`**\"    | The **TPC** format (*used by _SIMH_ prior to V2.3*)  |\r\n"
 832       " | \"**`P7B`**\"    | The **Paul Pierce** `7`-track tape archive format    |\r\n\r\n"
 833        /***************** 80 character line width template *************************/
 834       " * The default tape format can also be specified with the `SET` command\r\n"
 835       " prior to using the `ATTACH` command:\r\n\r\n"
 836       "++SET <tape_unit> FORMAT=<format>\r\n"
 837       "++ATTACH <tape_unit> <filename>\r\n\r\n"
 838       " * The format of a currently attached tape image can be displayed with\r\n"
 839       "   the `SHOW FORMAT` command:\r\n\r\n"
 840       "++SHOW <unit> FORMAT\r\n\r\n"
 841       " **Examples**\r\n\r\n"
 842       " The following example illustrates common `ATTACH` usage:\r\n"
 843       "++; Associate the tape image file \"12.8MULTICS.tap\" with the tape0 unit\r\n"
 844       "++; in read-only mode, where tape0 corresponds to the first tape device.\r\n"
 845       "++ATTACH -r tape0 12.8MULTICS.tap\r\n\r\n"
 846       "++; Associate the disk image file \"root.dsk\" with the disk0 unit.\r\n"
 847       "++; The disk0 unit corresponds to the first disk device.\r\n"
 848       "++ATTACH disk0 root.dsk\r\n\r\n"
 849        /***************** 80 character line width template *************************/
 850 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices Detaching_devices"
 851       "3Detaching devices\n" //-NLOK
 852       " The `DETACH` (*abbreviation* `DET`) command breaks the association between\r\n"
 853       " a unit and its backing file or device:\r\n\r\n"
 854       "++DETACH ALL             Detach all units\r\n"
 855       "++DETACH <unit>          Detach specified unit\r\n\r\n"
 856       " * **NOTE:** The `EXIT` command performs an automatic `DETACH ALL`.\r\n"
 857 #define HLP_SET         "*Commands SET"
 858       "2SET\n" //-NLOK
 859        /***************** 80 character line width template *************************/
 860 #define HLP_SET_LOG    "*Commands SET Logging"
 861       "3Logging\n" //-NLOK
 862       " Interactions with the simulator session can be recorded to a log file.\r\n\r\n"
 863       "+SET LOG log_file            Specify the log destination\r\n"
 864       "++++++++                     (STDOUT, DEBUG, or filename)\r\n"
 865       "+SET NOLOG                   Disables any currently active logging\r\n"
 866       "4Switches\n" //-NLOK
 867       "5`-N`\n" //-NLOK
 868       " By default, log output is written at the *end* of the specified log file.\r\n"
 869       " A new log file can created if the \"`-N`\" switch is used on the command\r\n"
 870       " line.\r\n\r\n"
 871       "5`-B`\n" //-NLOK
 872       " By default, log output is written in *text* mode.  The log file can be\r\n"
 873       " opened for *binary* mode writing if the \"`-B`\" switch is used on the\r\n"
 874       " command line.\r\n"
 875 #define HLP_SET_DEBUG  "*Commands SET Debug_Messages"
 876        /***************** 80 character line width template *************************/
 877       "3Debug Messages\n" //-NLOK
 878       "+SET DEBUG debug_file        Specify the debug destination\r\n"
 879       "++++++++                     (STDOUT, STDERR, LOG, or filename)\r\n"
 880       "+SET NODEBUG                 Disables any currently active debug output\r\n"
 881       "4Switches\n" //-NLOK
 882       " Debug message output contains a timestamp which indicates the number of\r\n"
 883       " simulated instructions which have been executed prior to the debug event.\r\n\r\n"
 884       " Debug message output can be enhanced to contain additional, potentially\r\n"
 885       " useful information.\r\n\r\n\r\n"
 886       " **NOTE**: If neither \"`-T`\" or \"`-A`\" is specified, \"`-T`\" is implied.\r\n"
 887       "5`-T`\n" //-NLOK
 888       " The \"`-T`\" switch causes debug output to contain a time of day displayed\r\n"
 889       " as `hh:mm:ss.msec`.\r\n"
 890       "5`-A`\n" //-NLOK
 891       " The \"`-A`\" switch causes debug output to contain a time of day displayed\r\n"
 892       " as `seconds.msec`.\r\n"
 893       "5`-R`\n" //-NLOK
 894       " The \"`-R`\" switch causes timing to be relative to the start of debugging.\r\n"
 895       "5`-P`\n" //-NLOK
 896       " The \"`-P`\" switch adds the output of the PC (program counter) to each\r\n"
 897       " debug message.\r\n"
 898       "5`-N`\n" //-NLOK
 899       " The \"`-N`\" switch causes a new (empty) file to be written to.\r\n"
 900       " (The default is to append to an existing debug log file).\r\n"
 901       "5`-D`\n" //-NLOK
 902       " The \"`-D`\" switch causes data blob output to also display the data\r\n"
 903       " as **`RADIX-50`** characters.\r\n"
 904       "5`-E`\n" //-NLOK
 905       " The \"`-E`\" switch causes data blob output to also display the data\r\n"
 906       " as \"**EBCDIC**\" characters.\r\n"
 907        /***************** 80 character line width template *************************/
 908 #define HLP_SET_ENVIRON "*Commands SET Environment_Variables"
 909       "3Environment Variables\n" //-NLOK
 910       "+SET ENVIRONMENT NAME=val    Set environment variable\r\n"
 911       "+SET ENVIRONMENT NAME        Clear environment variable\r\n"
 912 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
 913       "3Command Status Trap Dispatching\n" //-NLOK
 914       "+SET ON                      Enables error checking command execution\r\n"
 915       "+SET NOON                    Disables error checking command execution\r\n"
 916       "+SET ON INHERIT              Enables inheritance of ON state and actions\r\n"
 917       "+SET ON NOINHERIT            Disables inheritance of ON state and actions\r\n"
 918 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
 919       "3Command Execution Display\n" //-NLOK
 920       "+SET VERIFY                  Enables display of processed script commands\r\n"
 921       "+SET VERBOSE                 Enables display of processed script commands\r\n"
 922       "+SET NOVERIFY                Disables display of processed script commands\r\n"
 923       "+SET NOVERBOSE               Disables display of processed script commands\r\n"
 924 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
 925       "3Command Error Status Display\n" //-NLOK
 926       "+SET MESSAGE                 Re-enables display of script error messages\r\n"
 927       "+SET NOMESSAGE               Disables display of script error messages\r\n"
 928 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
 929       "3Command Output Display\n" //-NLOK
 930       "+SET QUIET                   Disables suppression of some messages\r\n"
 931       "+SET NOQUIET                 Re-enables suppression of some messages\r\n"
 932 #define HLP_SET_LOCALOPC "*Commands SET Local_Operator_Console"
 933       "3Local Operator Console\n" //-NLOK
 934       "+SET LOCALOPC                Enables local operator console\r\n"
 935       "+SET NOLOCALOPC              Disables local operator console\r\n"
 936 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
 937       "3Command Prompt\n" //-NLOK
 938       "+SET PROMPT \"string\"         Sets an alternate simulator prompt string\r\n"
 939       "3Device and Unit Settings\n" //-NLOK
 940       "+SET <dev> OCT|DEC|HEX       Set device display radix\r\n"
 941       "+SET <dev> ENABLED           Enable device\r\n"
 942       "+SET <dev> DISABLED          Disable device\r\n"
 943       "+SET <dev> DEBUG{=arg}       Set device debug flags\r\n"
 944       "+SET <dev> NODEBUG={arg}     Clear device debug flags\r\n"
 945       "+SET <dev> arg{,arg...}      Set device parameters\r\n"
 946       "+SET <unit> ENABLED          Enable unit\r\n"
 947       "+SET <unit> DISABLED         Disable unit\r\n"
 948       "+SET <unit> arg{,arg...}     Set unit parameters\r\n"
 949       "+HELP <dev> SET              Displays any device specific SET commands\r\n"
 950       " \r\n\r\n"
 951       " See the Omnibus documentation for a complete SET command reference.\r\n"
 952        /***************** 80 character line width template *************************/
 953 #define HLP_SHOW        "*Commands SHOW"
 954       "2SHOW\n" //-NLOK
 955       "+SH{OW} B{UILDINFO}               Show build-time compilation information\r\n"
 956       "+SH{OW} CL{OCKS}                  Show wall clock and timer information\r\n"
 957       "+SH{OW} C{ONFIGURATION}           Show simulator configuration\r\n"
 958       "+SH{OW} D{EFAULT_BASE_SYSTEM}     Show default base system script\r\n"
 959       "+SH{OW} DEV{ICES}                 Show devices\r\n"
 960       "+SH{OW} H{INTS}                   Show configuration hints\r\n"
 961       "+SH{OW} M{ODIFIERS}               Show SET commands for all devices\r\n"
 962       "+SH{OW} O{N}                      Show ON condition actions\r\n"
 963       "+SH{OW} P{ROM}                    Show CPU ID PROM initialization data\r\n"
 964       "+SH{OW} Q{UEUE}                   Show event queue\r\n"
 965       "+SH{OW} S{HOW}                    Show SHOW commands for all devices\r\n"
 966       "+SH{OW} T{IME}                    Show simulated timer\r\n"
 967       "+SH{OW} VE{RSION}                 Show simulator version\r\n"
 968       "+H{ELP} <dev> SHOW                Show device-specific SHOW commands\r\n"
 969       "+SH{OW} <dev> {arg,...}           Show device parameters\r\n"
 970       "+SH{OW} <dev> DEBUG               Show device debug flags\r\n"
 971       "+SH{OW} <dev> MODIFIERS           Show device modifiers\r\n"
 972       "+SH{OW} <dev> RADIX               Show device display radix\r\n"
 973       "+SH{OW} <dev> SHOW                Show device SHOW commands\r\n"
 974       "+SH{OW} <unit> {arg,...}          Show unit parameters\r\n\r\n"
 975       " See the Omnibus documentation for a complete SHOW command reference.\r\n\r\n"
 976 #define HLP_SHOW_CONFIG         "*Commands SHOW"
 977 #define HLP_SHOW_DEVICES        "*Commands SHOW"
 978 #define HLP_SHOW_FEATURES       "*Commands SHOW"
 979 #define HLP_SHOW_QUEUE          "*Commands SHOW"
 980 #define HLP_SHOW_TIME           "*Commands SHOW"
 981 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
 982 #define HLP_SHOW_NAMES          "*Commands SHOW"
 983 #define HLP_SHOW_SHOW           "*Commands SHOW"
 984 #define HLP_SHOW_VERSION        "*Commands SHOW"
 985 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
 986 #define HLP_SHOW_PROM           "*Commands SHOW"
 987 #define HLP_SHOW_HINTS          "*Commands SHOW"
 988 #define HLP_SHOW_DBS            "*Commands SHOW"
 989 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
 990 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
 991 #define HLP_SHOW_REMOTE         "*Commands SHOW"
 992 #define HLP_SHOW_BREAK          "*Commands SHOW"
 993 #define HLP_SHOW_LOG            "*Commands SHOW"
 994 #define HLP_SHOW_DEBUG          "*Commands SHOW"
 995 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
 996 #define HLP_SHOW_ON             "*Commands SHOW"
 997 #define HLP_SHOW_SEND           "*Commands SHOW"
 998 #define HLP_SHOW_EXPECT         "*Commands SHOW"
 999 #define HLP_HELP                "*Commands HELP"
1000        /***************** 80 character line width template *************************/
1001       "2HELP\n" //-NLOK
1002       "+H{ELP}                      Show this message\r\n"
1003       "+H{ELP} <command>            Show help for command\r\n"
1004       "+H{ELP} <dev>                Show help for device\r\n"
1005       "+H{ELP} <dev> REGISTERS      Show help for device register variables\r\n"
1006       "+H{ELP} <dev> ATTACH         Show help for device specific ATTACH command\r\n"
1007       "+H{ELP} <dev> SET            Show help for device specific SET commands\r\n"
1008       "+H{ELP} <dev> SHOW           Show help for device specific SHOW commands\r\n"
1009       "+H{ELP} <dev> <command>      Show help for device specific <command> command\r\n"
1010        /***************** 80 character line width template *************************/
1011       "2Altering The Simulated Configuration\n" //-NLOK
1012       " The \"SET <device> DISABLED\" command removes a device from the configuration.\r\n"
1013       " A `DISABLED` device is invisible to running programs.  The device can still\r\n"
1014       " be `RESET`, but it cannot be `ATTACH`ed, `DETACH`ed, or `BOOT`ed.\r\n\r\n"
1015       " The \"SET <device> ENABLED\" command restores a disabled device to a\r\n"
1016       " configuration.\r\n\r\n"
1017       " Most multi-unit devices allow units to be enabled or disabled:\r\n\r\n"
1018       "++SET <unit> ENABLED\r\n"
1019       "++SET <unit> DISABLED\r\n\r\n"
1020       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\r\n\r\n"
1021        /***************** 80 character line width template *************************/
1022 #define HLP_DO          "*Commands Executing_Command_Files Processing_Command_Files"
1023       "2Executing Command Files\n" //-NLOK
1024       "3Processing Command Files\n" //-NLOK
1025       " The simulator can invoke another script file with the \"`DO`\" command:\r\n\r\n"
1026       "++DO <filename> {arguments...}       execute commands in specified file\r\n\r\n"
1027       " The \"`DO`\" command allows command files to contain substitutable\r\n"
1028       " arguments. The string \"`%%n`\", where \"`n`\" is a number\r\n"
1029       " between \"`1`\" and \"`9`\", is replaced with argument \"`n`\" from\r\n"
1030       " the \"`DO`\" command line. (*i.e.* \"`%%0`\", \"`%%1`\", \"`%%2`\", etc.).\r\n"
1031       " The string \"`%%0`\" is replaced with \"<`filename`>\"\r\n"
1032       " The sequences \"`\\%%`\" and \"`\\\\`\" are replaced with the literal\r\n"
1033       " characters \"`%%`\" and \"`\\`\", respectively. Arguments with spaces must\r\n"
1034       " be enclosed in matching single or double quotation marks.\r\n\r\n"
1035       " * **NOTE**: Nested \"`DO`\" commands are supported, up to ten invocations\r\n"
1036       " deep.\r\n\r\n"
1037       "4Switches\n" //-NLOK
1038       "5`-v`\n\n" //-NLOK
1039       " If the switch \"`-v`\" is specified, commands in the command file are\r\n"
1040       " echoed *before* they are executed.\r\n\r\n"
1041       "5`-e`\n\n" //-NLOK
1042       " If the switch \"`-e`\" is specified, command processing (including nested\r\n"
1043       " command invocations) will be aborted if any command error is encountered.\r\n"
1044       " (A simulation stop **never** aborts processing; use `ASSERT` to catch\r\n"
1045       " unexpected stops.) Without this switch, all errors except `ASSERT` failures\r\n"
1046       " will be ignored, and command processing will continue.\r\n\r\n"
1047       "5`-o`\n\n" //-NLOK
1048       " If the switch \"`-o`\" is specified, the `ON` conditions and actions from\r\n"
1049       " the calling command file will be inherited by the command file being\r\n"
1050       " invoked.\r\n"
1051       "5`-q`\n\n" //-NLOK
1052       " If the switch \"`-q`\" is specified, *quiet mode* will be explicitly\r\n"
1053       " enabled for the called command file, otherwise the *quiet mode* setting\r\n"
1054       " is inherited from the calling context.\r\n"
1055        /***************** 80 character line width template *************************/
1056 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
1057       "3GOTO\n" //-NLOK
1058       " Commands in a command file execute in sequence until either an error\r\n"
1059       " trap occurs (when a command completes with an error status), or when an\r\n"
1060       " explicit request is made to start command execution elsewhere with\r\n"
1061       " the `GOTO` command:\r\n\r\n"
1062       "++GOTO <label>\r\n\r\n"
1063       " * Labels are lines in a command file which the first non-whitespace\r\n"
1064       " character is a \"`:`\".\r\n"
1065       " * The target of a `GOTO` is the first matching label in the current `DO`\r\n"
1066       " command file which is encountered.\r\n\r\n"
1067       " **Example**\r\n\r\n"
1068       " The following example illustrates usage of the `GOTO` command (by\r\n"
1069       " creating an infinite loop):\r\n\r\n"
1070       "++:Label\r\n"
1071       "++:: This is a loop.\r\n"
1072       "++GOTO Label\r\n\r\n"
1073 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
1074        /***************** 80 character line width template *************************/
1075       "3RETURN\n" //-NLOK
1076       " The `RETURN` command causes the current procedure call to be restored to\r\n"
1077       " the calling context, possibly returning a specific return status.\r\n"
1078       " If no return status is specified, the return status from the last command\r\n"
1079       " executed will be returned.  The calling context may have `ON` traps defined\r\n"
1080       " which may redirect command flow in that context.\r\n\r\n"
1081       "++RETURN                 return from command file with last command status\r\n"
1082       "++RETURN {-Q} <status>   return from command file with specific status\r\n\r\n"
1083       " * The status return can be any numeric value or one of the standard SCPE_\r\n"
1084       " condition names.\r\n\r\n"
1085       " * The \"`-Q`\" switch on the `RETURN` command will cause the specified\r\n"
1086       " status to be returned, but normal error status message printing to be\r\n"
1087       " suppressed.\r\n\r\n"
1088       " **Condition Names**\r\n\r\n"
1089       " The available standard SCPE_ condition names and their meanings are:\r\n\r\n"
1090       " | Name    | Meaning                         | Name    | Meaning                             |\r\n"
1091       " | ------- | --------------------------------| ------- | ----------------------------------- |\r\n"
1092       " | NXM     | Address space exceeded          | UNATT   | Unit not attached                   |\r\n"
1093       " | IOERR   | I/O error                       | CSUM    | Checksum error                      |\r\n"
1094       " | FMT     | Format error                    | NOATT   | Unit not attachable                 |\r\n"
1095       " | OPENERR | File open error                 | MEM     | Memory exhausted                    |\r\n"
1096       " | ARG     | Invalid argument                | STEP    | Step expired                        |\r\n"
1097       " | UNK     | Unknown command                 | RO      | Read only argument                  |\r\n"
1098       " | INCOMP  | Command not completed           | STOP    | Simulation stopped                  |\r\n"
1099       " | EXIT    | Goodbye                         | TTIERR  | Console input I/O error             |\r\n"
1100       " | TTOERR  | Console output I/O error        | EOF     | End of file                         |\r\n"
1101       " | REL     | Relocation error                | NOPARAM | No settable parameters              |\r\n"
1102       " | ALATT   | Unit already attached           | TIMER   | Hardware timer error                |\r\n"
1103       " | SIGERR  | Signal handler setup error      | TTYERR  | Console terminal setup error        |\r\n"
1104       " | NOFNC   | Command not allowed             | UDIS    | Unit disabled                       |\r\n"
1105       " | NORO    | Read only operation not allowed | INVSW   | Invalid switch                      |\r\n"
1106       " | MISVAL  | Missing value                   | 2FARG   | Too few arguments                   |\r\n"
1107       " | 2MARG   | Too many arguments              | NXDEV   | Non-existent device                 |\r\n"
1108       " | NXUN    | Non-existent unit               | NXREG   | Non-existent register               |\r\n"
1109       " | NXPAR   | Non-existent parameter          | NEST    | Nested DO command limit exceeded    |\r\n"
1110       " | IERR    | Internal error                  | MTRLNT  | Invalid magtape record length       |\r\n"
1111       " | LOST    | Console Telnet connection lost  | TTMO    | Console Telnet connection timed out |\r\n"
1112       " | STALL   | Console Telnet output stall     | AFAIL   | Assertion failed                    |\r\n"
1113       " | INVREM  | Invalid remote console command  |         |                                     |\r\n"
1114       "\r\n\r\n"
1115 #define HLP_SHIFT       "*Commands Executing_Command_Files Shift_Parameters"
1116       "3Shift Parameters\n" //-NLOK
1117       " Shift the command files positional parameters\r\n"
1118 #define HLP_CALL        "*Commands Executing_Command_Files Call_a_subroutine"
1119       "3Call a subroutine\n" //-NLOK
1120       " Control can be transferred to a labeled subroutine using `CALL`.\r\n\r\n"
1121       " **Example**\r\n\r\n"
1122       "++CALL routine\r\n"
1123       "++BYE\r\n"
1124       "++\r\n"
1125       "++:routine\r\n"
1126       "++ECHO routine called\r\n"
1127       "++RETURN\r\n\r\n"
1128 #define HLP_ON          "*Commands Executing_Command_Files ON"
1129       "3ON\n" //-NLOK
1130       " The `ON` command performs actions after a condition, or clears a condition.\r\n"
1131       "++ON <condition> <action>  Perform action after condition\r\n"
1132       "++ON <condition>           Clears action of specified condition\r\n"
1133 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1134 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1135        /***************** 80 character line width template *************************/
1136       "3PROCEED or IGNORE\n" //-NLOK
1137       " The `PROCEED` (or `IGNORE`) command does nothing.  It is potentially\r\n"
1138       " useful as a placeholder for any `ON` action condition that should be\r\n"
1139       " explicitly ignored, allowing command file execution to continue without\r\n"
1140       " taking any specific action.\r\n"
1141 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1142        /***************** 80 character line width template *************************/
1143       "3Displaying Arbitrary Text\n" //-NLOK
1144       " The `ECHO` command is a useful way of annotating command files.  `ECHO`\r\n"
1145       " prints out its arguments to the console (and to any applicable log file):\r\n\r\n"
1146       "++ECHO <string>      Output string to console\r\n\r\n"
1147       " **NOTE**: If no arguments are specified, `ECHO` prints a blank line.\r\n"
1148       " This may be used to provide spacing for console messages or log file\r\n"
1149       " output.\r\n"
1150        /***************** 80 character line width template *************************/
1151 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Assertions"
1152       "3Testing Assertions\n" //-NLOK
1153       " The `ASSERT` command tests a simulator state condition and halts command\r\n"
1154       " file execution if the condition is false:\r\n\r\n"
1155       "++ASSERT <Simulator State Expressions>\r\n\r\n"
1156       " * If the indicated expression evaluates to false, the command completes\r\n"
1157       " with an `AFAIL` condition.  By default, when a command file encounters a\r\n"
1158       " command which returns the `AFAIL` condition, it will exit the running\r\n"
1159       " command file with the `AFAIL` status to the calling command file.  This\r\n"
1160       " behavior can be changed with the `ON` command as well as switches to the\r\n"
1161       " invoking `DO` command.\r\n\r\n"
1162       " **Examples**\r\n\r\n"
1163       " The command file below might be used to bootstrap a hypothetical system\r\n"
1164       " that halts after the initial load from disk. The `ASSERT` command can then\r\n"
1165       " be used to confirm that the load completed successfully by examining the\r\n"
1166       " CPU's \"`A`\" register for the expected value:`\r\n\r\n"
1167       "++; Example INI file\r\n"
1168       "++BOOT\r\n"
1169       "++; A register contains error code; 0 = good boot\r\n"
1170       "++ASSERT A=0\r\n"
1171       "++RUN\r\n\r\n"
1172        /***************** 80 character line width template *************************/
1173       " * In the above example, if the \"`A`\" register is *not* `0`,\r\n"
1174       " the \"`ASSERT A=0`\" command will be displayed to the user, and the\r\n"
1175       " command file will be aborted with an \"`Assertion failed`\" message.\r\n"
1176       " Otherwise, the command file will continue to bring up the system.\r\n\r\n"
1177       " * See the **`IF`** command documentation for more information and details\r\n"
1178       " regarding simulator state expressions.\r\n\r\n"
1179 #define HLP_IF          "*Commands Executing_Command_Files Testing_Conditions"
1180       "3Testing Conditions\n" //-NLOK
1181       " The `IF` command tests a simulator state condition and executes additional\r\n"
1182       " commands if the condition is true:\r\n\r\n"
1183       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommand}...\r\n\r\n"
1184       " **Examples**\r\n\r\n"
1185       " The command file below might be used to bootstrap a hypothetical system\r\n"
1186       " that halts after the initial load from disk. The `IF` command can then\r\n"
1187       " be used to confirm that the load completed successfully by examining the\r\n"
1188       " CPU's \"`A`\" register for an expected value:\r\n\r\n"
1189       "++; Example INI file\r\n"
1190       "++BOOT\r\n"
1191       "++; A register contains error code; 0 = good boot\r\n"
1192       "++IF NOT A=0 echo Boot failed - Failure Code ; EX A; exit AFAIL\r\n"
1193       "++RUN\r\n\r\n"
1194        /***************** 80 character line width template *************************/
1195       " * In the above example, if the \"`A`\" register is *not* `0`, the\r\n"
1196       " message \"`Boot failed - Failure Code `\" will be displayed, the contents\r\n"
1197       " of the \"`A`\" register will be displayed, and the command file will be\r\n"
1198       " aborted with an \"`Assertion failed`\" message.  Otherwise, the command\r\n"
1199       " file will continue to bring up the system.\r\n"
1200       "4Conditional Expressions\n" //-NLOK
1201       " The `IF` and `ASSERT` commands evaluate the following two different forms\r\n"
1202       " of conditional expressions.\r\n\r\n"
1203       "5Simulator State Expressions\n" //-NLOK
1204       "  &nbsp;\r\n \r\n"
1205       " The values of simulator registers can be evaluated with:\r\n\r\n"
1206       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\r\n\r\n"
1207       " * If \"<`dev`>\" is not specified, `CPU` is assumed.  \"<`reg`>\" is a\r\n"
1208       " register belonging to the indicated device.\r\n"
1209       " * The \"<`addr`>\" is an address in the address space of the indicated\r\n"
1210       " device.\r\n"
1211       " * The \"<`conditional-op`>\" and optional \"<`logical-op`>\" are\r\n"
1212       " the same as those used for \"search specifiers\" by the `EXAMINE` and\r\n"
1213       " `DEPOSIT` commands.\r\n"
1214       " The \"<`value`>\" is expressed in the radix specified for \"<`reg`>\",\r\n"
1215       " not in the radix for the device when referencing a register; when an\r\n"
1216       " address is referenced the device radix is used as the default.\r\n\r\n"
1217       " * If \"<`logical-op`>\" and \"<`value`>\" are specified, the target\r\n"
1218       " register value is first altered as indicated.  The result is then compared\r\n"
1219       " to the \"<`value`>\" via the \"<`conditional-op`>\".\r\n"
1220       " * * If the result is *true*, the command(s) are executed before proceeding\r\n"
1221       " to the next line in the command file.\r\n"
1222       " * * If the result is *false*, the next command in the command file is\r\n"
1223       " processed.\r\n\r\n"
1224       "5String Comparison Expressions\n" //-NLOK
1225       "  &nbsp;\r\n \r\n"
1226       " String Values can be compared with:\r\n\r\n"
1227       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\r\n\r\n"
1228       " * The \"`-i`\" switch, if present, causes a comparison to be case\r\n"
1229       "   insensitive.\r\n"
1230       " * The \"<`string1`>\" and \"<`string2`>\" arguments are quoted string\r\n"
1231       "   values which may have environment variables substituted as desired.\r\n"
1232       " * The \"<`compare-op`>\" may be one of:\r\n\r\n"
1233       " |                |                        |\r\n"
1234       " | --------------:|:---------------------- |\r\n"
1235       " | \"**`==`**\"     |  equal                 |\r\n"
1236       " | \"**`EQU`**\"    |  equal                 |\r\n"
1237       " | \"**`!=`**\"     |  not equal             |\r\n"
1238       " | \"**`NEQ`**\"    |  not equal             |\r\n"
1239       " | \"**<**\"        |  less than             |\r\n"
1240       " | \"**`LSS`**\"    |  less than             |\r\n"
1241       " | \"**<=**\"       |  less than or equal    |\r\n"
1242       " | \"**`LEQ`**\"    |  less than or equal    |\r\n"
1243       " | \"**>**\"        |  greater than          |\r\n"
1244       " | \"**`GTR`**\"    |  greater than          |\r\n"
1245       " | \"**>=**\"       |  greater than or equal |\r\n"
1246       " | \"**`GEQ`**\"    |  greater than or equal |\r\n"
1247       " * **NOTE**: Comparisons are *generic*.  This means that if\r\n"
1248       " both \"<`string1`>\" and \"<`string2`>\" are comprised of all numeric\r\n"
1249       " digits, then the strings are converted to numbers and a numeric\r\n"
1250       " comparison is performed.  For example, the comparison\r\n"
1251       " '`\"+1\"` `EQU` `\"1\"`' evaluates to *true*.\r\n"
1252        /***************** 80 character line width template *************************/
1253 #define HLP_EXIT        "*Commands Exiting_the_Simulator"
1254       "2Exiting the Simulator\n" //-NLOK
1255       " The `EXIT` command (*synonyms* `QUIT` *and* `BYE`) exits the simulator,\r\n"
1256       " returning control to the host operating system.\r\n"
1257        /***************** 80 character line width template *************************/
1258 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1259       "2Executing System Commands\n" //-NLOK
1260       " * The simulator can execute host operating system commands with\r\n"
1261       " the \"`!`\" (*spawn*) command.\r\n\r\n"
1262       " |                         |                                             |\r\n"
1263       " |:----------------------- |:------------------------------------------- |\r\n"
1264       " | \"**`!`**\"             | Spawn the hosts default command interpreter |\r\n"
1265       " | \"**`!`** <`command`>\" | Execute the host operating system `command` |\r\n\r\n"
1266       " * **NOTE**: The *exit status* from the command which was executed is set\r\n"
1267       " as the *command completion status* for the \"`!`\" command.  This may\r\n"
1268       " influence any enabled `ON` condition traps.\r\n" ;
1269        /***************** 80 character line width template *************************/
1270 
1271 static CTAB cmd_table[] = {
1272     { "RESET",    &reset_cmd,  0,         HLP_RESET     },
1273     { "EXAMINE",  &exdep_cmd,  EX_E,      HLP_EXAMINE   },
1274     { "IEXAMINE", &exdep_cmd,  EX_E+EX_I, HLP_IEXAMINE  },
1275     { "DEPOSIT",  &exdep_cmd,  EX_D,      HLP_DEPOSIT   },
1276     { "IDEPOSIT", &exdep_cmd,  EX_D+EX_I, HLP_IDEPOSIT  },
1277     { "EVALUATE", &eval_cmd,   0,         HLP_EVALUATE  },
1278     { "RUN",      &run_cmd,    RU_RUN,    HLP_RUN,      NULL, &run_cmd_message },
1279     { "GO",       &run_cmd,    RU_GO,     HLP_GO,       NULL, &run_cmd_message },
1280     { "STEP",     &run_cmd,    RU_STEP,   HLP_STEP,     NULL, &run_cmd_message },
1281     { "NEXT",     &run_cmd,    RU_NEXT,   HLP_NEXT,     NULL, &run_cmd_message },
1282     { "CONTINUE", &run_cmd,    RU_CONT,   HLP_CONTINUE, NULL, &run_cmd_message },
1283     { "BOOT",     &run_cmd,    RU_BOOT,   HLP_BOOT,     NULL, &run_cmd_message },
1284     { "BREAK",    &brk_cmd,    SSH_ST,    HLP_BREAK     },
1285     { "NOBREAK",  &brk_cmd,    SSH_CL,    HLP_NOBREAK   },
1286     { "ATTACH",   &attach_cmd, 0,         HLP_ATTACH    },
1287     { "DETACH",   &detach_cmd, 0,         HLP_DETACH    },
1288     { "EXIT",     &exit_cmd,   0,         HLP_EXIT      },
1289     { "QUIT",     &exit_cmd,   0,         NULL          },
1290     { "BYE",      &exit_cmd,   0,         NULL          },
1291     { "SET",      &set_cmd,    0,         HLP_SET       },
1292     { "SHOW",     &show_cmd,   0,         HLP_SHOW      },
1293     { "DO",       &do_cmd,     1,         HLP_DO        },
1294     { "GOTO",     &goto_cmd,   1,         HLP_GOTO      },
1295     { "RETURN",   &return_cmd, 0,         HLP_RETURN    },
1296     { "SHIFT",    &shift_cmd,  0,         HLP_SHIFT     },
1297     { "CALL",     &call_cmd,   0,         HLP_CALL      },
1298     { "ON",       &on_cmd,     0,         HLP_ON        },
1299     { "IF",       &assert_cmd, 0,         HLP_IF        },
1300     { "PROCEED",  &noop_cmd,   0,         HLP_PROCEED   },
1301     { "IGNORE",   &noop_cmd,   0,         HLP_IGNORE    },
1302     { "ECHO",     &echo_cmd,   0,         HLP_ECHO      },
1303     { "ASSERT",   &assert_cmd, 1,         HLP_ASSERT    },
1304     { "SEND",     &send_cmd,   0          },            /* deprecated */
1305     { "EXPECT",   &expect_cmd, 1          },            /* deprecated */
1306     { "NOEXPECT", &expect_cmd, 0          },            /* deprecated */
1307     { "!",        &spawn_cmd,  0,         HLP_SPAWN     },
1308     { "HELP",     &help_cmd,   0,         HLP_HELP      },
1309     { NULL,       NULL,        0          }
1310     };
1311 
1312 static CTAB set_glob_tab[] = {
1313     { "CONSOLE",     &sim_set_console,        0      }, /* deprecated */
1314     { "REMOTE",      &sim_set_remote_console, 0      }, /* deprecated */
1315     { "BREAK",       &brk_cmd,                SSH_ST }, /* deprecated */
1316     { "NOBREAK",     &brk_cmd,                SSH_CL }, /* deprecated */
1317     { "TELNET",      &sim_set_telnet,         0      }, /* deprecated */
1318     { "NOTELNET",    &sim_set_notelnet,       0      }, /* deprecated */
1319     { "LOG",         &sim_set_logon,          0,     HLP_SET_LOG      },
1320     { "NOLOG",       &sim_set_logoff,         0,     HLP_SET_LOG      },
1321     { "DEBUG",       &sim_set_debon,          0,     HLP_SET_DEBUG    },
1322     { "NODEBUG",     &sim_set_deboff,         0,     HLP_SET_DEBUG    },
1323     { "ENVIRONMENT", &sim_set_environment,    1,     HLP_SET_ENVIRON  },
1324     { "ON",          &set_on,                 1,     HLP_SET_ON       },
1325     { "NOON",        &set_on,                 0,     HLP_SET_ON       },
1326     { "VERIFY",      &set_verify,             1,     HLP_SET_VERIFY   },
1327     { "VERBOSE",     &set_verify,             1,     HLP_SET_VERIFY   },
1328     { "NOVERIFY",    &set_verify,             0,     HLP_SET_VERIFY   },
1329     { "NOVERBOSE",   &set_verify,             0,     HLP_SET_VERIFY   },
1330     { "MESSAGE",     &set_message,            1,     HLP_SET_MESSAGE  },
1331     { "NOMESSAGE",   &set_message,            0,     HLP_SET_MESSAGE  },
1332     { "QUIET",       &set_quiet,              1,     HLP_SET_QUIET    },
1333     { "NOQUIET",     &set_quiet,              0,     HLP_SET_QUIET    },
1334     { "LOCALOPC",    &set_localopc,           1,     HLP_SET_LOCALOPC },
1335     { "NOLOCALOPC",  &set_localopc,           0,     HLP_SET_LOCALOPC },
1336     { "PROMPT",      &set_prompt,             0,     HLP_SET_PROMPT   },
1337     { NULL,          NULL,                    0      }
1338     };
1339 
1340 static C1TAB set_dev_tab[] = {
1341     { "OCTAL",    &set_dev_radix,   8  },
1342     { "DECIMAL",  &set_dev_radix,  10  },
1343     { "HEX",      &set_dev_radix,  16  },
1344     { "ENABLED",  &set_dev_enbdis,  1  },
1345     { "DISABLED", &set_dev_enbdis,  0  },
1346     { "DEBUG",    &set_dev_debug,   1  },
1347     { "NODEBUG",  &set_dev_debug,   0  },
1348     { NULL,       NULL,             0  }
1349     };
1350 
1351 static C1TAB set_unit_tab[] = {
1352     { "ENABLED",  &set_unit_enbdis, 1 },
1353     { "DISABLED", &set_unit_enbdis, 0 },
1354     { NULL,       NULL,             0 }
1355     };
1356 
1357 static SHTAB show_glob_tab[] = {
1358     { "CONFIGURATION",  &show_config,        0, HLP_SHOW_CONFIG    },
1359     { "DEFAULT_BASE_SYSTEM_SCRIPT",
1360            &show_default_base_system_script, 0, HLP_SHOW_DBS       },
1361     { "DEVICES",   &show_config,             1, HLP_SHOW_DEVICES   },
1362     { "FEATURES",  &show_config,             2, HLP_SHOW_FEATURES  },
1363     { "QUEUE",     &show_queue,              0, HLP_SHOW_QUEUE     },
1364     { "TIME",      &show_time,               0, HLP_SHOW_TIME      },
1365     { "MODIFIERS", &show_mod_names,          0, HLP_SHOW_MODIFIERS },
1366     { "NAMES",     &show_log_names,          0, HLP_SHOW_NAMES     },
1367     { "SHOW",      &show_show_commands,      0, HLP_SHOW_SHOW      },
1368     { "VERSION",   &show_version,            1, HLP_SHOW_VERSION   },
1369     { "BUILDINFO", &show_buildinfo,          1, HLP_SHOW_BUILDINFO },
1370     { "PROM",      &show_prom,               0, HLP_SHOW_PROM      },
1371     { "HINTS",     &show_hints,              0, HLP_SHOW_HINTS     },
1372     { "CONSOLE",   &sim_show_console,        0, HLP_SHOW_CONSOLE   },
1373     { "REMOTE",    &sim_show_remote_console, 0, HLP_SHOW_REMOTE    },
1374     { "BREAK",     &show_break,              0, HLP_SHOW_BREAK     },
1375     { "LOG",       &sim_show_log,            0, HLP_SHOW_LOG       },
1376     { "TELNET",    &sim_show_telnet,         0  },  /* deprecated */
1377     { "DEBUG",     &sim_show_debug,          0, HLP_SHOW_DEBUG     },
1378     { "CLOCKS",    &sim_show_timers,         0, HLP_SHOW_CLOCKS    },
1379     { "SEND",      &sim_show_send,           0, HLP_SHOW_SEND      },
1380     { "EXPECT",    &sim_show_expect,         0, HLP_SHOW_EXPECT    },
1381     { "ON",        &show_on,                 0, HLP_SHOW_ON        },
1382     { NULL,        NULL,                     0  }
1383     };
1384 
1385 static SHTAB show_dev_tab[] = {
1386     { "RADIX",     &show_dev_radix,         0 },
1387     { "DEBUG",     &show_dev_debug,         0 },
1388     { "MODIFIERS", &show_dev_modifiers,     0 },
1389     { "NAMES",     &show_dev_logicals,      0 },
1390     { "SHOW",      &show_dev_show_commands, 0 },
1391     { NULL,        NULL,                    0 }
1392     };
1393 
1394 static SHTAB show_unit_tab[] = {
1395     { NULL, NULL, 0 }
1396     };
1397 
1398 #if defined(_WIN32)
1399 static
1400 int setenv(const char *envname, const char *envval, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
1401 {
1402 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1403 int r;
1404 
1405 (void)sprintf(envstr, "%s=%s", envname, envval);
1406 r = _putenv(envstr);
1407 FREE(envstr);
1408 return r;
1409 }
1410 
1411 static
1412 int unsetenv(const char *envname)
     /* [previous][next][first][last][top][bottom][index][help] */
1413 {
1414 setenv(envname, "", 1);
1415 return 0;
1416 }
1417 #endif /* if defined(_WIN32) */
1418 
1419 #if !defined(NO_LOCALE)
1420 # define XSTR_EMAXLEN 32767
1421 
1422 const char
1423 *xstrerror_l(int errnum)
     /* [previous][next][first][last][top][bottom][index][help] */
1424 {
1425   int saved = errno;
1426   const char *ret = NULL;
1427   static __thread char buf[XSTR_EMAXLEN];
1428 
1429 # if defined(__APPLE__) || defined(_AIX) || defined(__MINGW32__) || \
1430      defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1431 #  if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1432   if (strerror_s(buf, sizeof(buf), errnum) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1433 #  else
1434   if (strerror_r(errnum, buf, sizeof(buf)) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1435 #  endif
1436 # else
1437 #  if defined(__NetBSD__)
1438   locale_t loc = LC_GLOBAL_LOCALE;
1439 #  else
1440   locale_t loc = uselocale((locale_t)0);
1441 #  endif
1442   locale_t copy = loc;
1443   if (copy == LC_GLOBAL_LOCALE)
1444     copy = duplocale(copy);
1445 
1446   if (copy != (locale_t)0)
1447     {
1448       ret = strerror_l(errnum, copy); /*LINTOK: xstrerror_l*/
1449       if (loc == LC_GLOBAL_LOCALE)
1450         {
1451           freelocale(copy);
1452         }
1453     }
1454 # endif
1455 
1456   if (!ret)
1457     {
1458       (void)snprintf(buf, sizeof(buf), "Unknown error %d", errnum);
1459       ret = buf;
1460     }
1461 
1462   errno = saved;
1463   return ret;
1464 }
1465 #else
1466 # define xstrerror_l strerror
1467 #endif
1468 
1469 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1470 
1471 /* Substring removal hack */
1472 
1473 char *strremove(char *str, const char *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
1474 {
1475     char *p, *q, *r;
1476     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1477         size_t len = strlen(sub);
1478         while ((r = strstr(p = r + len, sub)) != NULL) {
1479             while (p < r)
1480                 *q++ = *p++;
1481         }
1482         while ((*q++ = *p++) != '\0')
1483             continue;
1484     }
1485     return str;
1486 }
1487 
1488 /* Trim whitespace */
1489 
1490 void strtrimspace (char *str_trimmed, const char *str_untrimmed)
     /* [previous][next][first][last][top][bottom][index][help] */
1491 {
1492     while (*str_untrimmed != '\0') {
1493       if(!isspace((unsigned char)*str_untrimmed)) {
1494         *str_trimmed = (char)*str_untrimmed;
1495         str_trimmed++;
1496       }
1497       str_untrimmed++;
1498     }
1499     *str_trimmed = '\0';
1500 }
1501 
1502 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1503 void allowCores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1504 {
1505   int ret;
1506   struct rlimit limit;
1507 # if defined(RLIMIT_CORE)
1508   ret = getrlimit(RLIMIT_CORE, &limit);
1509   (void)ret;
1510 #  if defined(TESTING)
1511   if (ret != 0)
1512     {
1513       sim_warn ("Failed to query core dump configuration.");
1514       return;
1515     }
1516 #  endif /* if defined(TESTING) */
1517   limit.rlim_cur = limit.rlim_max;
1518   ret = setrlimit(RLIMIT_CORE, &limit);
1519 #  if defined(TESTING)
1520   if (ret != 0)
1521     {
1522       sim_warn ("Failed to enable unlimited core dumps.");
1523       return;
1524     }
1525 #  endif /* if defined(TESTING) */
1526 # else
1527 #  if defined(TESTING)
1528   sim_warn ("Unable to query core dump configuration.");
1529 #  endif /* if defined(TESTING) */
1530 # endif /* if defined(RLIMIT_CORE) */
1531   return;
1532 }
1533 #endif
1534 
1535 #if defined(USE_DUMA)
1536 void CleanDUMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1537 {
1538   (void)fflush(stdout);
1539   DUMA_CHECKALL();
1540   (void)fflush(stderr);
1541 }
1542 # undef USE_DUMA
1543 # define USE_DUMA 1
1544 #endif /* if defined(USE_DUMA) */
1545 
1546 #if !defined(SIG_SETMASK)
1547 # undef USE_BACKTRACE
1548 #endif /* if !defined(SIG_SETMASK) */
1549 
1550 #if defined(PERF_STRIP)
1551 # undef USE_BACKTRACE
1552 #endif /* if defined(PERF_STRIP) */
1553 
1554 #if defined(USE_BACKTRACE)
1555 # include <backtrace.h>
1556 # include <backtrace-supported.h>
1557 # define BACKTRACE_SKIP 1
1558 # define BACKTRACE_MAIN "main"
1559 # undef USE_BACKTRACE
1560 # define USE_BACKTRACE 1
1561 #endif /* if defined(USE_BACKTRACE) */
1562 
1563 #if defined(BACKTRACE_SUPPORTED)
1564 # if defined(BACKTRACE_SUPPORTS_THREADS)
1565 #  if !(BACKTRACE_SUPPORTED)
1566 #   undef USE_BACKTRACE
1567 #  endif /* if !(BACKTRACE_SUPPORTED) */
1568 # else  /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1569 #  undef USE_BACKTRACE
1570 # endif /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1571 #else  /* if defined(BACKTRACE_SUPPORTED) */
1572 # undef USE_BACKTRACE
1573 #endif /* if defined(BACKTRACE_SUPPORTED) */
1574 
1575 #if defined(USE_BACKTRACE)
1576 # if defined(BACKTRACE_SUPPORTED)
1577 #  include "backtrace_func.c"
1578 # endif /* if defined(BACKTRACE_SUPPORTED) */
1579 #endif /* if defined(USE_BACKTRACE) */
1580 
1581 #if !defined(__CYGWIN__)
1582 # if !defined(__APPLE__)
1583 #  if !defined(_AIX)
1584 #   if !defined(__MINGW32__)
1585 #    if !defined(__MINGW64__)
1586 #     if !defined(CROSS_MINGW32)
1587 #      if !defined(CROSS_MINGW64)
1588 #       if !defined(_WIN32)
1589 #        if !defined(__HAIKU__)
1590 #         if !defined(__QNX__)
1591 #          if !defined(__FILC__)
1592 static int
1593 dl_iterate_phdr_callback (struct dl_phdr_info *info, size_t size, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1594 {
1595   (void)size;
1596   (void)data;
1597 
1598   if (strlen(info->dlpi_name) >= 2) {
1599       if (!dl_iterate_phdr_callback_called)
1600           (void)printf ("\r\n Loaded shared objects: ");
1601 
1602       dl_iterate_phdr_callback_called++;
1603       (void)printf ("%s ", info->dlpi_name);
1604   }
1605 
1606   return 0;
1607 }
1608 #          endif
1609 #         endif
1610 #        endif
1611 #       endif
1612 #      endif
1613 #     endif
1614 #    endif
1615 #   endif
1616 #  endif
1617 # endif
1618 #endif
1619 
1620 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1621 # if defined(_UCRT)
1622 #  define MINGW_CRT "UCRT"
1623 # else
1624 #  define MINGW_CRT "MSVCRT"
1625 # endif
1626 #endif
1627 
1628 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1629 struct UCRTVersion
1630 {
1631   uint16_t ProductVersion[4];
1632 };
1633 
1634 int
1635 GetUCRTVersion (struct UCRTVersion *ucrtversion)
     /* [previous][next][first][last][top][bottom][index][help] */
1636 {
1637 # ifdef _DEBUG
1638   static const wchar_t *DllName = L"ucrtbased.dll";
1639 # else
1640   static const wchar_t *DllName = L"ucrtbase.dll";
1641 # endif
1642 
1643   HMODULE ucrt = GetModuleHandleW (DllName);
1644   if (!ucrt)
1645     return GetLastError ();
1646 
1647   wchar_t path[SIR_MAXPATH];
1648   if (!GetModuleFileNameW (ucrt, path, SIR_MAXPATH))
1649     return GetLastError ();
1650 
1651   DWORD versionInfoSize = GetFileVersionInfoSizeW (path, NULL);
1652   if (!versionInfoSize)
1653     return GetLastError ();
1654 
1655   uint8_t versionInfo[versionInfoSize];
1656 
1657   if (!GetFileVersionInfoW (path, 0, versionInfoSize, versionInfo))
1658     return GetLastError ();
1659 
1660   VS_FIXEDFILEINFO *fixedFileInfo;
1661   UINT fixedFileInfoSize;
1662   if (!VerQueryValueW (versionInfo, L"\\", (void **)&fixedFileInfo, &fixedFileInfoSize))
1663     return GetLastError ();
1664 
1665   memcpy (ucrtversion->ProductVersion, &fixedFileInfo->dwProductVersionMS, sizeof (uint32_t) * 2);
1666 
1667   return 0;
1668 }
1669 #endif
1670 
1671 /* libsir support */
1672 
1673 #if !defined(PERF_STRIP)
1674 static int dps8_sir_report_error(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1675 {
1676   char message[SIR_MAXERROR] = {0};
1677   (void)sir_geterror(message);
1678   (void)fprintf(stderr, SIR_BREDB("libsir error: ") SIR_RED("%s%s"), message, SIR_EOL);
1679   return EXIT_FAILURE;
1680 }
1681 
1682 /* Main command loop */
1683 
1684 int main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1685 {
1686 char *cptr = NULL, *cptr2 = NULL;
1687 char nbuf[PATH_MAX + 7];
1688 char cbuf[4*CBUFSIZE];
1689 char **targv = NULL;
1690 int32 i, sw;
1691 t_bool lookswitch;
1692 t_stat stat;
1693 
1694 (void)memset (nbuf, 0, sizeof(nbuf));
1695 (void)memset (cbuf, 0, sizeof(cbuf));
1696 
1697 /* Check for improper setuid root */
1698 # if !defined(__sun) && !defined(__illumos__) && !defined(_WIN32)
1699 uid_t real_uid = getuid();
1700 uid_t eff_uid = geteuid();
1701 if (0 == eff_uid && 0 != real_uid) {
1702   (void)fprintf(stderr, "FATAL: Running with improper setuid root privileges!\r\n");
1703   exit(EXIT_FAILURE);
1704 }
1705 # endif
1706 
1707 sunos_obtain_realtime_privileges(); /* Keep as early as possible */
1708 
1709 sim_free_memory = sim_memory_available(); /* stash free memory */
1710 
1711 sim_appfilename = _sir_getappfilename(); /* stash app filename */
1712 if (sim_appfilename == NULL) {
1713   sim_appfilename = "dps8";
1714 }
1715 
1716 /* libsir init */
1717 
1718 sirinit si;
1719 if (!sir_makeinit(&si))
1720     return dps8_sir_report_error();
1721 
1722 /* Levels for stdout: send debug, information, warning, and notice there. */
1723 si.d_stdout.levels = SIRL_DEBUG | SIRL_INFO | SIRL_WARN | SIRL_NOTICE;
1724 
1725 /* Options for stdout: don't show the name, level, timestamp, hostname, or PID. */
1726 si.d_stdout.opts = SIRO_NONAME | SIRO_NOLEVEL | SIRO_NOTIME | SIRO_NOHOST | SIRO_NOPID;
1727 
1728 /* Levels for stderr: send error and above there. */
1729 si.d_stderr.levels = SIRL_ERROR | SIRL_CRIT | SIRL_ALERT | SIRL_EMERG;
1730 
1731 /* Options for stderr: don't show the hostname. */
1732 si.d_stderr.opts = SIRO_NOHOST;
1733 
1734 /* Levels for the system logger: don't send any output there. */
1735 si.d_syslog.levels = SIRL_NONE;
1736 
1737 /* Options for the system logger: use the default value. */
1738 si.d_syslog.opts = SIRO_DEFAULT;
1739 
1740 /* Configure a name to associate with our output. */
1741 (void)_sir_strncpy(si.name, SIR_MAXNAME, appname, strnlen(appname, SIR_MAXNAME));
1742 
1743 /* Initialize libsir. */
1744 if (!sir_init(&si))
1745     return dps8_sir_report_error();
1746 
1747 # if defined(_AIX)
1748 if (getenv("DPS8M_SKIP_AIX_VARIABLES") == NULL) {
1749   if (setenv("DPS8M_SKIP_AIX_VARIABLES", "1", 1)) {
1750     (void)fprintf(stderr, "\rFATAL: Failed to set \"DPS8M_SKIP_AIX_VARIABLES=1\"! Aborting at %s[%s:%d]\r\n",
1751                   __func__, __FILE__, __LINE__);
1752     abort();
1753   }
1754 
1755   if (setenv("AIXTHREAD_AFFINITY", "strict", 1)) {
1756     (void)fprintf(stderr, "\rWARN: Failed to set \"AIXTHREAD_AFFINITY=strict\".\r\n");
1757   }
1758 
1759   if (setenv("AIXTHREAD_MUTEX_FAST", "ON", 1)) {
1760     (void)fprintf(stderr, "\rWARN: Failed to set \"AIXTHREAD_MUTEX_FAST=ON\".\r\n");
1761   }
1762 
1763   if (setenv("MALLOCOPTIONS", "multiheap", 1)) {
1764     (void)fprintf(stderr, "\rWARN: Failed to set \"MALLOCOPTIONS=multiheap\".\r\n");
1765   }
1766 
1767 #  if !defined(__PASE__)
1768   if (setenv("AIXTHREAD_SCOPE", "S", 1)) {
1769     (void)fprintf(stderr, "\rWARN: Failed to set \"AIXTHREAD_SCOPE=S\".\r\n");
1770   }
1771 #  else
1772 #   if !defined(TESTING)
1773   if (setenv("PASE_SYSCALL_NOSIGILL", "ALL", 1)) {
1774     (void)fprintf(stderr, "\rWARN: Failed to set \"PASE_SYSCALL_NOSIGILL=ALL\".\r\n");
1775   }
1776 #   endif
1777 #  endif
1778 
1779   if (execvp(argv[0], argv) == -1) {
1780     (void)fprintf(stderr, "\rFATAL: execvp failed! Aborting at %s[%s:%d]\r\n",
1781                   __func__, __FILE__, __LINE__);
1782     abort();
1783   }
1784 }
1785 # endif
1786 
1787 # if defined(USE_BACKTRACE)
1788 #  if defined(BACKTRACE_SUPPORTED)
1789 #   if defined(_INC_BACKTRACE_FUNC)
1790 bt_pid = (long)_sir_getpid();
1791 (void)bt_pid;
1792 #   endif /* if defined(_INC_BACKTRACE_FUNC) */
1793 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1794 # endif /* if defined(USE_BACKTRACE) */
1795 
1796 # if defined(__MINGW32__)
1797 #  undef IS_WINDOWS
1798 #  define IS_WINDOWS 1
1799 #  if !defined(NEED_CONSOLE_SETUP)
1800 #   define NEED_CONSOLE_SETUP
1801 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1802 # endif /* if defined(__MINGW32__)*/
1803 
1804 # if defined(CROSS_MINGW32)
1805 #  undef IS_WINDOWS
1806 #  define IS_WINDOWS 1
1807 #  if !defined(NEED_CONSOLE_SETUP)
1808 #   define NEED_CONSOLE_SETUP
1809 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1810 # endif /* if defined(CROSS_MINGW32) */
1811 
1812 # if defined(__MINGW64__)
1813 #  undef IS_WINDOWS
1814 #  define IS_WINDOWS 1
1815 #  if !defined(NEED_CONSOLE_SETUP)
1816 #   define NEED_CONSOLE_SETUP
1817 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1818 # endif /* if defined(__MINGW64__) */
1819 
1820 # if defined(CROSS_MINGW64)
1821 #  undef IS_WINDOWS
1822 #  define IS_WINDOWS 1
1823 #  if !defined(NEED_CONSOLE_SETUP)
1824 #   define NEED_CONSOLE_SETUP
1825 #  endif /* if !defined(NEED_CONSOLE_SETUP */
1826 # endif /* if defined(CROSS_MINGW64) */
1827 
1828 # if defined(__CYGWIN__)
1829 #  if defined(IS_WINDOWS)
1830 #   undef IS_WINDOWS
1831 #  endif /* if defined(IS_WINDOWS) */
1832 # endif /* if defined(__CYGWIN__) */
1833 
1834 # if defined(USE_DUMA)
1835 #  if defined(DUMA_EXPLICIT_INIT)
1836 duma_init();
1837 (void)fflush(stderr);
1838 #  endif /* if defined(DUMA_EXPLICIT_INIT) */
1839 #  if defined(DUMA_MIN_ALIGNMENT)
1840 #   if DUMA_MIN_ALIGNMENT > 0
1841 DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
1842 #   endif /* if DUMA_MIN_ALIGNMENT > 0 */
1843 #  endif /* if defined(DUMA_MIN_ALIGNMENT) */
1844 DUMA_SET_FILL(0x2E);
1845 (void)fflush(stderr);
1846 (void)atexit(CleanDUMA);
1847 # endif /* if defined(USE_DUMA) */
1848 
1849 # if defined(USE_BACKTRACE)
1850 #  if defined(BACKTRACE_SUPPORTED)
1851 #   include "backtrace_main.c"
1852 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1853 # endif /* if defined(USE_BACKTRACE) */
1854 
1855 # if !defined(NO_LOCALE)
1856 (void)setlocale(LC_ALL, "");
1857 # endif
1858 
1859 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1860 #  if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
1861 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1862 #  endif /* if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) */
1863 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1864 
1865 # if defined(NEED_CONSOLE_SETUP)
1866 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1867 if (handle != INVALID_HANDLE_VALUE)
1868   {
1869     DWORD mode = 0;
1870     if (GetConsoleMode(handle, &mode))
1871       {
1872         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1873         SetConsoleMode(handle, mode);
1874       }
1875   }
1876 puts ("\e[0m");
1877 # endif /* if defined(NEED_CONSOLE_SETUP) */
1878 
1879 # if defined(__HAIKU__)
1880 (void)disable_debugger(1);
1881 # endif /* if defined(__HAIKU__) */
1882 
1883 running_perf_test = false;
1884 
1885 /* Set a friendly name for the current thread. */
1886 char thread_name[SIR_MAXPID] = {0};
1887 _sir_snprintf_trunc(thread_name, SIR_MAXPID, "%s", appname);
1888 (void)_sir_setthreadname(thread_name);
1889 
1890 /* sanity checks */
1891 
1892 # if defined(__clang_analyzer__)
1893 (void)fprintf (stderr, "Error: Attempting to execute a Clang Analyzer build!\r\n");
1894 return 1;
1895 # endif /* if defined(__clang_analyzer__) */
1896 
1897 if (argc == 0) {
1898     (void)fprintf (stderr, "Error: main() called directly!\r\n");
1899     return 1;
1900 }
1901 
1902 /* Enable unlimited core dumps */
1903 # if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1904 allowCores();
1905 # endif
1906 
1907 int testEndian = decContextTestEndian();
1908 if (testEndian != 0) {
1909   if (testEndian == 1) {
1910     (void)fprintf (stderr,
1911                    "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\r\n");
1912     return 1;
1913   }
1914   if (testEndian == -1) {
1915     (void)fprintf (stderr,
1916                    "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\r\n");
1917     return 1;
1918   }
1919   (void)fprintf (stderr,
1920                  "Error: Unable to determine system byte order; aborting.\r\n");
1921   return 1;
1922 }
1923 # if defined(NEED_128)
1924 test_math128();
1925 # endif /* if defined(NEED_128) */
1926 
1927 # define UV_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
1928 
1929 # if defined(LINUX_OS) && UV_VERSION_HEX >= UV_VERSION(1, 44, 0)
1930 // Only use uv_available_parallelism on Linux and only on libuv 1.44.0 or higher.
1931 // This will return a value less than the actual number of CPUs if, for example,
1932 // the CPU affinity mask has been pinned to specific CPUs.  On all other systems,
1933 // prefer the _sir_nprocs routine to query the number of actual CPUs available.
1934 nprocs = (unsigned int)uv_available_parallelism();
1935 # else
1936 nprocs = (unsigned int)_sir_nprocs();
1937 # endif
1938 
1939 # if defined(FORCE_STATIC)
1940 ncores = 0;
1941 # else
1942 ncores = (unsigned int)get_core_count();
1943 # endif
1944 
1945 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1946 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1947 if (!targv)
1948   {
1949     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1950                    __func__, __FILE__, __LINE__);
1951 # if defined(USE_BACKTRACE)
1952 #  if defined(SIGUSR2)
1953     (void)raise(SIGUSR2);
1954     /*NOTREACHED*/ /* unreachable */
1955 #  endif /* if defined(SIGUSR2) */
1956 # endif /* if defined(USE_BACKTRACE) */
1957     abort();
1958   }
1959 for (i=0; i<argc; i++)
1960     targv[i] = argv[i];
1961 argv = targv;
1962 
1963 /* setup defaults */
1964 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1965 *cbuf = 0;                                              /* init arg buffer */
1966 sim_switches = 0;                                       /* init switches */
1967 lookswitch = TRUE;
1968 stdnul = fopen(NULL_DEVICE,"wb");
1969 
1970 /* process arguments */
1971 for (i = 1; i < argc; i++) {                            /* loop thru args */
1972     if (argv[i] == NULL)                                /* paranoia */
1973         continue;
1974 
1975 # if defined(THREADZ) || defined(LOCKLESS)
1976 /* performance test */
1977     int perftestflag  = strcmp(argv[i], "--perftest");
1978     if (perftestflag == 0) {
1979       char * testName = NULL;
1980       if (i + 1 < argc)
1981         testName = argv[i + 1];
1982       perfTest (testName);
1983       return 0;
1984     }
1985 # endif
1986 
1987 /* requested only version? */
1988     int onlyvers  = strcmp(argv[i], "--version");
1989     if (onlyvers == 0) {
1990 # if defined(VER_H_GIT_VERSION)
1991 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1992 #   if VER_H_GIT_PATCH_INT < 1
1993         (void)fprintf (stdout, "%s simulator %s\r\n",
1994                        sim_name, VER_H_GIT_VERSION);
1995 #   else
1996         (void)fprintf (stdout, "%s simulator %s+%s\r\n",
1997                        sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1998 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1999 #  else
2000         (void)fprintf (stdout, "%s simulator %s\r\n",
2001                        sim_name, VER_H_GIT_VERSION);
2002 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
2003 # else
2004         (void)fprintf (stdout, "%s simulator\r\n", sim_name);
2005 # endif /* if defined(VER_H_GIT_VERSION) */
2006         FREE (targv);
2007         return 0;
2008     }
2009 
2010 /* requested short or long help? */
2011     int longhelp  = strcmp(argv[i], "--help");
2012     int shorthelp = strcmp(argv[i], "-h");
2013     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
2014     if (longhelp == 0 || shorthelp == 0) {
2015 # if defined(VER_H_GIT_VERSION)
2016 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
2017 #   if VER_H_GIT_PATCH_INT < 1
2018         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
2019 #   else
2020         (void)fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
2021 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
2022 #  else
2023         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
2024 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
2025 # else
2026         (void)fprintf (stdout, "%s simulator", sim_name);
2027 # endif /* if defined(VER_H_GIT_VERSION) */
2028         (void)fprintf (stdout, "\r\n");
2029         (void)fprintf (stdout, "\r\n USAGE: %s [ [ SWITCH ] ... ] [ SCRIPT ]", argv[0]);
2030         (void)fprintf (stdout, "\r\n");
2031         (void)fprintf (stdout, "\r\n Invokes the %s simulator, with optional switches and/or script file.", sim_name);
2032         (void)fprintf (stdout, "\r\n");
2033         (void)fprintf (stdout, "\r\n Switches:");
2034         (void)fprintf (stdout, "\r\n  -e, -E            Aborts script processing immediately upon any error");
2035         (void)fprintf (stdout, "\r\n  -h, -H, --help    Prints only this informational help text and exits");
2036         (void)fprintf (stdout, "\r\n  -k, -K            Disables all support for exclusive file locking");
2037         (void)fprintf (stdout, "\r\n  -l, -L            Reports but ignores all exclusive file locking errors");
2038         (void)fprintf (stdout, "\r\n  -o, -O            Makes scripting ON conditions and actions inheritable");
2039 # if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) && !defined(__CYGWIN__)
2040         (void)fprintf (stdout, "\r\n  -p, -P            Enables real-time scheduling (may require privileges)");
2041 # endif
2042         (void)fprintf (stdout, "\r\n  -q, -Q            Disables printing of non-fatal informational messages");
2043         (void)fprintf (stdout, "\r\n  -r, -R            Enables an unlinked ephemeral system state file");
2044         (void)fprintf (stdout, "\r\n  -s, -S            Enables a randomized persistent system state file");
2045         (void)fprintf (stdout, "\r\n  -t, -T            Disables fsync and creation/usage of system state file");
2046         (void)fprintf (stdout, "\r\n  -v, -V            Prints commands read from script file before execution");
2047         (void)fprintf (stdout, "\r\n  --version         Prints only the simulator identification text and exits");
2048         (void)fprintf (stdout, "\r\n");
2049 # if defined(USE_DUMA)
2050         nodist++;
2051 # endif /* if defined(USE_DUMA) */
2052 if (!nodist) {
2053         (void)fprintf (stdout, "\r\n This software is made available under the terms of the ICU License.");
2054         (void)fprintf (stdout, "\r\n For complete license details, see the LICENSE file included with the");
2055         (void)fprintf (stdout, "\r\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\r\n");
2056 }
2057 else
2058 {
2059         (void)fprintf (stdout, "\r\n********** LICENSE RESTRICTED BUILD ****** NOT FOR REDISTRIBUTION **********");
2060 }
2061         (void)fprintf (stdout, "\r\n");
2062         FREE(argv); //-V726
2063         return 0;
2064     }
2065     /* invalid arguments? */
2066     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
2067         if ((sw = get_switches (argv[i])) < 0) {
2068             (void)fprintf (stderr, "Invalid switch \"%s\".\r\nTry \"%s -h\" for help.\r\n", argv[i], argv[0]);
2069             FREE(argv); //-V726
2070             return 1;
2071             }
2072         sim_switches = sim_switches | sw;
2073         }
2074     /* parse arguments */
2075     else {
2076         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
2077             (void)fprintf (stderr, "Argument string too long\r\n");
2078             FREE(argv); //-V726
2079             return 1;
2080             }
2081         if (*cbuf)                                  /* concat args */
2082             strcat (cbuf, " ");
2083         (void)sprintf(&cbuf[strlen(cbuf)], "%s%s%s", //-V755
2084                       strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : ""); //-V755
2085         lookswitch = FALSE;                         /* no more switches */
2086         }
2087     }                                               /* end for */
2088 sim_nolock = sim_switches & SWMASK ('K');           /* -k means skip locking     */
2089 sim_iglock = sim_switches & SWMASK ('L');           /* -l means ignore locking   */
2090 sim_randompst = sim_switches & SWMASK ('S');        /* -s means persist random   */
2091 sim_quiet = sim_switches & SWMASK ('Q');            /* -q means quiet            */
2092 sim_randstate = sim_switches & SWMASK ('R');        /* -r means random sys_state */
2093 if (sim_randompst) sim_randstate = 1;               /*    and is implied with -s */
2094 sim_nostate = sim_switches & SWMASK ('T');          /* -t means no sys_state     */
2095 if (sim_nostate)                                    /*    and disables -s and -r */
2096   {
2097     sim_randompst = 0;
2098     sim_randstate = 0;
2099   }
2100 sim_on_inherit = sim_switches & SWMASK ('O');       /* -o means inherit on state */
2101 
2102 # if !defined(__MVS__) && !defined(__MINGW32__) && !defined(__MINGW64__) && \
2103      !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) && !defined(__CYGWIN__)
2104 sim_realtime = sim_switches & SWMASK ('P');         /* -p means real-time priority */
2105 # endif
2106 
2107 sim_init_sock ();                                   /* init socket capabilities */
2108 if (sim_dflt_dev == NULL)                           /* if no default */
2109     sim_dflt_dev = sim_devices[0];
2110 if (sim_vm_init != NULL)                            /* call once only */
2111     (*sim_vm_init)();
2112 sim_finit ();                                       /* init fio package */
2113 for (i = 0; cmd_table[i].name; i++) {
2114     size_t alias_len = strlen (cmd_table[i].name);
2115     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
2116     if (!cmd_name)
2117       {
2118         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2119                        __func__, __FILE__, __LINE__);
2120 # if defined(USE_BACKTRACE)
2121 #  if defined(SIGUSR2)
2122         (void)raise(SIGUSR2);
2123         /*NOTREACHED*/ /* unreachable */
2124 #  endif /* if defined(SIGUSR2) */
2125 # endif /* if defined(USE_BACKTRACE) */
2126         abort();
2127       }
2128 
2129     strcpy (cmd_name, cmd_table[i].name);
2130     while (alias_len > 1) {
2131         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
2132         --alias_len;
2133         if (getenv (cmd_name))                      /* Externally defined command alias? */
2134             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
2135         }
2136     FREE (cmd_name);
2137     }
2138 stop_cpu = 0;
2139 sim_interval = 0;
2140 sim_time = sim_rtime = 0;
2141 noqueue_time = 0;
2142 sim_clock_queue = QUEUE_LIST_END;
2143 sim_is_running = 0;
2144 sim_log = NULL;
2145 if (sim_emax <= 0)
2146     sim_emax = 1;
2147 
2148 /* stash our pthread_id */
2149 main_thread_id = pthread_self();
2150 
2151 /* stash current (default) thread scheduler and priority */
2152 save_thread_sched(pthread_self());
2153 
2154 /* real-time priority (always call before sim_timer_init) */
2155 if (sim_realtime) {
2156     if (nprocs < 2) {
2157         sir_emerg("FATAL: Real-time requires >1 CPU but only %u detected!", nprocs);
2158         exit(EXIT_FAILURE);
2159     } else {
2160         watchdog_startup();
2161     }
2162 }
2163 
2164 /* do setup for realtime or normal operation */
2165 if (realtime_ok) {
2166     set_realtime_priority (pthread_self(), realtime_max_priority() - 1);
2167     check_realtime_priority (pthread_self(), realtime_max_priority() - 1);
2168 } else {
2169 # if !defined(__QNX__)
2170     (void)sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL);
2171 # endif
2172 }
2173 
2174 sim_timer_init ();
2175 
2176 if ((stat = sim_ttinit ()) != SCPE_OK) {
2177     (void)fprintf (stderr, "Fatal terminal initialization error\r\n%s\r\n",
2178                    sim_error_text (stat));
2179     FREE(argv); //-V726
2180     return 1;
2181     }
2182 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
2183     (void)fprintf (stderr, "Unable to allocate examine buffer\r\n");
2184     FREE(argv); //-V726
2185     return 1;
2186     };
2187 if ((stat = reset_all_p (0)) != SCPE_OK) {
2188     (void)fprintf (stderr, "Fatal simulator initialization error\r\n%s\r\n",
2189                    sim_error_text (stat));
2190     FREE(argv); //-V726
2191     return 1;
2192     }
2193 if ((stat = sim_brk_init ()) != SCPE_OK) {
2194     (void)fprintf (stderr, "Fatal breakpoint table initialization error\r\n%s\r\n",
2195                    sim_error_text (stat));
2196     FREE(argv); //-V726
2197     return 1;
2198     }
2199 if (!sim_quiet) {
2200     (void)printf ("\r\n");
2201     show_version (stdout, NULL, NULL, 0, NULL);
2202     }
2203 
2204 cptr = getenv("HOME");
2205 if (cptr == NULL) {
2206     cptr = getenv("HOMEPATH");
2207     cptr2 = getenv("HOMEDRIVE");
2208     }
2209 else
2210     cptr2 = NULL;
2211 (void)cptr2;
2212 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
2213     stat = do_cmd (0, cbuf);                            /* proc cmd file */
2214 else if (*argv[0]) {                                    /* sim name arg? */
2215     char *np;                                           /* "path.ini" */
2216     nbuf[0] = '"';                                      /* starting " */
2217     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
2218     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
2219         np = strrchr (nbuf, '/');                       /* strip path and try again in cwd */
2220         if (np == NULL)
2221             np = strrchr (nbuf, '\\');                  /* windows path separator */
2222         if (np != NULL) {
2223             *np = '"';
2224             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
2225             }
2226         }
2227     }
2228 
2229 argv = uv_setup_args(argc, argv);
2230 
2231 /* show if hints are not disabled and empty cbuf and not quiet mode */
2232 if (getenv("DPS8M_NO_HINTS") == NULL)
2233   if (!sim_quiet && 0 == strcmp(cbuf, ""))
2234     (void)show_hints(0, 0, 0, 1, 0);
2235 
2236 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
2237 
2238 if (sim_vm_exit != NULL)                                /* call once only */
2239     (*sim_vm_exit)();
2240 
2241 detach_all (0, TRUE);                                   /* close files */
2242 sim_set_deboff (0, NULL);                               /* close debug */
2243 sim_set_logoff (0, NULL);                               /* close log */
2244 sim_set_notelnet (0, NULL);                             /* close Telnet */
2245 sim_ttclose ();                                         /* close console */
2246 sim_cleanup_sock ();                                    /* cleanup sockets */
2247 fclose (stdnul);                                        /* close bit bucket file handle */
2248 FREE (targv);                                           /* release any argv copy that was made */
2249 FREE (sim_prompt);
2250 FREE (sim_eval);
2251 FREE (sim_internal_devices);
2252 FREE (sim_brk_tab);
2253 FREE (sim_staba.comp);
2254 FREE (sim_staba.mask);
2255 FREE (sim_stabr.comp);
2256 FREE (sim_stabr.mask);
2257 return sir_cleanup() ? EXIT_SUCCESS : dps8_sir_report_error();
2258 }
2259 #endif
2260 
2261 t_stat process_stdin_commands (t_stat stat, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
2262 {
2263 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
2264 CONST char *cptr;
2265 t_stat stat_nomessage;
2266 CTAB *cmdp = NULL;
2267 
2268 (void)memset (gbuf, 0, sizeof(gbuf));
2269 (void)memset (cbuf, 0, sizeof(cbuf));
2270 
2271 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
2272 while (stat != SCPE_EXIT) {                             /* in case exit */
2273     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
2274         (void)printf ("%s%s\r\n", sim_prompt, cptr);    /* echo */
2275     else if (sim_vm_read != NULL) {                     /* sim routine? */
2276         (void)printf ("%s", sim_prompt);                /* prompt */
2277         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
2278         }
2279     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prompt*/
2280     if (cptr == NULL) {                                 /* EOF? */
2281         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
2282         else break;                                     /* otherwise exit */
2283         }
2284     if (*cptr == 0)                                     /* ignore blank */
2285         continue;
2286     sim_sub_args (cbuf, sizeof(cbuf), argv);
2287     if (sim_log)                                        /* log cmd */
2288         (void)fprintf (sim_log, "%s%s\r\n", sim_prompt, cptr);
2289     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
2290         (void)fprintf (sim_deb, "%s%s\r\n", sim_prompt, cptr);
2291     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2292     sim_switches = 0;                                   /* init switches */
2293     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
2294         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
2295     else
2296         stat = SCPE_UNK;
2297     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
2298     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2299     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2300     sim_last_cmd_stat = stat;                           /* save command error status */
2301     if (!stat_nomessage) {                              /* displaying message status? */
2302         if (cmdp && (cmdp->message))                    /* special message handler? */
2303             cmdp->message (NULL, stat);                 /* let it deal with display */
2304         else
2305             if (stat >= SCPE_BASE)                      /* error? */
2306                 sim_printf ("%s\r\n", sim_error_text (stat));
2307         }
2308     if (sim_vm_post != NULL)
2309         (*sim_vm_post) (TRUE);
2310     }                                                   /* end while */
2311 return stat;
2312 }
2313 
2314 /* Set prompt routine */
2315 
2316 t_stat set_prompt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2317 {
2318 char gbuf[CBUFSIZE], *gptr;
2319 
2320 if ((NULL == cptr) || (*cptr == '\0'))
2321     return SCPE_ARG;
2322 
2323 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
2324 if (gbuf[0] == '\0') {                                  /* Token started with quote */
2325     gbuf[sizeof (gbuf)-1] = '\0';
2326     strncpy (gbuf, cptr, sizeof (gbuf)-1);
2327     gptr = strchr (gbuf, '"');
2328     if (NULL != gptr)
2329         *gptr = '\0';
2330     }
2331 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
2332 if (!sim_prompt)
2333   {
2334     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2335                    __func__, __FILE__, __LINE__);
2336 #if defined(USE_BACKTRACE)
2337 # if defined(SIGUSR2)
2338     (void)raise(SIGUSR2);
2339     /*NOTREACHED*/ /* unreachable */
2340 # endif /* if defined(SIGUSR2) */
2341 #endif /* if defined(USE_BACKTRACE) */
2342     abort();
2343   }
2344 (void)sprintf (sim_prompt, "%s ", gbuf);
2345 return SCPE_OK;
2346 }
2347 
2348 /* Find command routine */
2349 
2350 CTAB *find_cmd (const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
2351 {
2352 CTAB *cmdp = NULL;
2353 
2354 if (sim_vm_cmd)                                         /* try ext commands */
2355     cmdp = find_ctab (sim_vm_cmd, gbuf);
2356 if (cmdp == NULL)                                       /* try regular cmds */
2357     cmdp = find_ctab (cmd_table, gbuf);
2358 return cmdp;
2359 }
2360 
2361 /* Exit command */
2362 
2363 t_stat exit_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2364 {
2365 return SCPE_EXIT;
2366 }
2367 
2368 /* Help command */
2369 
2370 /* Used when sorting a list of command names */
2371 static int _cmd_name_compare (const void *pa, const void *pb)
     /* [previous][next][first][last][top][bottom][index][help] */
2372 {
2373 CTAB * const *a = (CTAB * const *)pa;
2374 CTAB * const *b = (CTAB * const *)pb;
2375 
2376 return strcmp((*a)->name, (*b)->name);
2377 }
2378 
2379 void fprint_help (FILE *st)
     /* [previous][next][first][last][top][bottom][index][help] */
2380 {
2381 CTAB *cmdp;
2382 CTAB **hlp_cmdp = NULL;
2383 size_t cmd_cnt = 0;
2384 size_t cmd_size = 0;
2385 size_t max_cmdname_size = 0;
2386 size_t i, line_offset;
2387 
2388 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
2389     if (cmdp->help) {
2390         if (cmd_cnt >= cmd_size) {
2391             cmd_size += 20;
2392             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2393             if (!hlp_cmdp)
2394               {
2395                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2396                                __func__, __FILE__, __LINE__);
2397 #if defined(USE_BACKTRACE)
2398 # if defined(SIGUSR2)
2399                 (void)raise(SIGUSR2);
2400                 /*NOTREACHED*/ /* unreachable */
2401 # endif /* if defined(SIGUSR2) */
2402 #endif /* if defined(USE_BACKTRACE) */
2403                 abort();
2404               }
2405             }
2406         hlp_cmdp[cmd_cnt] = cmdp;
2407         ++cmd_cnt;
2408         if (strlen(cmdp->name) > max_cmdname_size)
2409             max_cmdname_size = strlen(cmdp->name);
2410         }
2411     }
2412 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
2413     if (cmdp->help && (NULL == sim_vm_cmd || NULL == find_ctab (sim_vm_cmd, cmdp->name))) {
2414         if (cmd_cnt >= cmd_size) {
2415             cmd_size += 20;
2416             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2417             if (!hlp_cmdp)
2418               {
2419                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2420                                __func__, __FILE__, __LINE__);
2421 #if defined(USE_BACKTRACE)
2422 # if defined(SIGUSR2)
2423                 (void)raise(SIGUSR2);
2424                 /*NOTREACHED*/ /* unreachable */
2425 # endif /* if defined(SIGUSR2) */
2426 #endif /* if defined(USE_BACKTRACE) */
2427                 abort();
2428               }
2429             }
2430         hlp_cmdp[cmd_cnt] = cmdp;
2431         ++cmd_cnt;
2432         if (strlen (cmdp->name) > max_cmdname_size)
2433             max_cmdname_size = strlen(cmdp->name);
2434         }
2435     }
2436 (void)fprintf (st, "HELP is available for the following commands:\r\n\r\n    ");
2437 if (hlp_cmdp)
2438   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
2439 line_offset = 4;
2440 for ( i = 0 ; i < cmd_cnt ; ++i ) {
2441     fputs (hlp_cmdp[i]->name, st);
2442     line_offset += 5 + max_cmdname_size;
2443     if (line_offset + max_cmdname_size > 79) {
2444         line_offset = 4;
2445         (void)fprintf (st, "\r\n    ");
2446         }
2447     else
2448         (void)fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
2449     }
2450 FREE (hlp_cmdp);
2451 (void)fprintf (st, "\r\n");
2452 return;
2453 }
2454 
2455 static void fprint_header (FILE *st, t_bool *pdone, char *context)
     /* [previous][next][first][last][top][bottom][index][help] */
2456 {
2457 if (!*pdone)
2458     (void)fprintf (st, "%s", context);
2459 *pdone = TRUE;
2460 }
2461 
2462 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2463 {
2464 REG *rptr, *trptr;
2465 t_bool found = FALSE;
2466 t_bool all_unique = TRUE;
2467 size_t max_namelen = 0;
2468 DEVICE *tdptr;
2469 CONST char *tptr;
2470 char *namebuf;
2471 char rangebuf[32];
2472 
2473 if (dptr->registers)
2474     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2475         if (rptr->flags & REG_HIDDEN)
2476             continue;
2477         if (rptr->depth > 1)
2478             (void)sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2479         else
2480             strcpy (rangebuf, "");
2481         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2482             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2483         found = TRUE;
2484         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2485         if ((trptr == NULL) || (tdptr != dptr))
2486             all_unique = FALSE;
2487         }
2488 if (!found) {
2489     if (!silent)
2490         (void)fprintf (st, "No register HELP available for the %s device\r\n",
2491                        dptr->name);
2492     }
2493 else {
2494     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2495     if (!namebuf)
2496       {
2497         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2498                        __func__, __FILE__, __LINE__);
2499 #if defined(USE_BACKTRACE)
2500 # if defined(SIGUSR2)
2501         (void)raise(SIGUSR2);
2502         /*NOTREACHED*/ /* unreachable */
2503 # endif /* if defined(SIGUSR2) */
2504 #endif /* if defined(USE_BACKTRACE) */
2505         abort();
2506       }
2507     (void)fprintf (st, "\r\nThe %s device implements these registers:\r\n\r\n",
2508                    dptr->name);
2509     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2510         if (rptr->flags & REG_HIDDEN)
2511             continue;
2512         if (rptr->depth <= 1)
2513             (void)sprintf (namebuf, "%*s",
2514                            -((int)max_namelen),
2515                            rptr->name);
2516         else {
2517             (void)sprintf (rangebuf, "[%d:%d]",
2518                            0,
2519                            rptr->depth-1);
2520             (void)sprintf (namebuf, "%s%*s",
2521                            rptr->name,
2522                            (int)(strlen(rptr->name))-((int)max_namelen),
2523                            rangebuf);
2524             }
2525         if (all_unique) {
2526             (void)fprintf (st, "  %s %4d  %s\r\n",
2527                            namebuf,
2528                            rptr->width,
2529                            rptr->desc ? rptr->desc : "");
2530             continue;
2531             }
2532         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2533         if ((trptr == NULL) || (tdptr != dptr))
2534             (void)fprintf (st, "  %s %s %4d  %s\r\n",
2535                            dptr->name,
2536                            namebuf,
2537                            rptr->width,
2538                            rptr->desc ? rptr->desc : "");
2539         else
2540             (void)fprintf (st, "  %*s %s %4d  %s\r\n",
2541                            (int)strlen(dptr->name), "",
2542                            namebuf,
2543                            rptr->width,
2544                            rptr->desc ? rptr->desc : "");
2545         }
2546     FREE (namebuf);
2547     }
2548 }
2549 
2550 void fprint_reg_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2551 {
2552 fprint_reg_help_ex (st, dptr, TRUE);
2553 }
2554 
2555 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2556 {
2557 if (dptr->attach_help) {
2558     (void)fprintf (st, "\r\n%s device ATTACH commands:\r\n\r\n", dptr->name);
2559     dptr->attach_help (st, dptr, NULL, 0, NULL);
2560     return;
2561     }
2562 if (DEV_TYPE(dptr) == DEV_DISK) {
2563     (void)fprintf (st, "\r\n%s device ATTACH commands:\r\n\r\n", dptr->name);
2564     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2565     return;
2566     }
2567 if (DEV_TYPE(dptr) == DEV_TAPE) {
2568     (void)fprintf (st, "\r\n%s device ATTACH commands:\r\n\r\n", dptr->name);
2569     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2570     return;
2571     }
2572 if (!silent) {
2573     (void)fprintf (st, "No ATTACH help is available for the %s device\r\n", dptr->name);
2574     if (dptr->help)
2575         dptr->help (st, dptr, NULL, 0, NULL);
2576     }
2577 }
2578 
2579 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2580 {
2581 MTAB *mptr;
2582 DEBTAB *dep;
2583 t_bool found = FALSE;
2584 char buf[CBUFSIZE], header[CBUFSIZE];
2585 uint32 enabled_units = dptr->numunits;
2586 uint32 unit;
2587 
2588 (void)sprintf (header, "\r\n%s device SET commands:\r\n\r\n", dptr->name);
2589 for (unit=0; unit < dptr->numunits; unit++)
2590     if (dptr->units[unit].flags & UNIT_DIS)
2591         --enabled_units;
2592 if (dptr->modifiers) {
2593     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2594         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2595             continue;                                       /* skip unit only extended modifiers */
2596         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2597             continue;                                       /* skip unit only simple modifiers */
2598         if (mptr->mstring) {
2599             fprint_header (st, &found, header);
2600             (void)sprintf (buf, "SET %s %s%s", sim_dname (dptr),
2601                            mptr->mstring,
2602                            (strchr(mptr->mstring, '=')) \
2603                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2604                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2605                                ? "{=val}" : "")));
2606             if ((strlen (buf) < 30) || (NULL == mptr->help))
2607                 (void)fprintf (st, "%-30s\t%s\r\n", buf, mptr->help ? mptr->help : "");
2608             else
2609                 (void)fprintf (st, "%s\r\n%-30s\t%s\r\n", buf, "", mptr->help);
2610             }
2611         }
2612     }
2613 if (dptr->flags & DEV_DISABLE) {
2614     fprint_header (st, &found, header);
2615     (void)sprintf (buf, "SET %s ENABLE", sim_dname (dptr));
2616     (void)fprintf (st,  "%-30s\tEnables device %s\r\n", buf, sim_dname (dptr));
2617     (void)sprintf (buf, "SET %s DISABLE", sim_dname (dptr));
2618     (void)fprintf (st,  "%-30s\tDisables device %s\r\n", buf, sim_dname (dptr));
2619     }
2620 if (dptr->flags & DEV_DEBUG) {
2621     fprint_header (st, &found, header);
2622     (void)sprintf (buf, "SET %s DEBUG", sim_dname (dptr));
2623     (void)fprintf (st,  "%-30s\tEnables debugging for device %s\r\n", buf, sim_dname (dptr));
2624     (void)sprintf (buf, "SET %s NODEBUG", sim_dname (dptr));
2625     (void)fprintf (st,  "%-30s\tDisables debugging for device %s\r\n", buf, sim_dname (dptr));
2626     if (dptr->debflags) {
2627         t_bool desc_available = FALSE;
2628         strcpy (buf, "");
2629         (void)fprintf (st, "SET %s DEBUG=", sim_dname (dptr));
2630         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2631             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2632             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2633             }
2634         (void)fprintf (st, "\r\n");
2635         (void)fprintf (st,  "%-30s\tEnables specific debugging for device %s\r\n", buf, sim_dname (dptr));
2636         (void)fprintf (st, "SET %s NODEBUG=", sim_dname (dptr));
2637         for (dep = dptr->debflags; dep->name != NULL; dep++)
2638             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2639         (void)fprintf (st, "\r\n");
2640         (void)fprintf (st,  "%-30s\tDisables specific debugging for device %s\r\n", buf, sim_dname (dptr));
2641         if (desc_available) {
2642             (void)fprintf (st, "\r\n*%s device DEBUG settings:\r\n", sim_dname (dptr));
2643             for (dep = dptr->debflags; dep->name != NULL; dep++)
2644                 (void)fprintf (st, "%4s%-12s%s\r\n", "", dep->name, dep->desc ? dep->desc : "");
2645             }
2646         }
2647     }
2648 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2649     if (dptr->units->flags & UNIT_DISABLE) {
2650         fprint_header (st, &found, header);
2651         (void)sprintf (buf, "SET %sn ENABLE", sim_dname (dptr));
2652         (void)fprintf (st,  "%-30s\tEnables unit %sn\r\n", buf, sim_dname (dptr));
2653         (void)sprintf (buf, "SET %sn DISABLE", sim_dname (dptr));
2654         (void)fprintf (st,  "%-30s\tDisables unit %sn\r\n", buf, sim_dname (dptr));
2655         }
2656     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2657         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2658             continue;                                           /* skip device only modifiers */
2659         if ((NULL == mptr->valid) && MODMASK(mptr,MTAB_XTD))
2660             continue;                                           /* skip show only modifiers */
2661         if (mptr->mstring) {
2662             fprint_header (st, &found, header);
2663             (void)sprintf (buf, "SET %s%s %s%s", sim_dname (dptr),
2664                            (dptr->numunits > 1) ? "n" : "0", mptr->mstring,
2665                            (strchr(mptr->mstring, '=')) \
2666                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2667                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2668                                ? "{=val}" : "")));
2669             (void)fprintf (st, "%-30s\t%s\r\n", buf,
2670                            (strchr(mptr->mstring, '=')) \
2671                                ? "" : (mptr->help ? mptr->help : ""));
2672             }
2673         }
2674     }
2675 if (!found && !silent)
2676     (void)fprintf (st, "No SET help is available for the %s device\r\n", dptr->name);
2677 }
2678 
2679 void fprint_set_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2680 {
2681   fprint_set_help_ex (st, dptr, TRUE);
2682 }
2683 
2684 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2685 {
2686 MTAB *mptr;
2687 t_bool found = FALSE;
2688 char buf[CBUFSIZE], header[CBUFSIZE];
2689 uint32 enabled_units = dptr->numunits;
2690 uint32 unit;
2691 
2692 (void)sprintf (header, "\r\n%s device SHOW commands:\r\n\r\n", dptr->name);
2693 for (unit=0; unit < dptr->numunits; unit++)
2694     if (dptr->units[unit].flags & UNIT_DIS)
2695         --enabled_units;
2696 if (dptr->modifiers) {
2697     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2698         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2699             continue;                                       /* skip unit only extended modifiers */
2700         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2701             continue;                                       /* skip unit only simple modifiers */
2702         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2703             continue;
2704         fprint_header (st, &found, header);
2705         (void)sprintf (buf, "SHOW %s %s%s", sim_dname (dptr),
2706                        mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2707         (void)fprintf (st, "%-30s\t%s\r\n", buf, mptr->help ? mptr->help : "");
2708         }
2709     }
2710 if (dptr->flags & DEV_DEBUG) {
2711     fprint_header (st, &found, header);
2712     (void)sprintf (buf, "SHOW %s DEBUG", sim_dname (dptr));
2713     (void)fprintf (st, "%-30s\tDisplays debugging status for device %s\r\n", buf, sim_dname (dptr));
2714     }
2715 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2716     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2717         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2718             continue;                                           /* skip device only modifiers */
2719         if ((!mptr->disp) || (!mptr->pstring))
2720             continue;
2721         fprint_header (st, &found, header);
2722         (void)sprintf (buf, "SHOW %s%s %s%s", sim_dname (dptr),
2723                        (dptr->numunits > 1) ? "n" : "0", mptr->pstring,
2724                        MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2725         (void)fprintf (st, "%-30s\t%s\r\n", buf, mptr->help ? mptr->help : "");
2726         }
2727     }
2728 if (!found && !silent)
2729     (void)fprintf (st, "No SHOW help is available for the %s device\r\n", dptr->name);
2730 }
2731 
2732 void fprint_show_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2733     {
2734     fprint_show_help_ex (st, dptr, TRUE);
2735     }
2736 
2737 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2738 {
2739 BRKTYPTAB *brkt = dptr->brk_types;
2740 char gbuf[CBUFSIZE];
2741 
2742 if (sim_brk_types == 0) {
2743     if ((dptr != sim_dflt_dev) && (!silent)) {
2744         (void)fprintf (st, "Breakpoints are not supported in the %s simulator\r\n", sim_name);
2745         if (dptr->help)
2746             dptr->help (st, dptr, NULL, 0, NULL);
2747         }
2748     return;
2749     }
2750 if (brkt == NULL) {
2751     int i;
2752 
2753     if (dptr == sim_dflt_dev) {
2754         if (sim_brk_types & ~sim_brk_dflt) {
2755             (void)fprintf (st, "%s supports the following breakpoint types:\r\n", sim_dname (dptr));
2756             for (i=0; i<26; i++) {
2757                 if (sim_brk_types & (1<<i))
2758                     (void)fprintf (st, "  -%c\r\n", 'A'+i);
2759                 }
2760             }
2761         (void)fprintf (st, "The default breakpoint type is: %s\r\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2762         }
2763     return;
2764     }
2765 (void)fprintf (st, "%s supports the following breakpoint types:\r\n", sim_dname (dptr));
2766 while (brkt->btyp) {
2767     (void)fprintf (st, "  %s     %s\r\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2768     ++brkt;
2769     }
2770 (void)fprintf (st, "The default breakpoint type is: %s\r\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2771 }
2772 
2773 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2774 {
2775 char gbuf[CBUFSIZE];
2776 CTAB *cmdp;
2777 
2778 if (*cptr) {
2779     (void)get_glyph (cptr, gbuf, 0);
2780     if ((cmdp = find_cmd (gbuf))) {
2781         if (cmdp->action == &exdep_cmd) {
2782             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2783                 return dptr->help (st, dptr, uptr, flag, cptr);
2784             else
2785                 (void)fprintf (st, "No HELP available for the %s %s command\r\n", cmdp->name, sim_dname(dptr));
2786             return SCPE_OK;
2787             }
2788         if (cmdp->action == &set_cmd) {
2789             fprint_set_help_ex (st, dptr, FALSE);
2790             return SCPE_OK;
2791             }
2792         if (cmdp->action == &show_cmd) {
2793             fprint_show_help_ex (st, dptr, FALSE);
2794             return SCPE_OK;
2795             }
2796         if (cmdp->action == &attach_cmd) {
2797             fprint_attach_help_ex (st, dptr, FALSE);
2798             return SCPE_OK;
2799             }
2800         if (cmdp->action == &brk_cmd) {
2801             fprint_brk_help_ex (st, dptr, FALSE);
2802             return SCPE_OK;
2803             }
2804         if (dptr->help)
2805             return dptr->help (st, dptr, uptr, flag, cptr);
2806         (void)fprintf (st, "No %s HELP is available for the %s device\r\n", cmdp->name, dptr->name);
2807         return SCPE_OK;
2808         }
2809     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2810         fprint_reg_help_ex (st, dptr, FALSE);
2811         return SCPE_OK;
2812         }
2813     if (dptr->help)
2814         return dptr->help (st, dptr, uptr, flag, cptr);
2815     (void)fprintf (st, "No %s HELP is available for the %s device\r\n", gbuf, dptr->name);
2816     return SCPE_OK;
2817     }
2818 if (dptr->help) {
2819     return dptr->help (st, dptr, uptr, flag, cptr);
2820     }
2821 if (dptr->description)
2822     (void)fprintf (st, "%s %s HELP\r\n", dptr->description (dptr), dptr->name);
2823 else
2824     (void)fprintf (st, "%s HELP\r\n", dptr->name);
2825 fprint_set_help_ex    (st, dptr, TRUE);
2826 fprint_show_help_ex   (st, dptr, TRUE);
2827 fprint_attach_help_ex (st, dptr, TRUE);
2828 fprint_reg_help_ex    (st, dptr, TRUE);
2829 fprint_brk_help_ex    (st, dptr, TRUE);
2830 return SCPE_OK;
2831 }
2832 
2833 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
     /* [previous][next][first][last][top][bottom][index][help] */
2834 {
2835 switch (help[0]) {
2836     case '*':
2837         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2838         if (sim_log)
2839             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2840         break;
2841     default:
2842         fputs (help, stdout);
2843         if (sim_log)
2844             fputs (help, sim_log);
2845         break;
2846     }
2847 return SCPE_OK;
2848 }
2849 
2850 t_stat help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2851 {
2852 char gbuf[CBUFSIZE];
2853 CTAB *cmdp;
2854 
2855 GET_SWITCHES (cptr);
2856 if (sim_switches & SWMASK ('F'))
2857     flag = flag | SCP_HELP_FLAT;
2858 if (*cptr) {
2859     cptr = get_glyph (cptr, gbuf, 0);
2860     if ((cmdp = find_cmd (gbuf))) {
2861         if (*cptr) {
2862             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2863                 DEVICE *dptr;
2864                 UNIT *uptr;
2865                 t_stat r;
2866                 cptr = get_glyph (cptr, gbuf, 0);
2867                 dptr = find_unit (gbuf, &uptr);
2868                 if (dptr == NULL)
2869                     dptr = find_dev (gbuf);
2870                 if (dptr != NULL) {
2871                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2872                     if (sim_log)
2873                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2874                     return r;
2875                     }
2876                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2877                     /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2878                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2879                          (cmdp->help))
2880                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2881                     }
2882                 else { /* HELP SHOW xxx (not device or unit) */
2883                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2884                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2885                         return SCPE_ARG;
2886                     return help_cmd_output (flag, shptr->help, NULL);
2887                     }
2888                 return SCPE_ARG;
2889                 }
2890             else
2891                 return SCPE_2MARG;
2892             }
2893         if (cmdp->help) {
2894             if (strcmp (cmdp->name, "HELP") == 0) {
2895 
2896 
2897 
2898 
2899 
2900 
2901 
2902 
2903 
2904 
2905 
2906 
2907 
2908 
2909 
2910 
2911 
2912 
2913 
2914 
2915 
2916 
2917 
2918 
2919 
2920 
2921 
2922 
2923 
2924 
2925 
2926 
2927 
2928 
2929                 }
2930             else {
2931                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2932                     sim_dflt_dev && sim_dflt_dev->help) {
2933                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2934                         if (sim_log)
2935                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2936                     }
2937                 }
2938             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2939             }
2940         else { /* no help so it is likely a command alias */
2941             CTAB *cmdpa;
2942             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2943                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2944                     sim_printf ("%s is an alias for the %s command:\r\n%s",
2945                                 cmdp->name, cmdpa->name, cmdpa->help);
2946                     break;
2947                     }
2948             if (cmdpa->name == NULL)                /* not found? */
2949                 sim_printf ("No help available for the %s command\r\n", cmdp->name);
2950             }
2951         }
2952     else {
2953         DEVICE *dptr;
2954         UNIT *uptr;
2955         t_stat r;
2956         dptr = find_unit (gbuf, &uptr);
2957         if (dptr == NULL) {
2958             dptr = find_dev (gbuf);
2959             if (dptr == NULL)
2960                 return SCPE_ARG;
2961             if (dptr->flags & DEV_DIS)
2962                 sim_printf ("Device %s is currently disabled\r\n", dptr->name);
2963             }
2964         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2965         if (sim_log)
2966             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2967         return r;
2968         }
2969     }
2970 else {
2971     fprint_help (stdout);
2972     if (sim_log)
2973         fprint_help (sim_log);
2974     }
2975 return SCPE_OK;
2976 }
2977 
2978 /* Spawn command */
2979 
2980 t_stat spawn_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2981 {
2982 t_stat status;
2983 if ((cptr == NULL) || (strlen (cptr) == 0))
2984     cptr = getenv("SHELL");
2985 if ((cptr == NULL) || (strlen (cptr) == 0))
2986     cptr = getenv("ComSpec");
2987 (void)fflush(stdout);                                   /* flush stdout */
2988 if (sim_log)                                            /* flush log if enabled */
2989     (void)fflush (sim_log);
2990 if (sim_deb)                                            /* flush debug if enabled */
2991     (void)fflush (sim_deb);
2992 status = system (cptr);
2993 
2994 return status;
2995 }
2996 
2997 /* Echo command */
2998 
2999 t_stat echo_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3000 {
3001 sim_printf ("%s\r\n", cptr);
3002 return SCPE_OK;
3003 }
3004 
3005 /*
3006  * DO command
3007  *
3008  * Note that SCPE_STEP ("Step expired") is considered a note and
3009  * not an error; it does not abort command execution when using -E.
3010  *
3011  * Inputs:
3012  *      flag    =   caller and nesting level indicator
3013  *      fcptr   =   filename and optional arguments, space-separated
3014  * Outputs:
3015  *      status  =   error status
3016  *
3017  * The "flag" input value indicates the source of the call, as follows:
3018  *
3019  *      -1      =   initialization file (no error if not found)
3020  *       0      =   command line file
3021  *       1      =   "DO" command
3022  *      >1      =   nested "DO" command
3023  */
3024 
3025 t_stat do_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3026 {
3027 return do_cmd_label (flag, fcptr, NULL);
3028 }
3029 
3030 static char *do_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
3031 {
3032 static char cbuf[4*CBUFSIZE];
3033 
3034 (void)snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth],
3035                 sim_do_label[sim_do_depth] ? "::" : "",
3036                 sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "",
3037                 sim_goto_line[sim_do_depth]);
3038 return cbuf;
3039 }
3040 
3041 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
3042 {
3043 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
3044 CONST char *cptr;
3045 FILE *fpin;
3046 CTAB *cmdp = NULL;
3047 int32 echo, nargs, errabort, i;
3048 int32 saved_sim_do_echo = sim_do_echo,
3049       saved_sim_show_message = sim_show_message,
3050       saved_sim_on_inherit = sim_on_inherit,
3051       saved_sim_quiet = sim_quiet;
3052 t_bool staying;
3053 t_stat stat, stat_nomessage;
3054 
3055 stat = SCPE_OK;
3056 staying = TRUE;
3057 if (flag > 0)                                           /* need switches? */
3058     GET_SWITCHES (fcptr);                               /* get switches */
3059 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
3060 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
3061 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
3062 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
3063 
3064 abuf[sizeof(abuf)-1] = '\0';
3065 strncpy (abuf, fcptr, sizeof(abuf)-1);
3066 c = abuf;
3067 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
3068 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
3069     while (sim_isspace (*c))                            /* skip blanks */
3070         c++;
3071     if (*c == 0)                                        /* all done? */
3072         do_arg [nargs++] = NULL;                        /* null argument */
3073     else {
3074         if (*c == '\'' || *c == '"')                    /* quoted string? */
3075             quote = *c++;
3076         else quote = 0;
3077         do_arg[nargs++] = c;                            /* save start */
3078         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
3079             c++;
3080         if (*c)                                         /* term at quote/spc */
3081             *c++ = 0;
3082         }
3083     }                                                   /* end for */
3084 
3085 if (do_arg [0] == NULL)                                 /* need at least 1 */
3086     return SCPE_2FARG;
3087 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
3088     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
3089     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
3090         if (flag == 0)                                  /* cmd line file? */
3091              (void)fprintf (stderr, "Can't open file %s\r\n", do_arg[0]);
3092         return SCPE_OPENERR;                            /* return failure */
3093         }
3094     }
3095 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
3096     ++sim_do_depth;
3097     if (sim_on_inherit) {                               /* inherit ON condition actions? */
3098         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
3099         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
3100             if (sim_on_actions[sim_do_depth-1][i]) {
3101                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
3102                 if (NULL == sim_on_actions[sim_do_depth][i]) {
3103                     while (--i >= 0) {
3104                         FREE(sim_on_actions[sim_do_depth][i]);
3105                         sim_on_actions[sim_do_depth][i] = NULL;
3106                         }
3107                     sim_on_check[sim_do_depth] = 0;
3108                     sim_brk_clract ();                  /* defang breakpoint actions */
3109                     --sim_do_depth;                     /* unwind nesting */
3110                     fclose(fpin);
3111                     return SCPE_MEM;
3112                     }
3113                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
3114                 }
3115             }
3116         }
3117     }
3118 
3119 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
3120 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
3121 sim_goto_line[sim_do_depth] = 0;
3122 if (label) {
3123     sim_gotofile = fpin;
3124     sim_do_echo = echo;
3125     stat = goto_cmd (0, label);
3126     if (stat != SCPE_OK) {
3127         strcpy(cbuf, "RETURN SCPE_ARG");
3128         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
3129         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
3130         goto Cleanup_Return;
3131         }
3132     }
3133 if (errabort)                                           /* -e flag? */
3134     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
3135 
3136 do {
3137     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
3138     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
3139         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
3140         sim_goto_line[sim_do_depth] += 1;
3141         }
3142     if (cptr != NULL && strlen(cptr) < sizeof(cbuf)) {  /* validate */
3143         sim_sub_args(cbuf, sizeof(cbuf), do_arg);       /* substitute args */
3144         }
3145     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
3146     if (cptr == NULL) {                                 /* EOF? */
3147         stat = SCPE_OK;                                 /* set good return */
3148         break;
3149         }
3150     if (*cptr == 0)                                     /* ignore blank */
3151         continue;
3152     if (echo)                                           /* echo if -v */
3153         sim_printf("%s> %s\r\n", do_position(), cptr);
3154     if (*cptr == ':')                                   /* ignore label */
3155         continue;
3156     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
3157     sim_switches = 0;                                   /* init switches */
3158     sim_gotofile = fpin;
3159     sim_do_echo = echo;
3160     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
3161         if (cmdp->action == &return_cmd)                /* RETURN command? */
3162             break;                                      /*    done! */
3163         if (cmdp->action == &do_cmd) {                  /* DO command? */
3164             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
3165                 stat = SCPE_NEST;
3166             else
3167                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
3168             }
3169         else
3170             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
3171         }
3172     else stat = SCPE_UNK;                               /* bad cmd given */
3173     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
3174     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
3175     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
3176     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
3177     if (cmdp)
3178       if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
3179           ((cmdp->action != &return_cmd) &&
3180            (cmdp->action != &goto_cmd) &&
3181            (cmdp->action != &on_cmd) &&
3182            (cmdp->action != &echo_cmd)))
3183         sim_last_cmd_stat = stat;                       /* save command error status */
3184     switch (stat) {
3185         case SCPE_AFAIL:
3186             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
3187                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
3188             break;
3189         case SCPE_EXIT:
3190             staying = FALSE;
3191             break;
3192         case SCPE_OK:
3193         case SCPE_STEP:
3194             break;
3195         default:
3196             break;
3197         }
3198     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
3199         (stat != SCPE_STEP)) {
3200         if (!echo && !sim_quiet &&                      /* report if not echoing */
3201             !stat_nomessage &&                          /* and not suppressing messages */
3202             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
3203             sim_printf("%s> %s\r\n", do_position(), sim_do_ocptr[sim_do_depth]);
3204             }
3205         }
3206     if (!stat_nomessage) {                              /* report error if not suppressed */
3207         if (cmdp && cmdp->message)                      /* special message handler */
3208             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
3209         else
3210             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
3211                 sim_printf ("%s\r\n", sim_error_text (stat));
3212         }
3213     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
3214         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
3215     if (staying &&
3216         (sim_on_check[sim_do_depth]) &&
3217         (stat != SCPE_OK) &&
3218         (stat != SCPE_STEP)) {
3219         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
3220             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
3221         else
3222             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
3223         }
3224     if (sim_vm_post != NULL)
3225         (*sim_vm_post) (TRUE);
3226     } while (staying);
3227 Cleanup_Return:
3228 if (fpin) //-V547
3229     fclose (fpin);                                      /* close file */
3230 sim_gotofile = NULL;
3231 if (flag >= 0) {
3232     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
3233     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
3234     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
3235     }
3236 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
3237 if ((flag >= 0) || (!sim_on_inherit)) {
3238     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
3239         FREE (sim_on_actions[sim_do_depth][i]);
3240         sim_on_actions[sim_do_depth][i] = NULL;
3241         }
3242     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
3243     }
3244 if (flag >= 0)
3245     --sim_do_depth;                                     /* unwind nesting */
3246 sim_brk_clract ();                                      /* defang breakpoint actions */
3247 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
3248     sim_string_to_stat (cptr, &stat);
3249     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
3250     if (sim_switches & SWMASK ('Q'))
3251         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
3252     return stat;                                        /* return with explicit return status */
3253     }
3254 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
3255 }
3256 
3257 /*
3258  * Substitute_args - replace %n tokens in 'instr' with the do command's arguments
3259  *                   and other environment variables
3260  *
3261  * Calling sequence
3262  * instr        =       input string
3263  * instr_size   =       sizeof input string buffer
3264  * do_arg[10]   =       arguments
3265  *
3266  * Token "%0" expands to the command file name.
3267  * Token %n (n being a single digit) expands to the n'th argument
3268  * Tonen %* expands to the whole set of arguments (%1 ... %9)
3269  *
3270  * The input sequence "\%" represents a literal "%", and "\\" represents a
3271  * literal "\".  All other character combinations are rendered literally.
3272  *
3273  * Omitted parameters result in null-string substitutions.
3274  *
3275  * A Tokens preceded and followed by % characters are expanded as environment
3276  * variables, and if one isn't found then can be one of several special
3277  * variables:
3278  *   %DATE%              yyyy-mm-dd
3279  *   %TIME%              hh:mm:ss
3280  *   %STIME%             hh_mm_ss
3281  *   %CTIME%             Www Mmm dd hh:mm:ss yyyy
3282  *   %STATUS%            Status value from the last command executed
3283  *   %TSTATUS%           The text form of the last status value
3284  *   %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
3285  *   %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
3286  *   %SIM_QUIET%         The Quiet mode of the current Do command file
3287  *   %SIM_MESSAGE%       The message display status of the current Do command file
3288  * Environment variable lookups are done first with the precise name between
3289  * the % characters and if that fails, then the name between the % characters
3290  * is upcased and a lookup of that value is attempted.
3291 
3292  * The first Space delimited token on the line is extracted in uppercase and
3293  * then looked up as an environment variable.  If found it the value is
3294  * substituted for the original string before expanding everything else.  If
3295  * it is not found, then the original beginning token on the line is left
3296  * untouched.
3297  */
3298 
3299 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
     /* [previous][next][first][last][top][bottom][index][help] */
3300 {
3301 char gbuf[CBUFSIZE];
3302 char *ip = instr, *op, *oend, *tmpbuf;
3303 const char *ap;
3304 char rbuf[CBUFSIZE];
3305 int i;
3306 time_t now;
3307 struct tm *tmnow;
3308 
3309 time(&now);
3310 tmnow = localtime(&now);
3311 tmpbuf = (char *)malloc(instr_size);
3312 if (!tmpbuf)
3313   {
3314      (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3315                    __func__, __FILE__, __LINE__);
3316 #if defined(USE_BACKTRACE)
3317 # if defined(SIGUSR2)
3318      (void)raise(SIGUSR2);
3319      /*NOTREACHED*/ /* unreachable */
3320 # endif /* if defined(SIGUSR2) */
3321 #endif /* if defined(USE_BACKTRACE) */
3322      abort();
3323   }
3324 op = tmpbuf;
3325 oend = tmpbuf + instr_size - 2;
3326 while (sim_isspace (*ip))                               /* skip leading spaces */
3327     *op++ = *ip++;
3328 for (; *ip && (op < oend); ) {
3329     if ((ip [0] == '\\') &&                             /* literal escape? */
3330         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
3331         ip++;                                           /* skip '\' */
3332         *op++ = *ip++;                                  /* copy escaped char */
3333         }
3334     else
3335         if ((*ip == '%') &&
3336             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
3337             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
3338                 ap = do_arg[ip[1] - '0'];
3339                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
3340                     if (do_arg[i] == NULL) {
3341                         ap = NULL;
3342                         break;
3343                         }
3344                 ip = ip + 2;
3345                 }
3346             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
3347                 (void)memset (rbuf, '\0', sizeof(rbuf));
3348                 ap = rbuf;
3349                 for (i=1; i<=9; ++i)
3350                     if (do_arg[i] == NULL)
3351                         break;
3352                     else
3353                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
3354                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
3355                                 char quote = '"';
3356                                 if (strchr(do_arg[i], quote))
3357                                     quote = '\'';
3358                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"",
3359                                               (i != 1) ? " " : "", quote,
3360                                               do_arg[i], quote);
3361                                 }
3362                             else
3363                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%s",
3364                                               (i != 1) ? " " : "", do_arg[i]);
3365                             }
3366                         else
3367                             break;
3368                 ip = ip + 2;
3369                 }
3370             else {                                      /* environment variable */
3371                 ap = NULL;
3372                 (void)get_glyph_nc (ip+1, gbuf, '%');   /* first try using the literal name */
3373                 ap = getenv(gbuf);
3374                 if (!ap) {
3375                     (void)get_glyph (ip+1, gbuf, '%');  /* now try using the upcased name */
3376                     ap = getenv(gbuf);
3377                     }
3378                 ip += 1 + strlen (gbuf);
3379                 if (*ip == '%') ++ip;
3380                 if (!ap) {
3381                     /* ISO 8601 format date/time info */
3382                     if (!strcmp ("DATE", gbuf)) {
3383                         (void)sprintf (rbuf, "%4d-%02d-%02d",
3384                                        tmnow->tm_year + 1900,
3385                                        tmnow->tm_mon  + 1,
3386                                        tmnow->tm_mday);
3387                         ap = rbuf;
3388                         }
3389                     else if (!strcmp ("TIME", gbuf)) {
3390                         (void)sprintf (rbuf, "%02d:%02d:%02d",
3391                                        tmnow->tm_hour,
3392                                        tmnow->tm_min,
3393                                        tmnow->tm_sec);
3394                         ap = rbuf;
3395                         }
3396                     else if (!strcmp ("DATETIME", gbuf)) {
3397                         (void)sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d",
3398                                        tmnow->tm_year + 1900,
3399                                        tmnow->tm_mon  + 1,
3400                                        tmnow->tm_mday,
3401                                        tmnow->tm_hour,
3402                                        tmnow->tm_min,
3403                                        tmnow->tm_sec);
3404                         ap = rbuf;
3405                         }
3406                     /* Locale oriented formatted date/time info */
3407                     if (!strcmp ("LDATE", gbuf)) {
3408                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
3409                         ap = rbuf;
3410                         }
3411                     else if (!strcmp ("LTIME", gbuf)) {
3412                         strftime (rbuf, sizeof(rbuf), "%I:%M:%S %p", tmnow);
3413                         ap = rbuf;
3414                         }
3415                     else if (!strcmp ("CTIME", gbuf)) {
3416                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
3417                         ap = rbuf;
3418                         }
3419                     /* Separate Date/Time info */
3420                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
3421                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
3422                         ap = rbuf;
3423                         }
3424                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3425                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
3426                         ap = rbuf;
3427                         }
3428                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
3429                         (void)sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
3430                         ap = rbuf;
3431                         }
3432                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
3433                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
3434                         int year = tmnow->tm_year + 1900;
3435                         int days = year - 2001;
3436                         int leaps = days/4 - days/100 + days/400;
3437                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
3438                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
3439                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
3440                                               96, 80, 92, 76, 88, 72, 84};
3441                         int cal_year = years[selector];
3442 
3443                         if (!strcmp ("DATE_19XX_YY", gbuf))
3444                             (void)sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
3445                         else
3446                             (void)sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
3447                         ap = rbuf;
3448                         }
3449                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
3450                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
3451                         ap = rbuf;
3452                         }
3453                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
3454                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
3455                         ap = rbuf;
3456                         }
3457                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
3458                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
3459                         ap = rbuf;
3460                         }
3461                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
3462                         (void)sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
3463                         ap = rbuf;
3464                         }
3465                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
3466                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
3467                         int iso_yr = tmnow->tm_year + 1900;
3468                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
3469 
3470                         if (iso_wk == 0) {
3471                             iso_yr = iso_yr - 1;
3472                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
3473                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
3474                             }
3475                         else
3476                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
3477                                 ++iso_yr;
3478                                 iso_wk = 1;
3479                                 }
3480                         if (!strcmp ("DATE_WW", gbuf))
3481                             (void)sprintf (rbuf, "%02d", iso_wk);
3482                         else
3483                             (void)sprintf (rbuf, "%04d", iso_yr);
3484                         ap = rbuf;
3485                         }
3486                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
3487                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
3488                         ap = rbuf;
3489                         }
3490                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
3491                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
3492                         ap = rbuf;
3493                         }
3494                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
3495                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
3496                         ap = rbuf;
3497                         }
3498                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
3499                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
3500                         ap = rbuf;
3501                         }
3502                     else if (!strcmp ("STATUS", gbuf)) {
3503                         (void)sprintf (rbuf, "%08X", sim_last_cmd_stat);
3504                         ap = rbuf;
3505                         }
3506                     else if (!strcmp ("TSTATUS", gbuf)) {
3507                         (void)sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
3508                         ap = rbuf;
3509                         }
3510                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
3511                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3512                         ap = rbuf;
3513                         }
3514                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
3515                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3516                         ap = rbuf;
3517                         }
3518                     else if (!strcmp ("SIM_LOCALOPC", gbuf)) {
3519                         (void)sprintf (rbuf, "%s", sim_localopc ? "1" : "");
3520                         ap = rbuf;
3521                         }
3522                     else if (!strcmp ("SIM_QUIET", gbuf)) {
3523                         (void)sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
3524                         ap = rbuf;
3525                         }
3526                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
3527                         (void)sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
3528                         ap = rbuf;
3529                         }
3530                     else if (!strcmp ("HOSTID", gbuf)) {
3531 #if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__) && !defined(__QNX__)
3532                         (void)sprintf (rbuf, "%ld", (long)gethostid());
3533 #else
3534                         (void)sprintf (rbuf, "00000000");
3535 #endif /* if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__) */
3536                         ap = rbuf;
3537                         }
3538                     else if (!strcmp ("UID", gbuf)) {
3539 #if defined(HAVE_UNISTD)
3540                         (void)sprintf (rbuf, "%ld", (long)getuid());
3541 #else
3542                         (void)sprintf (rbuf, "0");
3543 #endif /* if defined(HAVE_UNISTD) */
3544                         ap = rbuf;
3545                         }
3546                     else if (!strcmp ("GID", gbuf)) {
3547 #if defined(HAVE_UNISTD)
3548                         (void)sprintf (rbuf, "%ld", (long)getgid());
3549 #else
3550                         (void)sprintf (rbuf, "0");
3551 #endif /* if defined(HAVE_UNISTD) */
3552                         ap = rbuf;
3553                         }
3554                     else if (!strcmp ("EUID", gbuf)) {
3555 #if defined(HAVE_UNISTD)
3556                         (void)sprintf (rbuf, "%ld", (long)geteuid());
3557 #else
3558                         (void)sprintf (rbuf, "0");
3559 #endif /* if defined(HAVE_UNISTD) */
3560                         ap = rbuf;
3561                         }
3562                     else if (!strcmp ("EGID", gbuf)) {
3563 #if defined(HAVE_UNISTD)
3564                         (void)sprintf (rbuf, "%ld", (long)getegid());
3565 #else
3566                         (void)sprintf (rbuf, "0");
3567 #endif /* if defined(HAVE_UNISTD) */
3568                         ap = rbuf;
3569                         }
3570                     else if (!strcmp ("PID", gbuf)) {
3571 #if defined(HAVE_UNISTD)
3572                         (void)sprintf (rbuf, "%ld", (long)_sir_getpid());
3573 #else
3574                         (void)sprintf (rbuf, "0");
3575 #endif /* if defined(HAVE_UNISTD) */
3576                         ap = rbuf;
3577                         }
3578                     else if (!strcmp ("PPID", gbuf)) {
3579 #if defined(HAVE_UNISTD)
3580                         (void)sprintf (rbuf, "%ld", (long)getppid());
3581 #else
3582                         (void)sprintf (rbuf, "0");
3583 #endif /* if defined(HAVE_UNISTD) */
3584                         ap = rbuf;
3585                         }
3586                     else if (!strcmp ("PGID", gbuf)) {
3587 #if defined(HAVE_UNISTD)
3588                         (void)sprintf (rbuf, "%ld", (long)getpgid(_sir_getpid()));
3589 #else
3590                         (void)sprintf (rbuf, "0");
3591 #endif /* if defined(HAVE_UNISTD) */
3592                         ap = rbuf;
3593                         }
3594                     else if (!strcmp ("SID", gbuf)) {
3595 #if defined(HAVE_UNISTD)
3596                         (void)sprintf (rbuf, "%ld", (long)getsid(_sir_getpid()));
3597 #else
3598                         (void)sprintf (rbuf, "0");
3599 #endif /* if defined(HAVE_UNISTD) */
3600                         ap = rbuf;
3601                         }
3602                     else if (!strcmp ("ENDIAN", gbuf)) {
3603 #if ( defined(DECLITEND) && DECLITEND == 1 )
3604                         (void)sprintf (rbuf, "LITTLE");
3605 #elif ( defined(DECLITEND) && DECLITEND == 0 )
3606                         (void)sprintf (rbuf, "BIG");
3607 #else
3608                         (void)sprintf (rbuf, "UNKNOWN");
3609 #endif /* if ( defined(DECLITEND) && DECLITEND == 1 ) */
3610                         ap = rbuf;
3611                         }
3612                     else if (!strcmp("SIM_NAME", gbuf)) {
3613                         (void)sprintf (rbuf, "%s", sim_name);
3614                         ap = rbuf;
3615                         }
3616                     else if (!strcmp("SIM_VERSION", gbuf)) {
3617 #if defined(VER_H_GIT_VERSION)
3618                         (void)sprintf (rbuf, "%s", VER_H_GIT_VERSION);
3619 #else
3620                         (void)sprintf (rbuf, "UNKNOWN");
3621 #endif /* if defined(VER_H_GIT_VERSION) */
3622                         ap = rbuf;
3623                         }
3624                     else if (!strcmp("SIM_HASH", gbuf)) {
3625 #if defined(VER_H_GIT_HASH)
3626                         (void)sprintf (rbuf, "%s", VER_H_GIT_HASH);
3627 #else
3628                         (void)sprintf (rbuf, "0000000000000000000000000000000000000000");
3629 #endif /* if defined(VER_H_GIT_HASH) */
3630                         ap = rbuf;
3631                         }
3632                     else if (!strcmp("SIM_RELT", gbuf)) {
3633 #if defined(VER_H_GIT_RELT)
3634                         (void)sprintf (rbuf, "%s", VER_H_GIT_RELT);
3635 #else
3636                         (void)sprintf (rbuf, "X");
3637 #endif /* if defined(VER_H_GIT_RELT) */
3638                         ap = rbuf;
3639                         }
3640                     else if (!strcmp("SIM_DATE", gbuf)) {
3641 #if defined(VER_H_GIT_DATE)
3642                         (void)sprintf (rbuf, "%s", VER_H_GIT_DATE);
3643 #else
3644                         (void)sprintf (rbuf, "UNKNOWN");
3645 #endif /* if defined(VER_H_GIT_DATE) */
3646                         ap = rbuf;
3647                         }
3648                     else if ((!strcmp("CORES", gbuf)) ||
3649                              (!strcmp("SIM_CORES", gbuf))) {
3650                         (void)sprintf(rbuf, "%u", (ncores >= 1 && nprocs >= 1) ? ((ncores > nprocs) ? nprocs : ncores) : 1);
3651                         ap = rbuf;
3652                         }
3653                     else if ((!strcmp("CPUS", gbuf)) ||
3654                              (!strcmp("SIM_PROCESSORS", gbuf))) {
3655                         (void)sprintf(rbuf, "%u", (nprocs < 2) ? 1U : nprocs);
3656                         ap = rbuf;
3657                         }
3658                     }
3659                 }
3660             if (ap) {                                   /* non-null arg? */
3661                 while (*ap && (op < oend))              /* copy the argument */
3662                     *op++ = *ap++;
3663                 }
3664             }
3665         else
3666             *op++ = *ip++;
3667     }
3668 *op = 0;                                                /* term buffer */
3669 strcpy (instr, tmpbuf);
3670 FREE (tmpbuf);
3671 return;
3672 }
3673 
3674 static
3675 int sim_cmp_string (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
3676 {
3677 long int v1, v2;
3678 char *ep1, *ep2;
3679 
3680 v1 = strtol(s1+1, &ep1, 0);
3681 v2 = strtol(s2+1, &ep2, 0);
3682 if ((ep1 != s1 + strlen (s1) - 1) ||
3683     (ep2 != s2 + strlen (s2) - 1))
3684     return strcmp (s1, s2);
3685 if (v1 == v2)
3686     return 0;
3687 if (v1 < v2)
3688     return -1;
3689 return 1;
3690 }
3691 
3692 /* Assert command */
3693 
3694 t_stat assert_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3695 {
3696 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3697 CONST char *tptr, *gptr;
3698 REG *rptr;
3699 uint32 idx = 0;
3700 t_value val;
3701 t_stat r;
3702 t_bool Not = FALSE;
3703 t_bool result;
3704 t_addr addr = 0;
3705 t_stat reason = SCPE_AFAIL; /* default fail reason */
3706 
3707 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3708                                                         /* get sw, default */
3709 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3710 # pragma diagnostic push
3711 # pragma diag_suppress = integer_sign_change
3712 #endif
3713 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3714 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3715 # pragma diagnostic pop
3716 #endif
3717 if (*cptr == 0)                                         /* must be more */
3718     return SCPE_2FARG;
3719 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3720 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3721     Not = TRUE;                                         /* remember that, and */
3722     cptr = (CONST char *)tptr;
3723     }
3724 if (*cptr == '"') {                                     /* quoted string comparison? */
3725     char op[CBUFSIZE];
3726     static struct {
3727         const char *op;
3728         int aval;
3729         int bval;
3730         t_bool invert;
3731         } *optr, compare_ops[] =
3732         {
3733             { "==",   0,  0, FALSE },
3734             { "EQU",  0,  0, FALSE },
3735             { "!=",   0,  0, TRUE  },
3736             { "NEQ",  0,  0, TRUE  },
3737             { "<",   -1, -1, FALSE },
3738             { "LSS", -1, -1, FALSE },
3739             { "<=",   0, -1, FALSE },
3740             { "LEQ",  0, -1, FALSE },
3741             { ">",    1,  1, FALSE },
3742             { "GTR",  1,  1, FALSE },
3743             { ">=",   0,  1, FALSE },
3744             { "GEQ",  0,  1, FALSE },
3745             { NULL }
3746         };
3747 
3748     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3749                                                     /* get first string */
3750     if (!*tptr)
3751         return SCPE_2FARG;
3752     cptr += strlen (gbuf);
3753     while (sim_isspace (*cptr))                     /* skip spaces */
3754         ++cptr;
3755     (void)get_glyph (cptr, op, '"');
3756     for (optr = compare_ops; optr->op; optr++)
3757         if (0 == strcmp (op, optr->op))
3758             break;
3759     if (!optr->op)
3760         return sim_messagef (SCPE_ARG, "Invalid operator: %s\r\n", op);
3761     cptr += strlen (op);
3762     while (sim_isspace (*cptr))                         /* skip spaces */
3763         ++cptr;
3764     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3765                                                         /* get second string */
3766     if (*cptr) {                                        /* more? */
3767         if (flag)                                       /* ASSERT has no more args */
3768             return SCPE_2MARG;
3769         }
3770     else {
3771         if (!flag)
3772             return SCPE_2FARG;                          /* IF needs actions! */
3773         }
3774     result = sim_cmp_string (gbuf, gbuf2);
3775     result = ((result == optr->aval) || (result == optr->bval));
3776     if (optr->invert)
3777         result = !result;
3778     }
3779 else {
3780     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3781     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3782     if (rptr) {                                         /* got register? */
3783         if (*gptr == '[') {                             /* subscript? */
3784             if (rptr->depth <= 1)                       /* array register? */
3785                 return SCPE_ARG;
3786             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3787             if ((gptr == tptr) || (*tptr++ != ']'))
3788                 return SCPE_ARG;
3789             gptr = tptr;                                /* update */
3790             }
3791         else idx = 0;                                   /* not array */
3792         if (idx >= rptr->depth)                         /* validate subscript */
3793             return SCPE_SUB;
3794         }
3795     else {                                              /* not reg, check for memory */
3796         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3797             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3798         else if (sim_dfdev) //-V547
3799             addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix); //-V547
3800         if (gbuf == gptr)                               /* error? */
3801             return SCPE_NXREG;
3802         }
3803     if (*gptr != 0)                                     /* more? must be search */
3804         (void)get_glyph (gptr, gbuf, 0);
3805     else {
3806         if (*cptr == 0)                                 /* must be more */
3807             return SCPE_2FARG;
3808         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3809         }
3810     if (*cptr) {                                        /* more? */
3811         if (flag)                                       /* ASSERT has no more args */
3812             return SCPE_2MARG;
3813         }
3814     else {
3815         if (!flag)
3816             return SCPE_2FARG;                          /* IF needs actions! */
3817         }
3818     if (rptr) {                                         /* Handle register case */
3819 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3820 # pragma diagnostic push
3821 # pragma diag_suppress = integer_sign_change
3822 #endif
3823         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3824             (sim_stabr.boolop == -1))                   /* relational op reqd */
3825             return SCPE_MISVAL;
3826 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3827 # pragma diagnostic pop
3828 #endif
3829         val = get_rval (rptr, idx);                     /* get register value */
3830         result = test_search (&val, &sim_stabr);        /* test condition */
3831         }
3832     else {                                              /* Handle memory case */
3833 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3834 # pragma diagnostic push
3835 # pragma diag_suppress = integer_sign_change
3836 #endif
3837         if (sim_dfdev)
3838             if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3839                 (sim_staba.boolop == -1))                    /* relational op reqd */
3840                 return SCPE_MISVAL;
3841 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3842 # pragma diagnostic pop
3843 #endif
3844         if (sim_dfdev)
3845             reason = get_aval (addr, sim_dfdev, sim_dfunit); /* get data */
3846         if (reason != SCPE_OK)                          /* return if error */
3847             return reason;
3848         result = test_search (sim_eval, &sim_staba);    /* test condition */
3849         }
3850     }
3851 if (Not ^ result) {
3852     if (!flag)
3853         sim_brk_setact (cptr);                          /* set up IF actions */
3854     }
3855 else
3856     if (flag)
3857         return SCPE_AFAIL;                              /* return assert status */
3858 return SCPE_OK;
3859 }
3860 
3861 /* Send command */
3862 
3863 t_stat send_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3864 {
3865 char gbuf[CBUFSIZE];
3866 CONST char *tptr;
3867 uint8 dbuf[CBUFSIZE];
3868 uint32 dsize = 0;
3869 uint32 delay = 0;
3870 uint32 after = 0;
3871 t_stat r;
3872 SEND *snd = NULL;
3873 
3874 GET_SWITCHES (cptr);                                    /* get switches */
3875 tptr = get_glyph (cptr, gbuf, ',');
3876 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3877     r = tmxr_locate_line_send (gbuf, &snd);
3878     if (r != SCPE_OK)
3879       return r;
3880     cptr = tptr;
3881     tptr = get_glyph (tptr, gbuf, ',');
3882     }
3883 else
3884     snd = sim_cons_get_send ();
3885 
3886 while (*cptr) {
3887     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3888         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3889         if (r != SCPE_OK)
3890             return sim_messagef (SCPE_ARG, "Invalid Delay Value\r\n");
3891         cptr = tptr;
3892         tptr = get_glyph (cptr, gbuf, ',');
3893         continue;
3894         }
3895     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3896         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3897         if (r != SCPE_OK)
3898             return sim_messagef (SCPE_ARG, "Invalid After Value\r\n");
3899         cptr = tptr;
3900         tptr = get_glyph (cptr, gbuf, ',');
3901         continue;
3902         }
3903     if ((*cptr == '"') || (*cptr == '\''))
3904         break;
3905     return SCPE_ARG;
3906     }
3907 if (*cptr) {
3908     if ((*cptr != '"') && (*cptr != '\'')) //-V560
3909         return sim_messagef (SCPE_ARG, "String must be quote delimited\r\n");
3910     cptr = get_glyph_quoted (cptr, gbuf, 0);
3911     if (*cptr != '\0')
3912         return SCPE_2MARG;                  /* No more arguments */
3913 
3914     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3915         return sim_messagef (SCPE_ARG, "Invalid String\r\n");
3916     }
3917 if ((dsize == 0) && (delay == 0) && (after == 0))
3918     return SCPE_2FARG;
3919 return sim_send_input (snd, dbuf, dsize, after, delay);
3920 }
3921 
3922 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3923 {
3924 char gbuf[CBUFSIZE];
3925 CONST char *tptr;
3926 t_stat r;
3927 SEND *snd = NULL;
3928 
3929 tptr = get_glyph (cptr, gbuf, ',');
3930 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3931     r = tmxr_locate_line_send (gbuf, &snd);
3932     if (r != SCPE_OK)
3933       return r;
3934     cptr = tptr;
3935     }
3936 else
3937     snd = sim_cons_get_send ();
3938 if (*cptr)
3939     return SCPE_2MARG;
3940 return sim_show_send_input (st, snd);
3941 }
3942 
3943 t_stat expect_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3944 {
3945 char gbuf[CBUFSIZE];
3946 CONST char *tptr;
3947 EXPECT *exp = NULL;
3948 
3949 GET_SWITCHES (cptr);                                    /* get switches */
3950 tptr = get_glyph (cptr, gbuf, ',');
3951 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3952     cptr = tptr;
3953 } else {
3954     exp = sim_cons_get_expect ();
3955 }
3956 
3957 if (flag) {
3958     return sim_set_expect (exp, cptr);
3959 } else {
3960     if (exp == NULL) {
3961         exp = sim_cons_get_expect();
3962     }
3963     return sim_set_noexpect (exp, cptr);
3964 }
3965 }
3966 
3967 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3968 {
3969 char gbuf[CBUFSIZE];
3970 CONST char *tptr;
3971 EXPECT *exp = NULL;
3972 t_stat r;
3973 
3974 tptr = get_glyph (cptr, gbuf, ',');
3975 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3976     r = tmxr_locate_line_expect (gbuf, &exp);
3977     if (r != SCPE_OK)
3978         return r;
3979     cptr = tptr;
3980     }
3981 else
3982     exp = sim_cons_get_expect ();
3983 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3984     return SCPE_ARG;            /* String must be quote delimited */
3985 tptr = get_glyph_quoted (cptr, gbuf, 0);
3986 if (*tptr != '\0')
3987     return SCPE_2MARG;          /* No more arguments */
3988 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3989     return SCPE_ARG;            /* String must be quote delimited */
3990 return sim_exp_show (st, exp, gbuf);
3991 }
3992 
3993 /* Goto command */
3994 
3995 t_stat goto_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3996 {
3997 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3998 const char *cptr;
3999 long fpos;
4000 int32 saved_do_echo = sim_do_echo;
4001 int32 saved_goto_line = sim_goto_line[sim_do_depth];
4002 
4003 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
4004 (void)get_glyph (fcptr, gbuf1, 0);
4005 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
4006 fpos = ftell(sim_gotofile);                             /* Save start position */
4007 rewind(sim_gotofile);                                   /* start search for label */
4008 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
4009 sim_do_echo = 0;                                        /* Don't echo while searching for label */
4010 while (1) {
4011     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
4012     if (cptr == NULL) break;                            /* exit on eof */
4013     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
4014     if (*cptr == 0) continue;                           /* ignore blank */
4015     if (*cptr != ':') continue;                         /* ignore non-labels */
4016     ++cptr;                                             /* skip : */
4017     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
4018     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
4019     if (0 == strcmp(gbuf, gbuf1)) {
4020         sim_brk_clract ();                              /* goto defangs current actions */
4021         sim_do_echo = saved_do_echo;                    /* restore echo mode */
4022         if (sim_do_echo)                                /* echo if -v */
4023             sim_printf("%s> %s\r\n", do_position(), cbuf);
4024         return SCPE_OK;
4025         }
4026     }
4027 sim_do_echo = saved_do_echo;                       /* restore echo mode         */
4028 fseek(sim_gotofile, fpos, SEEK_SET);               /* restore start position    */
4029 sim_goto_line[sim_do_depth] = saved_goto_line;     /* restore start line number */
4030 return SCPE_ARG;
4031 }
4032 
4033 /* Return command */
4034 
4035 /* The return command is invalid unless encountered in a do_cmd context,    */
4036 /* and in that context, it is handled as a special case inside of do_cmd()  */
4037 /* and not dispatched here, so if we get here a return has been issued from */
4038 /* interactive input */
4039 
4040 t_stat return_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4041 {
4042 return SCPE_UNK;                                 /* only valid inside of do_cmd */
4043 }
4044 
4045 /* Shift command */
4046 
4047 /* The shift command is invalid unless encountered in a do_cmd context,    */
4048 /* and in that context, it is handled as a special case inside of do_cmd() */
4049 /* and not dispatched here, so if we get here a shift has been issued from */
4050 /* interactive input (it is not valid interactively since it would have to */
4051 /* mess with the program's argv which is owned by the C runtime library    */
4052 
4053 t_stat shift_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4054 {
4055 return SCPE_UNK;                                 /* only valid inside of do_cmd */
4056 }
4057 
4058 /* Call command */
4059 
4060 /* The call command is invalid unless encountered in a do_cmd context,     */
4061 /* and in that context, it is handled as a special case inside of do_cmd() */
4062 /* and not dispatched here, so if we get here a call has been issued from  */
4063 /* interactive input                                                       */
4064 
4065 t_stat call_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4066 {
4067 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
4068 const char *cptr;
4069 
4070 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
4071 cptr = get_glyph (fcptr, gbuf, 0);
4072 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
4073 (void)snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
4074 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
4075 return do_cmd_label (flag, cbuf, gbuf);
4076 }
4077 
4078 /* On command */
4079 
4080 t_stat on_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4081 {
4082 char gbuf[CBUFSIZE];
4083 t_stat cond;
4084 
4085 cptr = get_glyph (cptr, gbuf, 0);
4086 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
4087 if (0 == strcmp("ERROR", gbuf))
4088     cond = 0;
4089 else
4090     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
4091         return SCPE_ARG;
4092 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
4093     FREE(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
4094     sim_on_actions[sim_do_depth][cond] = NULL; }
4095 else {
4096     sim_on_actions[sim_do_depth][cond] =
4097         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
4098     if (!sim_on_actions[sim_do_depth][cond])
4099       {
4100         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
4101                        __func__, __FILE__, __LINE__);
4102 #if defined(USE_BACKTRACE)
4103 # if defined(SIGUSR2)
4104         (void)raise(SIGUSR2);
4105         /*NOTREACHED*/ /* unreachable */
4106 # endif /* if defined(SIGUSR2) */
4107 #endif /* if defined(USE_BACKTRACE) */
4108         abort();
4109       }
4110     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
4111     }
4112 return SCPE_OK;
4113 }
4114 
4115 /* noop command */
4116 
4117 t_stat noop_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4118 {
4119 if (cptr && (*cptr != 0))                               /* now eol? */
4120     return SCPE_2MARG;
4121 return SCPE_OK;                                         /* we're happy doing nothing */
4122 }
4123 
4124 /* Set on/noon routine */
4125 
4126 t_stat set_on (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4127 {
4128 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
4129     char gbuf[CBUFSIZE];
4130 
4131     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
4132     if (((MATCH_CMD(gbuf,"INHERIT")) &&
4133          (MATCH_CMD(gbuf,"NOINHERIT"))) || //-V600
4134         (*cptr))
4135         return SCPE_2MARG;
4136     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT"))) //-V560
4137         sim_on_inherit = 1;
4138     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT"))) //-V560
4139         sim_on_inherit = 0;
4140     return SCPE_OK;
4141     }
4142 if (cptr && (*cptr != 0))                               /* now eol? */
4143     return SCPE_2MARG;
4144 sim_on_check[sim_do_depth] = flag;
4145 if ((sim_do_depth != 0) &&
4146     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
4147       sim_on_actions[sim_do_depth][0] =                 /* No, so make "RETURN" */
4148           (char *)malloc(1+strlen("RETURN"));           /* be the default action */
4149       if (sim_on_actions[sim_do_depth][0] == NULL)
4150       {
4151           (void)sir_emerg ("FATAL: Out of memory! Aborting at %s[%s:%d]",
4152                            __func__, __FILE__, __LINE__);
4153 #if defined(USE_BACKTRACE)
4154 # if defined(SIGUSR2)
4155           (void)raise(SIGUSR2);
4156           /*NOTREACHED*/ /* unreachable */
4157 # endif /* if defined(SIGUSR2) */
4158 #endif /* if defined(USE_BACKTRACE) */
4159           abort();
4160       }
4161       strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
4162     }
4163 if ((sim_do_depth != 0) &&
4164     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
4165     sim_on_actions[sim_do_depth][SCPE_AFAIL] =           /* No, so make "RETURN" */
4166         (char *)malloc(1+strlen("RETURN"));              /* be the action */
4167       if (sim_on_actions[sim_do_depth][SCPE_AFAIL] == NULL)
4168       {
4169           (void)sir_emerg ("FATAL: Out of memory! Aborting at %s[%s:%d]",
4170                            __func__, __FILE__, __LINE__);
4171 #if defined(USE_BACKTRACE)
4172 # if defined(SIGUSR2)
4173           (void)raise(SIGUSR2);
4174           /*NOTREACHED*/ /* unreachable */
4175 # endif /* if defined(SIGUSR2) */
4176 #endif /* if defined(USE_BACKTRACE) */
4177           abort();
4178       }
4179     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
4180     }
4181 return SCPE_OK;
4182 }
4183 
4184 /* Set verify/noverify routine */
4185 
4186 t_stat set_verify (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4187 {
4188 if (cptr && (*cptr != 0))                               /* now eol? */
4189     return SCPE_2MARG;
4190 if (flag == sim_do_echo)                                /* already set correctly? */
4191     return SCPE_OK;
4192 sim_do_echo = flag;
4193 return SCPE_OK;
4194 }
4195 
4196 /* Set message/nomessage routine */
4197 
4198 t_stat set_message (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4199 {
4200 if (cptr && (*cptr != 0))                               /* now eol? */
4201     return SCPE_2MARG;
4202 if (flag == sim_show_message)                           /* already set correctly? */
4203     return SCPE_OK;
4204 sim_show_message = flag;
4205 return SCPE_OK;
4206 }
4207 
4208 /* Set localopc/nolocalopc routine */
4209 
4210 t_stat set_localopc (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4211 {
4212 if (cptr && (*cptr != 0))                               /* now eol? */
4213     return SCPE_2MARG;
4214 if (flag == sim_localopc)                               /* already set correctly? */
4215     return SCPE_OK;
4216 sim_localopc = flag;
4217 return SCPE_OK;
4218 }
4219 /* Set quiet/noquiet routine */
4220 
4221 t_stat set_quiet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4222 {
4223 if (cptr && (*cptr != 0))                               /* now eol? */
4224     return SCPE_2MARG;
4225 if (flag == sim_quiet)                                  /* already set correctly? */
4226     return SCPE_OK;
4227 sim_quiet = flag;
4228 return SCPE_OK;
4229 }
4230 
4231 /* Set environment routine */
4232 
4233 t_stat sim_set_environment (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4234 {
4235 char varname[CBUFSIZE];
4236 
4237 if ((NULL == cptr) || (*cptr == 0))                            /* now eol? */
4238     return SCPE_2FARG;
4239 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
4240 setenv(varname, cptr, 1);
4241 return SCPE_OK;
4242 }
4243 
4244 /* Set command */
4245 
4246 t_stat set_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4247 {
4248 uint32 lvl = 0;
4249 t_stat r;
4250 char gbuf[CBUFSIZE], *cvptr;
4251 CONST char *svptr;
4252 DEVICE *dptr;
4253 UNIT *uptr;
4254 MTAB *mptr;
4255 CTAB *gcmdp;
4256 C1TAB *ctbr = NULL, *glbr;
4257 
4258 GET_SWITCHES (cptr);                                    /* get switches */
4259 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
4260     return SCPE_2FARG;
4261 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
4262 
4263 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4264     uptr = dptr->units;                                 /* first unit */
4265     ctbr = set_dev_tab;                                 /* global table */
4266     lvl = MTAB_VDV;                                     /* device match */
4267     GET_SWITCHES (cptr);                                /* get more switches */
4268     }
4269 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4270     if (uptr == NULL)                                   /* invalid unit */
4271         return SCPE_NXUN;
4272     ctbr = set_unit_tab;                                /* global table */
4273     lvl = MTAB_VUN;                                     /* unit match */
4274     GET_SWITCHES (cptr);                                /* get more switches */
4275     }
4276 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
4277     GET_SWITCHES (cptr);                                /* get more switches */
4278     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
4279     }
4280 else {
4281     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4282         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4283             *cvptr++ = 0;
4284         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
4285             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
4286                 dptr = sim_dflt_dev;
4287                 cptr = svptr;
4288                 while (sim_isspace(*cptr))
4289                     ++cptr;
4290                 break;
4291                 }
4292             }
4293         }
4294     if (!dptr)
4295         return SCPE_NXDEV;                              /* no match */
4296     lvl = MTAB_VDV;                                     /* device match */
4297     uptr = dptr->units;                                 /* first unit */
4298     }
4299 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4300     return SCPE_2FARG;
4301 GET_SWITCHES (cptr);                                    /* get more switches */
4302 
4303 while (*cptr != 0) {                                    /* do all mods */
4304     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
4305     if (0 == strcmp (gbuf, ";"))
4306         break;
4307     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4308         *cvptr++ = 0;
4309     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4310         if ((mptr->mstring) &&                          /* match string */
4311             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
4312             if (mptr->mask & MTAB_XTD) {                /* extended? */
4313                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
4314                     return SCPE_ARG;
4315                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
4316                     return SCPE_UDIS;                   /* unit disabled? */
4317                 if (mptr->valid) {                      /* validation rtn? */
4318                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
4319                         svptr = get_glyph_quoted (svptr, gbuf, ',');
4320                         if ((cvptr = strchr (gbuf, '='))) {
4321                             *cvptr++ = 0;
4322                             cptr = svptr;
4323                             }
4324                         }
4325                     else {
4326                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
4327                             (void)get_glyph_nc (svptr, gbuf, ',');
4328                             if ((cvptr = strchr (gbuf, '=')))
4329                                 *cvptr++ = 0;
4330                             }
4331                         }
4332                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
4333                     if (r != SCPE_OK)
4334                         return r;
4335                     }
4336                 else if (!mptr->desc)                   /* value desc? */
4337                     break;
4338                 else if (cvptr)                         /* = value? */
4339                     return SCPE_ARG;
4340                 else *((int32 *) mptr->desc) = mptr->match;
4341                 }                                       /* end if xtd */
4342             else {                                      /* old style */
4343                 if (cvptr)                              /* = value? */
4344                     return SCPE_ARG;
4345                 if (uptr->flags & UNIT_DIS)             /* disabled? */
4346                      return SCPE_UDIS;
4347                 if ((mptr->valid) &&                    /* invalid? */
4348                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
4349                     return r;
4350                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
4351                     (mptr->match & mptr->mask);         /* set new value */
4352                 }                                       /* end else xtd */
4353             break;                                      /* terminate for */
4354             }                                           /* end if match */
4355         }                                               /* end for */
4356     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4357         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
4358             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
4359             if (r != SCPE_OK)
4360                 return r;
4361             }
4362         else if (!dptr->modifiers)                      /* no modifiers? */
4363             return SCPE_NOPARAM;
4364         else return SCPE_NXPAR;
4365         }                                               /* end if no mat */
4366     }                                                   /* end while */
4367 return SCPE_OK;                                         /* done all */
4368 }
4369 
4370 /* Match CTAB/CTAB1 name */
4371 
4372 CTAB *find_ctab (CTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4373 {
4374 if (!tab)
4375     return NULL;
4376 for (; tab->name != NULL; tab++) {
4377     if (MATCH_CMD (gbuf, tab->name) == 0)
4378         return tab;
4379     }
4380 return NULL;
4381 }
4382 
4383 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4384 {
4385 if (!tab)
4386     return NULL;
4387 for (; tab->name != NULL; tab++) {
4388     if (MATCH_CMD (gbuf, tab->name) == 0)
4389         return tab;
4390     }
4391 return NULL;
4392 }
4393 
4394 /* Set device data radix routine */
4395 
4396 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4397 {
4398 if (cptr)
4399     return SCPE_ARG;
4400 dptr->dradix = flag & 037;
4401 return SCPE_OK;
4402 }
4403 
4404 /* Set device enabled/disabled routine */
4405 
4406 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4407 {
4408 UNIT *up;
4409 uint32 i;
4410 
4411 if (cptr)
4412     return SCPE_ARG;
4413 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
4414     return SCPE_NOFNC;
4415 if (flag) {                                             /* enable? */
4416     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
4417         return SCPE_OK;
4418     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
4419     }
4420 else {
4421     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
4422         return SCPE_OK;
4423     for (i = 0; i < dptr->numunits; i++) {              /* check units */
4424         up = (dptr->units) + i;                         /* att or active? */
4425         if ((up->flags & UNIT_ATT) || sim_is_active (up))
4426             return SCPE_NOFNC;                          /* can't do it */
4427         }
4428     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
4429     }
4430 if (dptr->reset)                                        /* reset device */
4431     return dptr->reset (dptr);
4432 else return SCPE_OK;
4433 }
4434 
4435 /* Set unit enabled/disabled routine */
4436 
4437 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4438 {
4439 if (cptr)
4440     return SCPE_ARG;
4441 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
4442     return SCPE_NOFNC;
4443 if (flag)                                               /* enb? enable */
4444     uptr->flags = uptr->flags & ~UNIT_DIS;
4445 else {
4446     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
4447         sim_is_active (uptr))                           /* more tests */
4448         return SCPE_NOFNC;
4449     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
4450     }
4451 return SCPE_OK;
4452 }
4453 
4454 /* Set device debug enabled/disabled routine */
4455 
4456 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4457 {
4458 char gbuf[CBUFSIZE];
4459 DEBTAB *dep;
4460 
4461 if ((dptr->flags & DEV_DEBUG) == 0)
4462     return SCPE_NOFNC;
4463 if (cptr == NULL) {                                     /* no arguments? */
4464     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
4465     if (flag && dptr->debflags) {                       /* enable with table? */
4466         for (dep = dptr->debflags; dep->name != NULL; dep++)
4467             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
4468         }
4469     return SCPE_OK;
4470     }
4471 if (dptr->debflags == NULL)                             /* must have table */
4472     return SCPE_ARG;
4473 while (*cptr) {
4474     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
4475     for (dep = dptr->debflags; dep->name != NULL; dep++) {
4476         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
4477             if (flag)
4478                 dptr->dctrl = dptr->dctrl | dep->mask;
4479             else dptr->dctrl = dptr->dctrl & ~dep->mask;
4480             break;
4481             }
4482         }                                               /* end for */
4483     if (dep->mask == 0)                                 /* no match? */
4484         return SCPE_ARG;
4485     }                                                   /* end while */
4486 return SCPE_OK;
4487 }
4488 
4489 /* Show command */
4490 
4491 t_stat show_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4492 {
4493 t_stat r = SCPE_IERR;
4494 
4495 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
4496                                                         /* get sw, ofile */
4497 if (NULL == cptr)                                              /* error? */
4498     return r;
4499 if (sim_ofile) {                                        /* output file? */
4500     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
4501     fclose (sim_ofile);
4502     }
4503 else {
4504     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
4505     if (sim_log && (sim_log != stdout))
4506         show_cmd_fi (sim_log, flag, cptr);
4507     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
4508         show_cmd_fi (sim_deb, flag, cptr);
4509     }
4510 return r;
4511 }
4512 
4513 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4514 {
4515 uint32 lvl = 0xFFFFFFFF;
4516 char gbuf[CBUFSIZE], *cvptr;
4517 CONST char *svptr;
4518 DEVICE *dptr;
4519 UNIT *uptr;
4520 MTAB *mptr;
4521 SHTAB *shtb = NULL, *shptr;
4522 
4523 GET_SWITCHES (cptr);                                    /* get switches */
4524 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4525     return SCPE_2FARG;
4526 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
4527 
4528 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4529     uptr = dptr->units;                                 /* first unit */
4530     shtb = show_dev_tab;                                /* global table */
4531     lvl = MTAB_VDV;                                     /* device match */
4532     GET_SWITCHES (cptr);                                /* get more switches */
4533     }
4534 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4535     if (uptr == NULL)                                   /* invalid unit */
4536         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\r\n", gbuf);
4537     if (uptr->flags & UNIT_DIS)                         /* disabled? */
4538         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\r\n", gbuf);
4539     shtb = show_unit_tab;                               /* global table */
4540     lvl = MTAB_VUN;                                     /* unit match */
4541     GET_SWITCHES (cptr);                                /* get more switches */
4542     }
4543 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
4544     GET_SWITCHES (cptr);                                /* get more switches */
4545     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
4546     }
4547 else {
4548     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4549         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4550             *cvptr++ = 0;
4551         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
4552             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
4553                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) || //-V600
4554                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
4555                 dptr = sim_dflt_dev;
4556                 lvl = MTAB_VDV;                         /* device match */
4557                 cptr = svptr;
4558                 while (sim_isspace(*cptr))
4559                     ++cptr;
4560                 break;
4561                 }
4562             }
4563         }
4564     if (!dptr) {
4565         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
4566             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
4567         else
4568             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\r\n", gbuf);/* no match */
4569         }
4570     }
4571 
4572 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
4573     return (lvl == MTAB_VDV)?
4574         show_device (ofile, dptr, 0):
4575         show_unit (ofile, dptr, uptr, -1);
4576     }
4577 GET_SWITCHES (cptr);                                    /* get more switches */
4578 
4579 while (*cptr != 0) {                                    /* do all mods */
4580     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
4581     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4582         *cvptr++ = 0;
4583     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4584         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
4585             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
4586             ((mptr->disp && mptr->pstring &&            /* named disp? */
4587             (MATCH_CMD (gbuf, mptr->pstring) == 0))
4588             )) {
4589             if (cvptr && !MODMASK(mptr,MTAB_SHP))
4590                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\r\n", gbuf, cvptr);
4591             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
4592             break;
4593             }                                           /* end if */
4594         }                                               /* end for */
4595     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4596         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
4597             t_stat r;
4598 
4599             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
4600             if (r != SCPE_OK)
4601                 return r;
4602             }
4603         else {
4604             if (!dptr->modifiers)                       /* no modifiers? */
4605                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\r\n", dptr->name);
4606             else
4607                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\r\n", gbuf);
4608             }
4609         }                                               /* end if */
4610     }                                                   /* end while */
4611 return SCPE_OK;
4612 }
4613 
4614 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4615 {
4616 if (!tab)
4617     return NULL;
4618 for (; tab->name != NULL; tab++) {
4619     if (MATCH_CMD (gbuf, tab->name) == 0)
4620         return tab;
4621     }
4622 return NULL;
4623 }
4624 
4625 /* Show device and unit */
4626 
4627 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4628 {
4629 uint32 j, udbl, ucnt;
4630 UNIT *uptr;
4631 int32 toks = 0;
4632 
4633 if (strcmp(sim_dname (dptr),"SYS") != 0) {
4634 (void)fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
4635 if ((flag == 2) && dptr->description) {
4636     (void)fprintf (st, "\t%s\r\n", dptr->description(dptr));
4637     }
4638 else {
4639     if ((sim_switches & SWMASK ('D')) && dptr->description)
4640         (void)fprintf (st, "\t%s\r\n", dptr->description(dptr));
4641     }
4642 if (qdisable (dptr)) {                                  /* disabled? */
4643     (void)fprintf (st, "\tdisabled\r\n");
4644     return SCPE_OK;
4645     }
4646 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
4647     uptr = dptr->units + j;
4648     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
4649         ucnt++;
4650     else if (uptr->flags & UNIT_DISABLE)
4651         udbl++;                                         /* count user-disabled */
4652     }
4653 //show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
4654 if (dptr->numunits == 0) {
4655     // if (toks) //-V547
4656     //     (void)fprintf (st, "\r\n");
4657 }
4658  else
4659 {
4660     if (ucnt == 0) {
4661         fprint_sep (st, &toks);
4662         (void)fprintf (st, "all units disabled\r\n");
4663         }
4664     else if ((ucnt + udbl) == 1) {
4665         fprint_sep (st, &toks);
4666         (void)fprintf (st, " 1 unit\r\n");
4667         }
4668     else if ((ucnt > 1) || (udbl > 0)) {
4669         fprint_sep (st, &toks);
4670         (void)fprintf (st, "%2.d units\r\n", ucnt + udbl);
4671         }
4672     else
4673         if ((flag != 2) || !dptr->description || toks)
4674             (void)fprintf (st, "\r\n");
4675     toks = 0;
4676 }
4677 if (flag)                                               /* dev only? */
4678     return SCPE_OK;
4679 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
4680     uptr = dptr->units + j;
4681     if ((uptr->flags & UNIT_DIS) == 0)
4682         show_unit (st, dptr, uptr, ucnt + udbl);
4683     }
4684 }
4685 return SCPE_OK;
4686 }
4687 
4688 void fprint_sep (FILE *st, int32 *tokens)
     /* [previous][next][first][last][top][bottom][index][help] */
4689 {
4690 (void)fprintf (st, "%s", (*tokens > 0) ? "" : "\t");
4691 *tokens += 1;
4692 }
4693 
4694 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4695 {
4696 int32 u = (int32)(uptr - dptr->units);
4697 int32 toks = 0;
4698 
4699 if (flag > 1)
4700     (void)fprintf (st, "   %s%d\r\n", sim_dname (dptr), u);
4701 else if (flag < 0)
4702     (void)fprintf (st, " %s%d ", sim_dname (dptr), u);
4703 if (uptr->flags & UNIT_ATT) {
4704     fprint_sep (st, &toks);
4705     (void)fprintf (st, "status   : attached to %s", uptr->filename);
4706     if (uptr->flags & UNIT_RO)
4707         (void)fprintf (st, ", read only");
4708     }
4709 else {
4710     if (uptr->flags & UNIT_ATTABLE) {
4711         fprint_sep (st, &toks);
4712         (void)fprintf (st, "status   : not attached");
4713         }
4714     }
4715 if ((uptr->capac > 0) && (uptr->flags & UNIT_FIX)) {
4716     fprint_sep (st, &toks);
4717     fprint_capac (st, dptr, uptr);
4718     }
4719 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
4720 if (toks || (flag < 0) || (flag > 1))
4721     (void)fprintf (st, "\r\n");
4722 return SCPE_OK;
4723 }
4724 
4725 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4726 {
4727 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4728 t_offset kval = (t_offset)((uptr->flags & UNIT_BINK) ? 1024: 1000);
4729 t_offset mval;
4730 t_offset psize = (t_offset)uptr->capac;
4731 char *scale, *width;
4732 
4733 if (sim_switches & SWMASK ('B'))
4734     kval = 1024;
4735 mval = kval * kval;
4736 if (dptr->flags & DEV_SECTORS) {
4737     kval = kval / 512;
4738     mval = mval / 512;
4739     }
4740 if ((dptr->dwidth / dptr->aincr) > 8)
4741     width = "W";
4742 else
4743     width = "B";
4744 if ((psize < (kval * 10)) &&
4745     (0 != (psize % kval))) {
4746     scale = "";
4747     }
4748 else if ((psize < (mval * 10)) &&
4749          (0 != (psize % mval))){
4750     scale = "K";
4751     psize = psize / kval;
4752     }
4753 else {
4754     scale = "M";
4755     psize = psize / mval;
4756     }
4757 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4758 (void)sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4759 return capac_buf;
4760 }
4761 
4762 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4763 {
4764 (void)fprintf (st, " %s", sprint_capac (dptr, uptr));
4765 }
4766 
4767 /* Show <global name> processors  */
4768 
4769 extern void print_default_base_system_script (void);
4770 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] */
4771 {
4772 #if !defined(PERF_STRIP)
4773   print_default_base_system_script();
4774 #endif /* if !defined(PERF_STRIP) */
4775   return 0;
4776 }
4777 
4778 static void printp (unsigned char * PROM, char * label, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4779   sim_printf (" %s ", label);
4780   sim_printf ("   %2d     %3o(8)     '", length, offset);
4781   for (int l = 0; l < length; l ++)
4782     {
4783       unsigned int byte = PROM[offset + l];
4784       if (byte == 255)
4785         {
4786           byte = ' ';
4787         }
4788       sim_printf (isprint (byte) ? "%c" : "\\%03o", byte);
4789     }
4790   sim_printf ("'\r\n");
4791 }
4792 
4793 static void strip_spaces(char* str) {
     /* [previous][next][first][last][top][bottom][index][help] */
4794   int i, x;
4795   for (i=x=0; str[i]; ++i)
4796     {
4797       if (!isspace((int)str[i]) || (i > 0 && !isspace((int)str[(int)(i-1)]))) //-V781
4798         {
4799           str[x++] = str[i];
4800         }
4801     }
4802   str[x] = '\0';
4803   i = -1;
4804   x = 0;
4805   while (str[x] != '\0')
4806     {
4807       if (str[x] != ' ' && str[x] != '\t' && str[x] != '\n')
4808         {
4809           i=x;
4810         }
4811       x++;
4812     }
4813   str[i+1] = '\0';
4814 }
4815 
4816 static void printpq (unsigned char * PROM, FILE * st, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4817   char sx[1024];
4818   sx[1023] = '\0';
4819   unsigned int lastbyte = 0;
4820   for (int l = 0; l < length; l ++)
4821     {
4822       unsigned int byte = PROM[offset + l];
4823       if (byte == 255)
4824         {
4825           byte = 20;
4826         }
4827       if ((lastbyte != 20) && (byte != 20))
4828         {
4829           (void)sprintf(&sx[l], isprint (byte) ? "%c" : " ", byte);
4830         }
4831       lastbyte = byte;
4832     }
4833   strip_spaces(sx);
4834   (void)fprintf (st, "%s", sx);
4835 }
4836 
4837 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4838 {
4839   unsigned char PROM[1024];
4840   setupPROM (0, PROM);
4841 
4842   sim_printf (" PROM size: %llu bytes\r\n",
4843               (long long unsigned)sizeof(PROM));
4844   sim_printf (" PROM initialization data:\r\n\r\n");
4845 
4846   sim_printf ("     Field Description      Length   Offset              Contents\r\n");
4847   sim_printf (" ========================= ======== ======== ==================================\r\n");
4848   sim_printf ("\r\n");
4849 
4850   //                      Field                 Offset       Length
4851   //             -------------------------    ----------   ----------
4852   printp (PROM, "CPU Model                ",       0,          11);
4853   printp (PROM, "CPU Serial               ",      11,          11);
4854   printp (PROM, "Ship Date                ",      22,           6);
4855   printp (PROM, "PROM Layout Version      ",      60,           1);
4856   printp (PROM, "Release Git Commit Date  ",      70,          10);
4857   printp (PROM, "Release Major            ",      80,           3);
4858   printp (PROM, "Release Minor            ",      83,           3);
4859   printp (PROM, "Release Patch            ",      86,           3);
4860   printp (PROM, "Release Iteration        ",      89,           3);
4861   printp (PROM, "Release Build Number     ",      92,           8);  /* Reserved */
4862   printp (PROM, "Release Type             ",     100,           1);
4863   printp (PROM, "Release Version Text     ",     101,          29);
4864   printp (PROM, "Build Architecture       ",     130,          20);
4865   printp (PROM, "Build Operating System   ",     150,          20);
4866   printp (PROM, "Target Architecture      ",     170,          20);
4867   printp (PROM, "Target Operating System  ",     190,          20);
4868 
4869   sim_printf("\r\n");
4870   return 0;
4871 }
4872 
4873 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4874 {
4875     (void)fprintf (st, "\r Build Information:\r\n");
4876 #if defined(BUILDINFO_scp)
4877     (void)fprintf (st, "\r\n      Compilation info: %s\r\n", BUILDINFO_scp );
4878 #else
4879     (void)fprintf (st, "\r\n      Compilation info: Not available\r\n" );
4880 #endif
4881 #if !defined(__CYGWIN__)
4882 # if !defined(__APPLE__)
4883 #  if !defined(_AIX)
4884 #   if !defined(__MINGW32__)
4885 #    if !defined(__MINGW64__)
4886 #     if !defined(CROSS_MINGW32)
4887 #      if !defined(CROSS_MINGW64)
4888 #       if !defined(_WIN32)
4889 #        if !defined(__HAIKU__)
4890 #         if !defined(__QNX__)
4891 #          if !defined(__FILC__)
4892     (void)dl_iterate_phdr (dl_iterate_phdr_callback, NULL);
4893     if (dl_iterate_phdr_callback_called)
4894         (void)fprintf (st, "\r\n");
4895     dl_iterate_phdr_callback_called = 0;
4896 #          endif
4897 #         endif
4898 #        endif
4899 #       endif
4900 #      endif
4901 #     endif
4902 #    endif
4903 #   endif
4904 #  endif
4905 # endif
4906 #endif
4907 #if defined(UV_VERSION_MAJOR) && \
4908     defined(UV_VERSION_MINOR) && \
4909     defined(UV_VERSION_PATCH)
4910 # if defined(UV_VERSION_MAJOR)
4911 #  if !defined(UV_VERSION_MINOR) && \
4912       !defined(UV_VERSION_PATCH) && \
4913       !defined(UV_VERSION_SUFFIX)
4914     (void)fprintf (st, "\r\n    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4915 #  endif /* if !defined(UV_VERSION_MINOR) && !defined(UV_VERSION_PATCH) && defined(UV_VERSION_SUFFIX) */
4916 #  if defined(UV_VERSION_MINOR)
4917 #   if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX)
4918     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4919                    UV_VERSION_MINOR);
4920 #   endif /* if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX) */
4921 #   if defined(UV_VERSION_PATCH)
4922 #    if !defined(UV_VERSION_SUFFIX)
4923     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4924                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4925 #    endif /* if !defined(UV_VERSION_SUFFIX) */
4926 #    if defined(UV_VERSION_SUFFIX)
4927     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4928                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4929 #     if defined(UV_VERSION_IS_RELEASE)
4930 #      if UV_VERSION_IS_RELEASE == 1
4931 #       define UV_RELEASE_TYPE " (release)"
4932 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4933 #      if UV_VERSION_IS_RELEASE == 0
4934 #       define UV_RELEASE_TYPE "-dev"
4935 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4936 #      if !defined(UV_RELEASE_TYPE)
4937 #       define UV_RELEASE_TYPE ""
4938 #      endif /* if !defined(UV_RELEASE_TYPE) */
4939 #      if defined(UV_RELEASE_TYPE)
4940     (void)fprintf (st, "%s", UV_RELEASE_TYPE);
4941 #      endif /* if defined(UV_RELEASE_TYPE) */
4942 #     endif /* if defined(UV_VERSION_IS_RELEASE) */
4943 #    endif /* if defined(UV_VERSION_SUFFIX) */
4944 #   endif /* if defined(UV_VERSION_PATCH) */
4945 #  endif /* if defined(UV_VERSION_MINOR) */
4946     unsigned int CurrentUvVersion = uv_version();
4947     if (CurrentUvVersion > 0)
4948         if (uv_version_string() != NULL)
4949             (void)fprintf (st, "; %s in use", uv_version_string());
4950 # endif /* if defined(UV_VERSION_MAJOR) */
4951 #else
4952     (void)fprintf (st, "\r\n    Event loop library: Using libuv (or compatible) library, unknown version");
4953 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4954         *    defined(UV_VERSION_MINOR) &&  \
4955         *    defined(UV_VERSION_PATCH)     \
4956         */
4957     (void)fprintf (st, "\r\n   Log support library: Built with libsir %d.%d.%d%s%s; %s%s in use",
4958                    SIR_VERSION_MAJOR, SIR_VERSION_MINOR, SIR_VERSION_PATCH,
4959                    SIR_VERSION_SUFFIX, SIR_VERSION_IS_RELEASE ? " (release)" : "",
4960                    sir_getversionstring(), SIR_VERSION_IS_RELEASE ? "" : sir_isprerelease() ? "" : " (release)");
4961 #if defined(DECNUMBERLOC)
4962 # if defined(DECVERSION)
4963 #  if defined(DECVERSEXT)
4964     (void)fprintf (st, "\r\n          Math library: %s-%s", DECVERSION, DECVERSEXT);
4965 #  else
4966 #   if defined(DECNLAUTHOR)
4967     (void)fprintf (st, "\r\n          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4968 #   else
4969     (void)fprintf (st, "\r\n          Math library: %s", DECVERSION);
4970 #   endif /* if defined(DECNLAUTHOR) */
4971 #  endif /* if defined(DECVERSEXT) */
4972 # else
4973     (void)fprintf (st, "\r\n          Math library: decNumber, unknown version");
4974 # endif /* if defined(DECVERSION) */
4975 #endif /* if defined(DECNUMBERLOC) */
4976 #if defined(LOCKLESS)
4977     (void)fprintf (st, "\r\n     Atomic operations: ");
4978 # if defined(AIX_ATOMICS)
4979     (void)fprintf (st, "C11 and IBM AIX-style");
4980 # elif defined(BSD_ATOMICS)
4981     (void)fprintf (st, "C11 and FreeBSD-style");
4982 # elif defined(GNU_ATOMICS)
4983     (void)fprintf (st, "C11 and GNU-style");
4984 # elif defined(SYNC_ATOMICS)
4985     (void)fprintf (st, "C11 and GNU sync-style");
4986 # elif defined(ISO_ATOMICS)
4987     (void)fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4988 # elif defined(NT_ATOMICS)
4989     (void)fprintf (st, "C11 and Windows NT interlocked operations");
4990 # endif
4991 #endif /* if defined(LOCKLESS) */
4992     (void)fprintf (st, "\r\n          File locking: ");
4993 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4994     (void)fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4995 #endif
4996 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4997     (void)fprintf (st, "POSIX-style fcntl() locking");
4998 #endif
4999 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
5000     (void)fprintf (st, "BSD-style flock() locking");
5001 #endif
5002 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
5003     (void)fprintf (st, "No file locking available");
5004 #endif
5005 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64) || defined(__CYGWIN__)
5006     (void)fprintf (st, "\r\n       Windows support: ");
5007 #endif
5008 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
5009 # if defined(__MINGW64_VERSION_STR)
5010     (void)fprintf (st, "Built with MinGW-w64 %s", __MINGW64_VERSION_STR);
5011 # elif defined(__MINGW32_MAJOR_VERSION) && defined(__MINGW32_MINOR_VERSION)
5012     (void)fprintf (st, "Built with MinGW %d.%d", __MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);
5013 # else
5014     (void)fprintf (st, "Built with MinGW");
5015 # endif
5016 
5017 # if defined(MINGW_CRT)
5018     (void)fprintf (st, "; %s", MINGW_CRT);
5019 #  if !defined(_UCRT)
5020 #   if defined(__MSVCRT_VERSION__)
5021 #    if __MSVCRT_VERSION__ > 0x00
5022     (void)fprintf (st, " %d.%d", (__MSVCRT_VERSION__ >> CHAR_BIT) & UCHAR_MAX, __MSVCRT_VERSION__ & UCHAR_MAX);
5023 #    endif
5024 #   endif
5025 #  else
5026 
5027     struct UCRTVersion ucrtversion;
5028     int result = GetUCRTVersion (&ucrtversion);
5029 
5030     if (result == 0)
5031       (void)fprintf (st, " %u.%u.%u.%u",
5032                      ucrtversion.ProductVersion[1], ucrtversion.ProductVersion[0],
5033                      ucrtversion.ProductVersion[3], ucrtversion.ProductVersion[2]);
5034 #  endif
5035     (void)fprintf (st, " in use");
5036 # endif
5037 #elif defined(__CYGWIN__)
5038     struct utsname utsname;
5039     (void)fprintf (st, "Built with Cygwin %d.%d.%d",
5040                    CYGWIN_VERSION_DLL_MAJOR / 1000,
5041                    CYGWIN_VERSION_DLL_MAJOR % 1000,
5042                    CYGWIN_VERSION_DLL_MINOR);
5043     if (uname(&utsname) == 0)
5044       fprintf (st, "; %s in use", utsname.release);
5045 #endif
5046 
5047     (void)fprintf (st, "\r\n");
5048     return 0;
5049 }
5050 
5051 static void
5052 removehex(char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
5053 {
5054   if (!s) return;
5055 
5056   size_t len = strlen(s);
5057   for (size_t i = 0; i < len; i++) {
5058     if (s[i] != ' ')
5059      continue;
5060 
5061     if ((len - i) < 41)
5062       continue;
5063 
5064     size_t j;
5065     for (j = 1; j <= 40; j++) {
5066       unsigned char uc = (unsigned char)s[i + j];
5067       if (!isxdigit(uc))
5068         break;
5069     }
5070 
5071     if (j == 41) {
5072       memmove(&s[i], &s[i + 41], strlen(&s[i + 41]) + 1);
5073       return;
5074     }
5075   }
5076 }
5077 
5078 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5079 {
5080 const char *arch = "";
5081 char *whydirty = " ";
5082 int dirty = 0;
5083 
5084 if (cptr && (*cptr != 0))
5085     return SCPE_2MARG;
5086 if (flag) {
5087         (void)fprintf (st, " %s Simulator:", sim_name);
5088 #if defined(USE_DUMA)
5089 # undef NO_SUPPORT_VERSION
5090 # define NO_SUPPORT_VERSION 1
5091         nodist++;
5092 #endif /* if defined(USE_DUMA) */
5093 #if defined(NO_SUPPORT_VERSION) ||  \
5094     defined(WITH_SOCKET_DEV)    ||  \
5095     defined(WITH_ABSI_DEV)      ||  \
5096     defined(TESTING)            ||  \
5097     defined(ISOLTS)             ||  \
5098     defined(USE_DUMA)
5099 # if !defined(NO_SUPPORT_VERSION)
5100 #  define NO_SUPPORT_VERSION 1
5101 # endif /* if !defined(NO_SUPPORT_VERSION) */
5102 #endif
5103 #if defined(NO_SUPPORT_VERSION)
5104         dirty++;
5105 #endif
5106 #if defined(GENERATED_MAKE_VER_H)
5107 # if defined(VER_H_GIT_VERSION)
5108 
5109         /* Dirty if git source is dirty */
5110         if (strstr(VER_H_GIT_VERSION, "*"))
5111           {
5112                 dirty++;
5113           }
5114 
5115         /* Dirty if version contains "X", "D", "A", or "B" */
5116         if ((strstr(VER_H_GIT_VERSION, "X")) ||  \
5117             (strstr(VER_H_GIT_VERSION, "D")) ||  \
5118             (strstr(VER_H_GIT_VERSION, "A")) ||  \
5119             (strstr(VER_H_GIT_VERSION, "B")))
5120           {
5121                 dirty++;
5122           }
5123 
5124         /* Why? */
5125         if (dirty) //-V547
5126           {
5127             if ((strstr(VER_H_GIT_VERSION, "X")))
5128               {
5129                     whydirty = " ";
5130               }
5131             else if ((strstr(VER_H_GIT_VERSION, "D")))
5132               {
5133                     whydirty = " DEV ";
5134               }
5135             else if ((strstr(VER_H_GIT_VERSION, "A")))
5136               {
5137                     whydirty = " ALPHA ";
5138               }
5139             else if ((strstr(VER_H_GIT_VERSION, "B")))
5140               {
5141                     whydirty = " BETA ";
5142               }
5143           }
5144 
5145 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
5146 #   if defined(VER_H_GIT_HASH)
5147 #    if VER_H_GIT_PATCH_INT < 1
5148     (void)fprintf (st, "\r\n   Version: %s (%ld-bit)\r\n    Commit: %s",
5149                    VER_H_GIT_VERSION,
5150                    (long)(CHAR_BIT*sizeof(void *)),
5151                    VER_H_GIT_HASH);
5152 #    else
5153 #     define NO_SUPPORT_VERSION 1
5154     (void)fprintf (st, "\r\n   Version: %s+%s (%ld-bit)\r\n    Commit: %s",
5155                    VER_H_GIT_VERSION, VER_H_GIT_PATCH,
5156                    (long)(CHAR_BIT*sizeof(void *)),
5157                    VER_H_GIT_HASH);
5158 #    endif
5159 #   else
5160 #    if VER_H_GIT_PATCH_INT < 1
5161         (void)fprintf (st, "\r\n   Version: %s (%ld-bit)",
5162                        VER_H_GIT_VERSION,
5163                        (long)(CHAR_BIT*sizeof(void *)));
5164 #    else
5165 #     define NO_SUPPORT_VERSION 1
5166         (void)fprintf (st, "\r\n   Version: %s+%s (%ld-bit)",
5167                        VER_H_GIT_VERSION, VER_H_GIT_PATCH,
5168                        (long)(CHAR_BIT*sizeof(void *)));
5169 #    endif
5170 #   endif
5171 #  else
5172 #   if defined(VER_H_GIT_HASH)
5173         (void)fprintf (st, "\r\n   Version: %s (%ld-bit)\r\n    Commit: %s",
5174                        VER_H_GIT_VERSION,
5175                        (long)(CHAR_BIT*sizeof(void *)),
5176                        VER_H_GIT_HASH);
5177 #   else
5178         (void)fprintf (st, "\r\n   Version: %s (%ld-bit)",
5179                        VER_H_GIT_VERSION,
5180                        (long)(CHAR_BIT*sizeof(void *)));
5181 #   endif
5182 #  endif
5183 # endif
5184 #endif
5185 
5186 /* TESTING */
5187 #if defined(TESTING)
5188     (void)fprintf (st, "\r\n   Options: ");
5189 # if !defined(HAVE_DPSOPT)
5190 #  define HAVE_DPSOPT 1
5191 # endif
5192     (void)fprintf (st, "TESTING");
5193 #endif /* if defined(TESTING) */
5194 
5195 /* ISOLTS */
5196 #if defined(ISOLTS)
5197 # if defined(HAVE_DPSOPT)
5198     (void)fprintf (st, ", ");
5199 # else
5200     (void)fprintf (st, "\r\n   Options: ");
5201 # endif
5202 # if !defined(HAVE_DPSOPT)
5203 #  define HAVE_DPSOPT 1
5204 # endif
5205     (void)fprintf (st, "ISOLTS");
5206 #endif /* if defined(ISOLTS) */
5207 
5208 /* NEED_128 */
5209 #if defined(NEED_128)
5210 # if defined(HAVE_DPSOPT)
5211     (void)fprintf (st, ", ");
5212 # else
5213     (void)fprintf (st, "\r\n   Options: ");
5214 # endif
5215 # if !defined(HAVE_DPSOPT)
5216 #  define HAVE_DPSOPT 1
5217 # endif
5218     (void)fprintf (st, "NEED_128");
5219 #endif /* if defined(NEED_128) */
5220 
5221 /* NO_LOCKLESS */
5222 #if !defined(LOCKLESS)
5223 # if defined(HAVE_DPSOPT)
5224     (void)fprintf (st, ", ");
5225 # else
5226     (void)fprintf (st, "\r\n   Options: ");
5227 # endif
5228 # if !defined(HAVE_DPSOPT)
5229 #  define HAVE_DPSOPT 1
5230 # endif
5231     (void)fprintf (st, "NO_LOCKLESS");
5232 #endif /* if !defined(LOCKLESS) */
5233 
5234 /* PANEL68 */
5235 #if defined(PANEL68)
5236 # if defined(HAVE_DPSOPT)
5237     (void)fprintf (st, ", ");
5238 # else
5239     (void)fprintf (st, "\r\n Options: ");
5240 # endif
5241 # if !defined(HAVE_DPSOPT)
5242 #  define HAVE_DPSOPT 1
5243 # endif
5244     (void)fprintf (st, "PANEL68");
5245 #endif /* if defined(PANEL68) */
5246 
5247 /* ABSI */  /* XXX: Change to NO_ABSI once code is non-experimental */
5248 #if defined(WITH_ABSI_DEV)
5249 # if defined(HAVE_DPSOPT)
5250     (void)fprintf (st, ", ");
5251 # else
5252     (void)fprintf (st, "\r\n   Options: ");
5253 # endif
5254 # if !defined(HAVE_DPSOPT)
5255 #  define HAVE_DPSOPT 1
5256 # endif
5257     (void)fprintf (st, "ABSI");
5258 #endif /* if defined(WITH_ABSI_DEV) */
5259 
5260 /* SOCKET */  /* XXX: Change to !SOCKET once code is non-experimental */
5261 #if defined(WITH_SOCKET_DEV)
5262 # if defined(HAVE_DPSOPT)
5263     (void)fprintf (st, ", ");
5264 # else
5265     (void)fprintf (st, "\r\n   Options: ");
5266 # endif
5267 # if !defined(HAVE_DPSOPT)
5268 #  define HAVE_DPSOPT 1
5269 # endif
5270     (void)fprintf (st, "SOCKET");
5271 #endif /* if defined(WITH_SOCKET_DEV) */
5272 
5273 /* CHAOSNET */
5274 #if !defined(WITH_MGP_DEV)
5275 # if defined(HAVE_DPSOPT)
5276     (void)fprintf (st, ", ");
5277 # else
5278     (void)fprintf (st, "\r\n   Options: ");
5279 # endif
5280 # if !defined(HAVE_DPSOPT)
5281 #  define HAVE_DPSOPT 1
5282 # endif
5283     (void)fprintf (st, "NO_CHAOSNET");
5284 #endif /* if !defined(WITH_MGP_DEV) */
5285 
5286 /* INTERNET */
5287 #if !defined(WITH_NET_DEV)
5288 # if defined(HAVE_DPSOPT)
5289     (void)fprintf (st, ", ");
5290 # else
5291     (void)fprintf (st, "\r\n   Options: ");
5292 # endif
5293 # if !defined(HAVE_DPSOPT)
5294 #  define HAVE_DPSOPT 1
5295 # endif
5296     (void)fprintf (st, "NO_INTERNET");
5297 #endif /* if defined(WITH_NET_DEV) */
5298 
5299 /* DUMA */
5300 #if defined(USE_DUMA)
5301 # if defined(HAVE_DPSOPT)
5302     (void)fprintf (st, ", ");
5303 # else
5304     (void)fprintf (st, "\r\n   Options: ");
5305 # endif
5306 # if !defined(HAVE_DPSOPT)
5307 #  define HAVE_DPSOPT 1
5308 # endif
5309     (void)fprintf (st, "DUMA");
5310 #endif /* if defined(USE_DUMA) */
5311 
5312 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
5313 # if defined(NO_SUPPORT_VERSION)
5314     (void)fprintf (st, "\r\n  Modified: %s", VER_H_GIT_DATE);
5315 # else
5316     (void)fprintf (st, "\r\n  Released: %s", VER_H_GIT_DATE);
5317 # endif
5318 #endif
5319 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
5320     (void)fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
5321 #endif
5322 #if defined(VER_CURRENT_TIME)
5323     (void)fprintf (st, "\r\n  Compiled: %s", VER_CURRENT_TIME);
5324 #endif
5325     if (dirty) //-V547
5326       {
5327         (void)fprintf (st, "\r\n\r\n ****** THIS%sBUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******", whydirty);
5328       }
5329     (void)fprintf (st, "\r\n\r\n Build Information:");
5330 #if defined(BUILD_PROM_OSV_TEXT) && defined(BUILD_PROM_OSA_TEXT)
5331     char build_os_version_raw[255];
5332     char build_os_arch_raw[255];
5333     (void)sprintf(build_os_version_raw, "%.254s", BUILD_PROM_OSV_TEXT);
5334     (void)sprintf(build_os_arch_raw, "%.254s", BUILD_PROM_OSA_TEXT);
5335     char *build_os_version = strdup(build_os_version_raw);
5336     if (!build_os_version)
5337       {
5338         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5339                        __func__, __FILE__, __LINE__);
5340 # if defined(USE_BACKTRACE)
5341 #  if defined(SIGUSR2)
5342         (void)raise(SIGUSR2);
5343         /*NOTREACHED*/ /* unreachable */
5344 #  endif /* if defined(SIGUSR2) */
5345 # endif /* if defined(USE_BACKTRACE) */
5346         abort();
5347       }
5348     char *build_os_arch = strdup(build_os_arch_raw);
5349     if (!build_os_arch)
5350       {
5351         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5352                        __func__, __FILE__, __LINE__);
5353 # if defined(USE_BACKTRACE)
5354 #  if defined(SIGUSR2)
5355         (void)raise(SIGUSR2);
5356         /*NOTREACHED*/ /* unreachable */
5357 #  endif /* if defined(SIGUSR2) */
5358 # endif /* if defined(USE_BACKTRACE) */
5359         abort();
5360       }
5361     unsigned char SPROM[1024];
5362     setupPROM (0, SPROM);
5363     (void)fprintf (st, "\r\n    Target: ");
5364     printpq (SPROM, st, 190, 20);
5365     if (SPROM[170] != 20)
5366       {
5367         if (SPROM[170] != 255)
5368           {
5369             (void)fprintf (st, " on ");
5370             printpq (SPROM, st, 170, 20);
5371           }
5372       }
5373     strtrimspace(build_os_version, build_os_version_raw);
5374     strtrimspace(build_os_arch, build_os_arch_raw);
5375     (void)fprintf (st, "\r\n  Build OS: %s %s", build_os_version, build_os_arch);
5376     FREE(build_os_version);
5377     FREE(build_os_arch);
5378 #endif
5379 #if defined(__VERSION__)
5380     char gnumver[2];
5381     char postver[1024];
5382     (void)sprintf(gnumver, "%.1s", __VERSION__);
5383     (void)sprintf(postver, "%.1023s", __VERSION__);
5384     strremove(postver, "(TM)");
5385     strremove(postver, "(R)");
5386     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
5387     strremove(postver, "https://github.com/OpenIndiana/oi-userland.git ");
5388     strremove(postver, " gcc 4.9 mode");
5389     strremove(postver, "4.2.1 Compatible ");
5390     strremove(postver, "git@github.com:llvm/llvm-project.git ");
5391     strremove(postver, "https://github.com/llvm/llvm-project.git ");
5392     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
5393     strremove(postver, "https://github.com/yrnkrn/zapcc ");
5394     strremove(postver, "(experimental) ");
5395     strremove(postver, ".module+el8.7.0+20823+214a699d");
5396     strremove(postver, "17.1.1 (5725-C72, 5765-J20), version ");
5397     strremove(postver, "17.1.1 (5725-C72, 5765-J18), version ");
5398     strremove(postver, "17.1.2 (5725-C72, 5765-J20), version ");
5399     strremove(postver, "17.1.2 (5725-C72, 5765-J18), version ");
5400     strremove(postver, "llvmorg-16.0.6-0-");
5401     strremove(postver, " Clang 15.0.0 (build 760095e)");
5402     strremove(postver, " Clang 15.0.0 (build 6af5742)");
5403     strremove(postver, " Clang 15.0.0 (build ca7115e)");
5404     strremove(postver, " Clang 15.0.0 (build 232543c)");
5405     strremove(postver, " Clang 17.0.6 (build 19a779f)");
5406     strremove(postver, "CLANG: ");
5407     removehex(postver);
5408     strremove(postver, " (git@github.com:pizlonator/llvm-project-deluge.git)");
5409 #endif
5410 #if ( defined(__GNUC__) && defined(__VERSION__) ) && !defined(__EDG__)
5411 # if !defined(__clang_version__)
5412     if (isdigit((unsigned char)gnumver[0])) {
5413         (void)fprintf (st, "\r\n  Compiler: GCC %s", postver);
5414     } else {
5415         (void)fprintf (st, "\r\n  Compiler: %s", postver);
5416     }
5417 # endif
5418 # if defined(__clang_analyzer__ )
5419     (void)fprintf (st, "\r\n  Compiler: Clang C/C++ Static Analyzer");
5420 # elif defined(__clang_version__) && defined(__VERSION__)
5421     char clangllvmver[1024];
5422     (void)sprintf(clangllvmver, "%.1023s", __clang_version__);
5423     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
5424     strremove(clangllvmver, "https://github.com/OpenIndiana/oi-userland.git ");
5425     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git ");
5426     strremove(clangllvmver, "c13b7485b87909fcf739f62cfa382b55407433c0");
5427     strremove(clangllvmver, "e6c3289804a67ea0bb6a86fadbe454dd93b8d855");
5428     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git");
5429     strremove(clangllvmver, " ( )");
5430     strremove(clangllvmver, " ()");
5431     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
5432         (void)fprintf (st, "\r\n  Compiler: Clang %s", clangllvmver);
5433     } else {
5434         (void)fprintf (st, "\r\n  Compiler: %s", postver);
5435     }
5436 # elif defined(__clang_version__)
5437     (void)fprintf (st, "\r\n  Compiler: %s", postver);
5438 # endif
5439 #elif defined(__PGI) && !defined(__NVCOMPILER)
5440     (void)fprintf (st, "\r\n  Compiler: Portland Group, Inc. (PGI) C Compiler ");
5441 # if defined(__PGIC__)
5442     (void)fprintf (st, "%d", __PGIC__);
5443 #  if defined(__PGIC_MINOR__)
5444     (void)fprintf (st, ".%d", __PGIC_MINOR__);
5445 #   if defined(__PGIC_PATCHLEVEL__)
5446     (void)fprintf (st, ".%d", __PGIC_PATCHLEVEL__);
5447 #   endif
5448 #  endif
5449 # endif
5450 #elif defined(__NVCOMPILER)
5451     (void)fprintf (st, "\r\n  Compiler: NVIDIA HPC SDK C Compiler ");
5452 # if defined(__NVCOMPILER_MAJOR__)
5453     (void)fprintf (st, "%d", __NVCOMPILER_MAJOR__);
5454 #  if defined(__NVCOMPILER_MINOR__)
5455     (void)fprintf (st, ".%d", __NVCOMPILER_MINOR__);
5456 #   if defined(__NVCOMPILER_PATCHLEVEL__)
5457     (void)fprintf (st, ".%d", __NVCOMPILER_PATCHLEVEL__);
5458 #   endif
5459 #  endif
5460 # endif
5461 #elif defined(_MSC_FULL_VER) && defined(_MSC_BUILD)
5462     (void)fprintf (st, "\r\n  Compiler: Microsoft C %d.%02d.%05d.%02d",
5463                    _MSC_FULL_VER/10000000,
5464                    (_MSC_FULL_VER/100000)%100,
5465                    _MSC_FULL_VER%100000,
5466                    _MSC_BUILD);
5467 #elif ( defined(__xlc__) && !defined(__clang_version__) )
5468 # if defined(_AIX) && defined(__PASE__)
5469     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
5470 # endif
5471 # if defined(_AIX) && !defined(__PASE__)
5472     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
5473 # endif
5474 # if defined(__linux__) && ( !defined(_AIX) || !defined(__PASE__) )
5475     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
5476 # endif
5477 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(__PASE__) && !defined(__linux__) && defined(__xlc__) )
5478 #  if defined(__PPC__) && defined(__APPLE__)
5479     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++ V%s for Mac OS X", __xlc__);
5480 #  else
5481     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++ V%s", __xlc__);
5482 #  endif
5483 # endif
5484 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__SUNPRO_CC_COMPAT)
5485 # define VER_ENC(maj, min, rev) \
5486   (((maj) * 1000000) + ((min) * 1000) + (rev))
5487 # define VER_DEC_MAJ(ver) \
5488   ((ver) / 1000000)
5489 # define VER_DEC_MIN(ver) \
5490   (((ver) % 1000000) / 1000)
5491 # define VER_DEC_REV(ver) \
5492   ((ver) % 1000)
5493 # if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
5494 #  define COMP_VER VER_ENC(                                        \
5495    (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
5496    (((__SUNPRO_C >>  8) & 0xf) * 10) + ((__SUNPRO_C >>  4) & 0xf), \
5497      (__SUNPRO_C & 0xf) * 10)
5498 # elif defined(__SUNPRO_C)
5499 #  define COMP_VER VER_ENC(    \
5500      (__SUNPRO_C >>  8) & 0xf, \
5501      (__SUNPRO_C >>  4) & 0xf, \
5502      (__SUNPRO_C) & 0xf)
5503 # elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
5504 #  define COMP_VER VER_ENC(                                          \
5505    (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
5506    (((__SUNPRO_CC >>  8) & 0xf) * 10) + ((__SUNPRO_CC >>  4) & 0xf), \
5507      (__SUNPRO_CC & 0xf) * 10)
5508 # elif defined(__SUNPRO_CC)
5509 #  define COMP_VER VER_ENC(     \
5510      (__SUNPRO_CC >>  8) & 0xf, \
5511      (__SUNPRO_CC >>  4) & 0xf, \
5512      (__SUNPRO_CC) & 0xf)
5513 # endif
5514 # if !defined(COMP_VER)
5515 #  define COMP_VER 0
5516 # endif
5517     (void)fprintf (st, "\r\n  Compiler: Oracle Developer Studio C/C++ %d.%d.%d",
5518                    VER_DEC_MAJ(COMP_VER),
5519                    VER_DEC_MIN(COMP_VER),
5520                    VER_DEC_REV(COMP_VER));
5521 #elif defined(__DMC__)
5522     (void)fprintf (st, "\r\n  Compiler: Digital Mars C/C++");
5523 #elif defined(__PCC__)
5524     (void)fprintf (st, "\r\n  Compiler: Portable C Compiler");
5525 #elif defined(KENC) || defined(KENCC) || defined(__KENC__) || defined(__KENCC__)
5526     (void)fprintf (st, "\r\n  Compiler: Plan 9 Compiler Suite");
5527 #elif defined(__ACK__)
5528     (void)fprintf (st, "\r\n  Compiler: Amsterdam Compiler Kit");
5529 #elif defined(__COMO__)
5530     (void)fprintf (st, "\r\n  Compiler: Comeau C++");
5531 #elif defined(__COMPCERT__)
5532     (void)fprintf (st, "\r\n  Compiler: CompCert C");
5533 #elif defined(__COVERITY__)
5534     (void)fprintf (st, "\r\n  Compiler: Coverity C/C++ Static Analyzer");
5535 #elif defined(__LCC__)
5536 # if defined(__e2k__) || defined(__e2k64__) || defined(__elbrus__) || defined(__ELBRUS__) || defined(__elbrus64__) || defined(__E2K__)
5537     (void)fprintf (st, "\r\n  Compiler: MCST Elbrus C Compiler");
5538 #  if __LCC__ > 99
5539     (void)fprintf(st, " - LCC %1.2f", (double)(__LCC__) / (double)100);
5540 #   if defined(__LCC_MINOR__)
5541 #    if __LCC_MINOR__ > 0
5542     (void)fprintf(st, ".%02ld", (long)(__LCC_MINOR__));
5543 #    endif
5544 #   endif
5545 #  endif
5546 #  if defined(__EDG__) && defined(__EDG_VERSION__)
5547 #   if __EDG_VERSION__ > 99
5548     (void)fprintf(st, " - EDG %d.%d", (int)((__EDG_VERSION__) / 100),
5549                   (int)(((__EDG_VERSION__) % 100) % 10));
5550 #   endif
5551 #  endif
5552 # else
5553     (void)fprintf (st, "\r\n  Compiler: Local/Little C Compiler (lcc)");
5554 # endif
5555 #elif defined(sgi) || defined(__sgi) || defined(_sgi) || defined(_SGI_COMPILER_VERSION)
5556     (void)fprintf (st, "\r\n  Compiler: SGI MIPSpro");
5557 #elif defined(__OPEN64__)
5558     (void)fprintf (st, "\r\n  Compiler: Open64 %s", __OPEN64__);
5559 #elif defined(__PGI) || defined(__PGIC__)
5560     (void)fprintf (st, "\r\n  Compiler: Portland Group/PGI C/C++");
5561 #elif defined(__VBCC__)
5562     (void)fprintf (st, "\r\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
5563 #elif defined(__WATCOMC__)
5564     (void)fprintf (st, "\r\n  Compiler: Watcom C/C++ %d.%d",
5565                    __WATCOMC__ / 100,
5566                    __WATCOMC__ % 100);
5567 #elif defined(__xlC__)
5568     (void)fprintf (st, "\r\n  Compiler: IBM XL C/C++");
5569 #elif defined(__INTEL_COMPILER) || defined(__ICC)
5570 # if defined(__INTEL_COMPILER_UPDATE)
5571 #  if defined(__INTEL_COMPILER_BUILD_DATE)
5572     (void)fprintf (st, "\r\n  Compiler: Intel C++ Compiler %d.%d (%d)",
5573                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE,
5574                    __INTEL_COMPILER_BUILD_DATE);
5575 #  else
5576     (void)fprintf (st, "\r\n  Compiler: Intel C++ Compiler %d.%d",
5577                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE);
5578 #  endif
5579 # else
5580     (void)fprintf (st, "\r\n  Compiler: Intel C++ Compiler %d",
5581                    __INTEL_COMPILER);
5582 # endif
5583 #elif defined(SIM_COMPILER)
5584 # define S_xstr(a) S_str(a)
5585 # define S_str(a) #a
5586     (void)fprintf (st, "\r\n  Compiler: %s", S_xstr(SIM_COMPILER));
5587 # undef S_str
5588 # undef S_xstr
5589 #else
5590     (void)fprintf (st, "\r\n  Compiler: Unknown");
5591 #endif
5592 
5593 #if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || \
5594     defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
5595 # define SC_IS_PPC64 1
5596 #else
5597 # define SC_IS_PPC64 0
5598 #endif
5599 
5600 #if defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__PPC) || \
5601     defined(__ppc32__) || defined(__PPC32__) || defined(__powerpc32__) || defined(__POWERPC32__) || defined(_M_PPC32) || \
5602     defined(__PPC32)
5603 # define SC_IS_PPC32 1
5604 #else
5605 # define SC_IS_PPC32 0
5606 #endif
5607 
5608 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
5609     arch = " x86_64";
5610 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
5611     arch = " x86";
5612 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
5613     arch = " arm64";
5614 #elif defined(_M_ARM) || defined(__arm__)
5615     arch = " arm";
5616 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
5617     arch = " ia64";
5618 #elif SC_IS_PPC64
5619     arch = " powerpc64";
5620 #elif SC_IS_PPC32
5621     arch = " powerpc";
5622 #elif defined(__s390x__)
5623     arch = " s390x";
5624 #elif defined(__s390__)
5625     arch = " s390";
5626 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
5627     arch = " j2";
5628 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
5629     arch = " sh4";
5630 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
5631     arch = " sh2";
5632 #elif defined(__alpha__)
5633     arch = " alpha";
5634 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
5635     arch = " hppa";
5636 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
5637     arch = " ice9";
5638 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
5639     arch = " mips64";
5640 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
5641     arch = " mips";
5642 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || \
5643       defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
5644     arch = " openrisc";
5645 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
5646     arch = " sparc64";
5647 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
5648     arch = " sparc";
5649 #elif defined(__riscv) || defined(__riscv__)
5650     arch = " riscv";
5651 #elif defined(__e2k__) || defined(__E2K__) || defined(__elbrus64__) || defined(__elbrus__) || defined(__ELBRUS__)
5652     arch = " e2k";
5653 #elif defined(__myriad2__)
5654     arch = " myriad2";
5655 #elif defined(__loongarch64) || defined(__loongarch__)
5656     arch = " loongarch";
5657 #elif defined(_m68851) || defined(__m68k__) || defined(__m68000__) || defined(__M68K)
5658     arch = " m68k";
5659 #elif defined(__m88k__) || defined(__m88000__) || defined(__M88K)
5660     arch = " m88k";
5661 #elif defined(__VAX__) || defined(__vax__)
5662     arch = " vax";
5663 #elif defined(__NIOS2__) || defined(__nios2__)
5664     arch = " nios2";
5665 #elif defined(__MICROBLAZE__) || defined(__microblaze__)
5666     arch = " microblaze";
5667 #else
5668     arch = " ";
5669 #endif
5670     (void)fprintf (st, "%s", arch);
5671 #if defined(BUILD_BY_USER)
5672         (void)fprintf (st, "\r\n  Built by: %s", BUILD_BY_USER);
5673 #else
5674 # if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
5675         (void)fprintf (st, "\r\n  Built by: %s", VER_H_PREP_USER);
5676 # endif
5677 #endif
5678                 (void)fprintf (st, "\r\n\r\n Host System Information:");
5679 #if defined(_WIN32)
5680     if (1) {
5681         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
5682         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
5683         char osversion[PATH_MAX+1] = "";
5684         FILE *f;
5685 
5686         if ((f = _popen ("ver", "r"))) {
5687             (void)memset (osversion, 0, sizeof(osversion));
5688             do {
5689                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
5690                     break;
5691                 sim_trim_endspc (osversion);
5692                 } while (osversion[0] == '\0');
5693             _pclose (f);
5694             }
5695         (void)fprintf (st, "\r\n   Host OS: %s", osversion);
5696         (void)fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
5697         }
5698 #else
5699     if (1) {
5700         char osversion[2*PATH_MAX+1] = "";
5701         FILE *f;
5702 # if !defined(_AIX)
5703         if ((f = popen \
5704              ("uname -mrs 2> /dev/null", "r"))) {
5705 # else
5706         if ((f = popen \
5707              ("sh -c 'echo \"$(command -p env uname -v \
5708                2> /dev/null).$(command -p env uname -r \
5709                2> /dev/null) $(command -p env uname -p \
5710                2> /dev/null)\"' 2> /dev/null", "r"))) {
5711 # endif /* if !defined(_AIX) */
5712             (void)memset (osversion, 0, sizeof(osversion));
5713             do {
5714               if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
5715                     break;
5716               }
5717             sim_trim_endspc (osversion);
5718             } while (osversion[0] == '\0');
5719             pclose (f);
5720             strremove(osversion, "0000000000000000 ");
5721             strremove(osversion, " 0000000000000000");
5722             strremove(osversion, "000000000000 ");
5723             strremove(osversion, " 000000000000");
5724             strremove(osversion, "IBM ");
5725             strremove(osversion, " (emulated by qemu)");
5726             strremove(osversion, " (emulated by QEMU)");
5727         }
5728 # if !defined(_AIX)
5729             (void)fprintf (st, "\r\n   Host OS: %s", osversion);
5730 # else
5731             strremove(osversion, "AIX ");
5732 #  if !defined(__PASE__)
5733             (void)fprintf (st, "\r\n   Host OS: IBM AIX %s", osversion);
5734 #  else
5735             (void)fprintf (st, "\r\n   Host OS: IBM OS/400 (PASE) %s", osversion);
5736 #  endif /* if !defined(__PASE__) */
5737 # endif /* if !defined(_AIX) */
5738     } else {
5739 # if !defined(_AIX)
5740         (void)fprintf (st, "\r\n   Host OS: Unknown");
5741 # else
5742 #  if !defined(__PASE__)
5743         (void)fprintf (st, "\r\n   Host OS: IBM AIX");
5744 #  else
5745         (void)fprintf (st, "\r\n   Host OS: IBM OS/400 (PASE)");
5746 #  endif /* if !defined(__PASE__) */
5747 # endif /* if !defined(_AIX) */
5748     }
5749 #endif
5750     if (nodist)
5751       {
5752         sim_printf ("\r\n\r\n ********* LICENSE RESTRICTED BUILD *** NOT FOR REDISTRIBUTION *********\r\n");
5753       }
5754     else
5755       {
5756         (void)fprintf (st, "\r\n");
5757         (void)fprintf (st, "\r\n This software is made available under the terms of the ICU License.");
5758         (void)fprintf (st, "\r\n For complete license details, see the LICENSE file included with the");
5759         (void)fprintf (st, "\r\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
5760       }
5761         (void)fprintf (st, "\r\n");
5762     }
5763 return SCPE_OK;
5764 }
5765 
5766 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5767 {
5768 size_t i;
5769 DEVICE *dptr;
5770 t_bool only_enabled = (sim_switches & SWMASK ('E'));
5771 
5772 if (cptr && (*cptr != 0))
5773     return SCPE_2MARG;
5774 (void)fprintf (st, "%s simulator configuration%s\r\n\r\n", sim_name, only_enabled ? " (enabled devices)" : "");
5775 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5776     if (!only_enabled || !qdisable (dptr))
5777         show_device (st, dptr, flag);
5778 return SCPE_OK;
5779 }
5780 
5781 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5782 {
5783 int32 i;
5784 DEVICE *dptr;
5785 
5786 if (cptr && (*cptr != 0))
5787     return SCPE_2MARG;
5788 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5789     show_dev_logicals (st, dptr, NULL, 1, cptr);
5790 return SCPE_OK;
5791 }
5792 
5793 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5794 {
5795 if (dptr->lname)
5796     (void)fprintf (st, "%s -> %s\r\n", dptr->lname, dptr->name);
5797 else if (!flag)
5798     fputs ("no logical name assigned\r\n", st);
5799 return SCPE_OK;
5800 }
5801 
5802 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5803 {
5804 DEVICE *dptr;
5805 UNIT *uptr;
5806 int32 accum;
5807 
5808 if (cptr && (*cptr != 0))
5809     return SCPE_2MARG;
5810 if (sim_clock_queue == QUEUE_LIST_END)
5811     (void)fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructions/sec\r\n",
5812                    sim_name, sim_time, sim_timer_inst_per_sec ());
5813 else {
5814     const char *tim;
5815 
5816     (void)fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\r\n",
5817                    sim_name, sim_time, sim_timer_inst_per_sec ());
5818     accum = 0;
5819     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
5820         if (uptr == &sim_step_unit)
5821             (void)fprintf (st, "  Step timer");
5822         else
5823             if (uptr == &sim_expect_unit)
5824                 (void)fprintf (st, "  Expect fired");
5825             else
5826                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
5827                     (void)fprintf (st, "  %s", sim_dname (dptr));
5828                     if (dptr->numunits > 1)
5829                         (void)fprintf (st, " unit %d", (int32) (uptr - dptr->units));
5830                     }
5831                 else
5832                     (void)fprintf (st, "  Unknown");
5833         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
5834         (void)fprintf (st, " at %d%s%s%s%s\r\n", accum + uptr->time,
5835                        (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
5836                        (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
5837         accum = accum + uptr->time;
5838         }
5839     }
5840 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
5841 return SCPE_OK;
5842 }
5843 
5844 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5845 {
5846 if (cptr && (*cptr != 0))
5847     return SCPE_2MARG;
5848 (void)fprintf (st, "Time:\t%.0f\r\n", sim_gtime());
5849 return SCPE_OK;
5850 }
5851 
5852 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5853 {
5854 t_stat r;
5855 
5856 if (cptr && (*cptr != 0))
5857     r = ssh_break (st, cptr, 1);  /* more? */
5858 else
5859     r = sim_brk_showall (st, (uint32)sim_switches);
5860 return r;
5861 }
5862 
5863 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5864 {
5865 (void)fprintf (st, "Radix=%d\r\n", dptr->dradix);
5866 return SCPE_OK;
5867 }
5868 
5869 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5870 {
5871 int32 any = 0;
5872 DEBTAB *dep;
5873 
5874 if (dptr->flags & DEV_DEBUG) {
5875     if (dptr->dctrl == 0)
5876         fputs ("Debugging disabled", st);
5877     else if (dptr->debflags == NULL)
5878         fputs ("Debugging enabled", st);
5879     else {
5880         uint32 dctrl = dptr->dctrl;
5881 
5882         fputs ("Debug=", st);
5883         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
5884             if ((dctrl & dep->mask) == dep->mask) {
5885                 dctrl &= ~dep->mask;
5886                 if (any)
5887                     fputc (';', st);
5888                 fputs (dep->name, st);
5889                 any = 1;
5890                 }
5891             }
5892         }
5893     fputc ('\n', st);
5894     return SCPE_OK;
5895     }
5896 else return SCPE_NOFNC;
5897 }
5898 
5899 /* Show On actions */
5900 
5901 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5902 {
5903 int32 lvl, i;
5904 
5905 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
5906 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
5907     if (lvl > 0)
5908         (void)fprintf(st, "On Processing at Do Nest Level: %d", lvl);
5909     else
5910         (void)fprintf(st, "On Processing for input commands");
5911     (void)fprintf(st, " is %s\r\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
5912     for (i=1; i<SCPE_BASE; ++i) {
5913         if (sim_on_actions[lvl][i])
5914             (void)fprintf(st, "    on %5d    %s\r\n", i, sim_on_actions[lvl][i]); }
5915     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
5916         if (sim_on_actions[lvl][i])
5917             (void)fprintf(st, "    on %-5s    %s\r\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
5918     if (sim_on_actions[lvl][0])
5919         (void)fprintf(st, "    on ERROR    %s\r\n", sim_on_actions[lvl][0]);
5920     (void)fprintf(st, "\r\n");
5921     }
5922 if (sim_on_inherit)
5923     (void)fprintf(st, "on state and actions are inherited by nested do commands and subroutines\r\n");
5924 return SCPE_OK;
5925 }
5926 
5927 /* Show modifiers */
5928 
5929 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5930 {
5931 int32 i;
5932 DEVICE *dptr;
5933 
5934 if (cptr && (*cptr != 0))                               /* now eol? */
5935     return SCPE_2MARG;
5936 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5937     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5938 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5939     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5940 return SCPE_OK;
5941 }
5942 
5943 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5944 {
5945 fprint_set_help (st, dptr);
5946 return SCPE_OK;
5947 }
5948 
5949 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
     /* [previous][next][first][last][top][bottom][index][help] */
5950 {
5951 MTAB *mptr;
5952 t_stat r = SCPE_OK;
5953 
5954 if (dptr->modifiers == NULL)
5955     return SCPE_OK;
5956 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
5957     if (mptr->pstring &&
5958         ((mptr->mask & MTAB_XTD)?
5959             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
5960             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
5961         if (*toks > 0) {
5962             (void)fprintf (st, "\r\n");
5963             *toks = 0;
5964             }
5965         if (r == SCPE_OK)
5966             fprint_sep (st, toks);
5967         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
5968         }
5969     }
5970 return SCPE_OK;
5971 }
5972 
5973 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
     /* [previous][next][first][last][top][bottom][index][help] */
5974     CONST char *cptr, int32 flag)
5975 {
5976 t_stat r = SCPE_OK;
5977 
5978 if (mptr->disp)
5979     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
5980 else
5981     fputs (mptr->pstring, st);
5982 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
5983     fputc ('\n', st);
5984 return r;
5985 }
5986 
5987 /* Show show commands */
5988 
5989 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5990 {
5991 int32 i;
5992 DEVICE *dptr;
5993 
5994 if (cptr && (*cptr != 0))                               /* now eol? */
5995     return SCPE_2MARG;
5996 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5997     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5998 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5999     show_dev_show_commands (st, dptr, NULL, flag, cptr);
6000 return SCPE_OK;
6001 }
6002 
6003 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] */
6004 {
6005 fprint_show_help (st, dptr);
6006 return SCPE_OK;
6007 }
6008 
6009 /* Breakpoint commands */
6010 
6011 t_stat brk_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6012 {
6013 GET_SWITCHES (cptr);                                    /* get switches */
6014 return ssh_break (NULL, cptr, flg);                     /* call common code */
6015 }
6016 
6017 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
     /* [previous][next][first][last][top][bottom][index][help] */
6018 {
6019 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
6020 CONST char *tptr, *t1ptr;
6021 DEVICE *dptr = sim_dflt_dev;
6022 UNIT *uptr;
6023 t_stat r;
6024 t_addr lo, hi, max;
6025 int32 cnt;
6026 
6027 if (sim_brk_types == 0)
6028     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\r\n");
6029 if (dptr == NULL)
6030     return SCPE_IERR;
6031 uptr = dptr->units;
6032 if (uptr == NULL)
6033     return SCPE_IERR;
6034 max = uptr->capac - 1;
6035 abuf[sizeof(abuf)-1] = '\0';
6036 strncpy (abuf, cptr, sizeof(abuf)-1);
6037 cptr = abuf;
6038 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
6039     if (flg != SSH_ST)                                  /* only on SET */
6040         return sim_messagef (SCPE_ARG, "Invalid argument: %s\r\n", aptr);
6041     *aptr++ = 0;                                        /* separate strings */
6042     }
6043 if (*cptr == 0) {                                       /* no argument? */
6044     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
6045     return ssh_break_one (st, flg, lo, 0, aptr);
6046     }
6047 while (*cptr) {
6048     cptr = get_glyph (cptr, gbuf, ',');
6049     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
6050     if (tptr == NULL)
6051         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\r\n", gbuf);
6052     if (*tptr == '[') {
6053         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
6054         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
6055             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\r\n", tptr + 1);
6056         tptr = t1ptr + 1;
6057         }
6058     else cnt = 0;
6059     if (*tptr != 0)
6060         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\r\n", tptr);
6061     if ((lo == 0) && (hi == max)) {
6062         if (flg == SSH_CL)
6063             sim_brk_clrall (sim_switches);
6064         else
6065             if (flg == SSH_SH)
6066                 sim_brk_showall (st, (uint32)sim_switches);
6067             else
6068                 return SCPE_ARG;
6069         }
6070     else {
6071         for ( ; lo <= hi; lo = lo + 1) {
6072             r = ssh_break_one (st, flg, lo, cnt, aptr);
6073             if (r != SCPE_OK)
6074                 return r;
6075             }
6076         }
6077     }
6078 return SCPE_OK;
6079 }
6080 
6081 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] */
6082 {
6083 if (!sim_brk_types)
6084     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\r\n");
6085 switch (flg) {
6086     case SSH_ST:
6087         return sim_brk_set (lo, sim_switches, cnt, aptr);
6088         /*NOTREACHED*/ /* unreachable */
6089         break;
6090 
6091     case SSH_CL:
6092         return sim_brk_clr (lo, sim_switches);
6093         /*NOTREACHED*/ /* unreachable */
6094         break;
6095 
6096     case SSH_SH:
6097         return sim_brk_show (st, lo, sim_switches);
6098         /*NOTREACHED*/ /* unreachable */
6099         break;
6100 
6101     default:
6102         return SCPE_ARG;
6103     }
6104 }
6105 
6106 /* Reset command and routines */
6107 
6108 static t_bool run_cmd_did_reset = FALSE;
6109 
6110 t_stat reset_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6111 {
6112 char gbuf[CBUFSIZE];
6113 DEVICE *dptr;
6114 
6115 GET_SWITCHES (cptr);                                    /* get switches */
6116 run_cmd_did_reset = FALSE;
6117 if (*cptr == 0)                                         /* reset(cr) */
6118     return (reset_all (0));
6119 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6120 if (*cptr != 0)                                         /* now eol? */
6121     return SCPE_2MARG;
6122 if (strcmp (gbuf, "ALL") == 0)
6123     return (reset_all (0));
6124 dptr = find_dev (gbuf);                                 /* locate device */
6125 if (dptr == NULL)                                       /* found it? */
6126     return SCPE_NXDEV;
6127 if (dptr->reset != NULL)
6128     return dptr->reset (dptr);
6129 else return SCPE_OK;
6130 }
6131 
6132 /* Reset devices start..end
6133 
6134    Inputs:
6135         start   =       number of starting device
6136    Outputs:
6137         status  =       error status
6138 */
6139 
6140 t_stat reset_all (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
6141 {
6142 DEVICE *dptr;
6143 uint32 i;
6144 t_stat reason;
6145 
6146 for (i = 0; i < start; i++) {
6147     if (sim_devices[i] == NULL)
6148         return SCPE_IERR;
6149     }
6150 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
6151     if (dptr->reset != NULL) {
6152         reason = dptr->reset (dptr);
6153         if (reason != SCPE_OK)
6154             return reason;
6155         }
6156     }
6157 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
6158     if (dptr->reset != NULL) {
6159         reason = dptr->reset (dptr);
6160         if (reason != SCPE_OK)
6161             return reason;
6162         }
6163     }
6164 return SCPE_OK;
6165 }
6166 
6167 /* Reset to powerup state
6168 
6169    Inputs:
6170         start   =       number of starting device
6171    Outputs:
6172         status  =       error status
6173 */
6174 
6175 t_stat reset_all_p (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
6176 {
6177 t_stat r;
6178 int32 old_sw = sim_switches;
6179 
6180 sim_switches = SWMASK ('P');
6181 r = reset_all (start);
6182 sim_switches = old_sw;
6183 return r;
6184 }
6185 
6186 /* Attach command */
6187 
6188 t_stat attach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6189 {
6190 char gbuf[4*CBUFSIZE];
6191 DEVICE *dptr;
6192 UNIT *uptr;
6193 t_stat r;
6194 
6195 GET_SWITCHES (cptr);                                    /* get switches */
6196 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6197     return SCPE_2FARG;
6198 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6199 GET_SWITCHES (cptr);                                    /* get switches */
6200 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* now eol? */
6201     return SCPE_2FARG;
6202 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6203 if (dptr == NULL)                                       /* found dev? */
6204     return SCPE_NXDEV;
6205 if (uptr == NULL)                                       /* valid unit? */
6206     return SCPE_NXUN;
6207 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
6208     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
6209         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
6210         r = scp_detach_unit (dptr, uptr);               /* detach it */
6211         if (r != SCPE_OK)                               /* error? */
6212             return r; }
6213     else {
6214         if (!(uptr->dynflags & UNIT_ATTMULT))
6215             return SCPE_ALATT;                          /* Already attached */
6216         }
6217     }
6218 gbuf[sizeof(gbuf)-1] = '\0';
6219 strncpy (gbuf, cptr, sizeof(gbuf)-1);
6220 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
6221 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
6222 }
6223 
6224 /* Call device-specific or file-oriented attach unit routine */
6225 
6226 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6227 {
6228 if (dptr->attach != NULL)                               /* device routine? */
6229     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
6230 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
6231 }
6232 
6233 /* Attach unit to file */
6234 
6235 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6236 {
6237 DEVICE *dptr;
6238 
6239 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6240     return SCPE_UDIS;
6241 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
6242     return SCPE_NOATT;
6243 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6244     return SCPE_NOATT;
6245 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
6246 if (uptr->filename == NULL)
6247     return SCPE_MEM;
6248 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
6249 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
6250     ((uptr->flags & UNIT_RO) != 0)) {
6251     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
6252         ((uptr->flags & UNIT_RO) == 0))
6253         return attach_err (uptr, SCPE_NORO);            /* no, error */
6254     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
6255     if (uptr->fileref == NULL)                          /* open fail? */
6256         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
6257     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
6258     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6259         sim_printf ("%s: unit is read only (%s)\r\n", sim_dname (dptr), cptr);
6260         }
6261     }
6262 else {
6263     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
6264         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
6265         if (uptr->fileref == NULL)                      /* open fail? */
6266             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
6267         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6268             sim_printf ("%s: creating new file (%s)\r\n", sim_dname (dptr), cptr);
6269             }
6270         }
6271     else {                                              /* normal */
6272         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
6273         if (uptr->fileref == NULL) {                    /* open fail? */
6274 #if defined(EWOULDBLOCK)
6275             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
6276 #else
6277             if ((errno == EAGAIN))
6278 #endif
6279                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6280 
6281 #if defined(EPERM)
6282             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
6283 #else
6284             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
6285 #endif
6286                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
6287                     return attach_err (uptr, SCPE_NORO);/* no error */
6288                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
6289                 if (uptr->fileref == NULL)              /* open fail? */
6290                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6291                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
6292                 if (!sim_quiet) {
6293                     sim_printf ("%s: unit is read only (%s)\r\n", sim_dname (dptr), cptr);
6294                     }
6295                 }
6296             else {                                      /* doesn't exist */
6297                 if (sim_switches & SWMASK ('E'))        /* must exist? */
6298                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6299                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
6300                 if (uptr->fileref == NULL)              /* open fail? */
6301                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6302                 if (!sim_quiet) {
6303                     sim_printf ("%s: creating new file (%s)\r\n", sim_dname (dptr), cptr);
6304                     }
6305                 }
6306             }                                           /* end if null */
6307         }                                               /* end else */
6308     }
6309 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
6310     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
6311     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
6312         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
6313     if (uptr->filebuf == NULL)                          /* no buffer? */
6314         return attach_err (uptr, SCPE_MEM);             /* error */
6315     if (!sim_quiet) {
6316         sim_printf ("%s: buffering file in memory\r\n", sim_dname (dptr));
6317         }
6318     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
6319         SZ_D (dptr), cap, uptr->fileref);
6320     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
6321     }
6322 uptr->flags = uptr->flags | UNIT_ATT;
6323 uptr->pos = 0;
6324 return SCPE_OK;
6325 }
6326 
6327 t_stat attach_err (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
6328 {
6329 FREE (uptr->filename);
6330 uptr->filename = NULL;
6331 return stat;
6332 }
6333 
6334 /* Detach command */
6335 
6336 t_stat detach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6337 {
6338 char gbuf[CBUFSIZE];
6339 DEVICE *dptr;
6340 UNIT *uptr;
6341 
6342 GET_SWITCHES (cptr);                                    /* get switches */
6343 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6344     return SCPE_2FARG;
6345 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6346 if (*cptr != 0)                                         /* now eol? */
6347     return SCPE_2MARG;
6348 if (strcmp (gbuf, "ALL") == 0)
6349     return (detach_all (0, FALSE));
6350 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6351 if (dptr == NULL)                                       /* found dev? */
6352     return SCPE_NXDEV;
6353 if (uptr == NULL)                                       /* valid unit? */
6354     return SCPE_NXUN;
6355 return scp_detach_unit (dptr, uptr);                    /* detach */
6356 }
6357 
6358 /* Detach devices start..end
6359 
6360    Inputs:
6361         start   =       number of starting device
6362         shutdown =      TRUE if simulator shutting down
6363    Outputs:
6364         status  =       error status
6365 
6366    Note that during shutdown, detach routines for non-attachable devices
6367    will be called.  These routines can implement simulator shutdown.  Error
6368    returns during shutdown are ignored.
6369 */
6370 
6371 t_stat detach_all (int32 start, t_bool shutdown)
     /* [previous][next][first][last][top][bottom][index][help] */
6372 {
6373 uint32 i, j;
6374 DEVICE *dptr;
6375 UNIT *uptr;
6376 t_stat r;
6377 
6378 if ((start < 0) || (start > 1))
6379     return SCPE_IERR;
6380 if (shutdown)
6381     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
6382 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
6383     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
6384         uptr = (dptr->units) + j;
6385         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
6386             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
6387             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
6388             r = scp_detach_unit (dptr, uptr);           /* detach unit */
6389 
6390             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
6391                 return r;                               /* bail out now with error status */
6392             }
6393         }
6394     }
6395 return SCPE_OK;
6396 }
6397 
6398 /* Call device-specific or file-oriented detach unit routine */
6399 
6400 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6401 {
6402 if (dptr->detach != NULL)                               /* device routine? */
6403     return dptr->detach (uptr);
6404 return detach_unit (uptr);                              /* no, standard */
6405 }
6406 
6407 /* Detach unit from file */
6408 
6409 t_stat detach_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6410 {
6411 DEVICE *dptr;
6412 
6413 if (uptr == NULL)
6414     return SCPE_IERR;
6415 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
6416     return SCPE_NOATT;
6417 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
6418     if (sim_switches & SIM_SW_REST)                     /* restoring? */
6419         return SCPE_OK;                                 /* allow detach */
6420     else
6421         return SCPE_NOTATT;                             /* complain */
6422     }
6423 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6424     return SCPE_OK;
6425 if (uptr->flags & UNIT_BUF) {
6426     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
6427     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
6428         if (!sim_quiet) {
6429             sim_printf ("%s: writing buffer to file\r\n", sim_dname (dptr));
6430             }
6431         rewind (uptr->fileref);
6432         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
6433         if (ferror (uptr->fileref))
6434             sim_printf ("%s: I/O error - %s (Error %d)\r\n",
6435                         sim_dname (dptr), xstrerror_l(errno), errno);
6436         }
6437     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
6438         FREE (uptr->filebuf);                           /* free buf */
6439         uptr->filebuf = NULL;
6440         }
6441     uptr->flags = uptr->flags & ~UNIT_BUF;
6442     }
6443 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
6444 FREE (uptr->filename);
6445 uptr->filename = NULL;
6446 if (fclose (uptr->fileref) == EOF)
6447     return SCPE_IOERR;
6448 return SCPE_OK;
6449 }
6450 
6451 /* Get device display name */
6452 
6453 const char *sim_dname (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6454 {
6455 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
6456 }
6457 
6458 /* Get unit display name */
6459 
6460 const char *sim_uname (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6461 {
6462 DEVICE *d = find_dev_from_unit(uptr);
6463 static char uname[CBUFSIZE];
6464 
6465 if (!d)
6466     return "";
6467 if (d->numunits == 1)
6468     return sim_dname (d);
6469 (void)sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
6470 return uname;
6471 }
6472 
6473 /* Run, go, boot, cont, step, next commands */
6474 
6475 t_stat run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6476 {
6477 char gbuf[CBUFSIZE] = "";
6478 CONST char *tptr;
6479 uint32 i, j;
6480 int32 sim_next = 0;
6481 int32 unitno;
6482 t_value pcv, orig_pcv;
6483 t_stat r;
6484 DEVICE *dptr;
6485 UNIT *uptr;
6486 
6487 GET_SWITCHES (cptr);                                    /* get switches */
6488 sim_step = 0;
6489 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
6490     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
6491     if (*cptr != 0) {                                   /* argument? */
6492         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6493         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
6494             if (sim_dflt_dev && sim_vm_parse_addr)      /* address parser? */
6495                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
6496             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
6497             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
6498                 (pcv > width_mask[sim_PC->width]))
6499                 return SCPE_ARG;
6500             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
6501             }
6502         }
6503     if ((flag == RU_RUN) &&                             /* run? */
6504         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
6505         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
6506         return r;
6507         }
6508     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { //-V600 /* should be end */
6509         int32 saved_switches = sim_switches;
6510 
6511         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6512             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
6513         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6514             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\r\n",
6515                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
6516         sim_switches = 0;
6517         GET_SWITCHES (cptr);
6518         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
6519             r = expect_cmd (1, cptr);
6520             if (r != SCPE_OK)
6521                 return r;
6522             }
6523         else {                                          /* BREAK UNTIL condition */
6524             if (sim_switches == 0)
6525                 sim_switches = sim_brk_dflt;
6526             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
6527             sim_brk_types |= BRK_TYP_TEMP;
6528             r = ssh_break (NULL, cptr, SSH_ST);
6529             if (r != SCPE_OK)
6530                 return sim_messagef (r, "Unable to establish breakpoint at: %s\r\n", cptr);
6531             }
6532         sim_switches = saved_switches;
6533         }
6534     }
6535 
6536 else if ((flag == RU_STEP) ||
6537          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
6538     static t_bool not_implemented_message = FALSE;
6539 
6540     if ((!not_implemented_message) && (flag == RU_NEXT)) {
6541         not_implemented_message = TRUE;
6542         flag = RU_STEP;
6543         }
6544     if (*cptr != 0) {                                   /* argument? */
6545         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6546         if (*cptr != 0)                                 /* should be end */
6547             return SCPE_2MARG;
6548         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6549         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
6550             return SCPE_ARG;
6551         }
6552     else sim_step = 1;
6553     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
6554         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
6555     }
6556 else if (flag == RU_NEXT) {                             /* next */
6557     t_addr *addrs;
6558 
6559     if (*cptr != 0) {                                   /* argument? */
6560         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6561         if (*cptr != 0)                                 /* should be end */
6562             return SCPE_2MARG;
6563         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6564         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
6565             return SCPE_ARG;
6566         }
6567     else sim_next = 1;
6568     if (sim_vm_is_subroutine_call(&addrs)) {
6569         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6570         for (i=0; addrs[i]; i++)
6571             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6572         }
6573     else
6574         sim_step = 1;
6575     }
6576 else if (flag == RU_BOOT) {                             /* boot */
6577     if (*cptr == 0)                                     /* must be more */
6578         return SCPE_2FARG;
6579     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
6580     if (*cptr != 0)                                     /* should be end */
6581         return SCPE_2MARG;
6582     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
6583     if (dptr == NULL)                                   /* found dev? */
6584         return SCPE_NXDEV;
6585     if (uptr == NULL)                                   /* valid unit? */
6586         return SCPE_NXUN;
6587     if (dptr->boot == NULL)                             /* can it boot? */
6588         return SCPE_NOFNC;
6589     if (uptr->flags & UNIT_DIS)                         /* disabled? */
6590         return SCPE_UDIS;
6591     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
6592         !(uptr->flags & UNIT_ATT))
6593         return SCPE_UNATT;
6594     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
6595     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
6596         return r;
6597     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
6598         return r;
6599     }
6600 
6601 else
6602     if (flag != RU_CONT)                                /* must be cont */
6603         return SCPE_IERR;
6604     else                                                /* CONTINUE command */
6605         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
6606             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\r\n");
6607 
6608 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
6609     return SCPE_OK;
6610 
6611 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
6612     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
6613         uptr = dptr->units + j;
6614         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
6615             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
6616         }
6617     }
6618 stop_cpu = 0;
6619 sim_is_running = 1;                                     /* flag running */
6620 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
6621     sim_is_running = 0;                                 /* flag idle */
6622     sim_ttcmd ();
6623     return SCPE_TTYERR;
6624     }
6625 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
6626     sim_is_running = 0;                                 /* flag idle */
6627     sim_ttcmd ();
6628     return r;
6629     }
6630 #if !defined(IS_WINDOWS)
6631 # if defined(SIGINT)
6632 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
6633     sim_is_running = 0;                                 /* flag idle */
6634     sim_ttcmd ();
6635     return SCPE_SIGERR;
6636     }
6637 # endif
6638 #endif
6639 #if !defined(IS_WINDOWS)
6640 # if defined(SIGHUP)
6641 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
6642     sim_is_running = 0;                                 /* flag idle */
6643     sim_ttcmd ();
6644     return SCPE_SIGERR;
6645     }
6646 # endif
6647 #endif
6648 #if !defined(IS_WINDOWS)
6649 # if defined(SIGTERM)
6650 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
6651     sim_is_running = 0;                                 /* flag idle */
6652     sim_ttcmd ();
6653     return SCPE_SIGERR;
6654     }
6655 # endif
6656 #endif
6657 if (sim_step)                                           /* set step timer */
6658     sim_activate (&sim_step_unit, sim_step);
6659 (void)fflush(stdout);                                   /* flush stdout */
6660 if (sim_log)                                            /* flush log if enabled */
6661     (void)fflush (sim_log);
6662 sim_rtcn_init_all ();                                   /* re-init clocks */
6663 sim_start_timer_services ();                            /* enable wall clock timing */
6664 
6665 do {
6666     t_addr *addrs;
6667 
6668     while (1) {
6669         r = sim_instr();
6670         if (r != SCPE_REMOTE)
6671             break;
6672         sim_remote_process_command ();                  /* Process the command and resume processing */
6673         }
6674     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
6675         (--sim_next <=0))
6676         break;
6677     if (sim_step == 0) {                                /* doing a NEXT? */
6678         t_addr val;
6679         BRKTAB *bp;
6680 
6681         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
6682             break;
6683         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
6684             val = (t_addr)(*sim_vm_pc_value)();
6685         else
6686             val = (t_addr)get_rval (sim_PC, 0);
6687         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6688             break;
6689         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
6690         }
6691     else {
6692         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
6693             break;
6694         }
6695     /* setup another next/step */
6696     sim_step = 0;
6697     if (sim_vm_is_subroutine_call(&addrs)) {
6698         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6699         for (i=0; addrs[i]; i++)
6700             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6701         }
6702     else
6703         sim_step = 1;
6704     if (sim_step)                                       /* set step timer */
6705         sim_activate (&sim_step_unit, sim_step);
6706     } while (1);
6707 
6708 sim_is_running = 0;                                     /* flag idle */
6709 sim_stop_timer_services ();                             /* disable wall clock timing */
6710 sim_ttcmd ();                                           /* restore console */
6711 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
6712 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
6713 #if defined(SIGHUP)
6714 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
6715 #endif
6716 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
6717 if (sim_log)                                            /* flush console log */
6718     (void)fflush (sim_log);
6719 if (sim_deb)                                            /* flush debug log */
6720     sim_debug_flush ();
6721 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
6722     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
6723         uptr = dptr->units + j;
6724         if (uptr->flags & UNIT_ATT) {                   /* attached, */
6725             if (uptr->io_flush)                         /* unit specific flush routine */
6726                 uptr->io_flush (uptr);                  /* call it */
6727             else {
6728                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
6729                     (uptr->fileref) &&                  /* real file, */
6730                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
6731                     !(uptr->flags & UNIT_RO))           /* not read only? */
6732                     (void)fflush (uptr->fileref);
6733                 }
6734             }
6735         }
6736     }
6737 sim_cancel (&sim_step_unit);                            /* cancel step timer */
6738 UPDATE_SIM_TIME;                                        /* update sim time */
6739 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6740 }
6741 
6742 /* run command message handler */
6743 
6744 void
6745 run_cmd_message (const char *unechoed_cmdline, t_stat r)
     /* [previous][next][first][last][top][bottom][index][help] */
6746 {
6747 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6748     sim_printf("%s> %s\r\n", do_position(), unechoed_cmdline);
6749 #if defined(WIN_STDIO)
6750 (void)fflush(stderr);
6751 (void)fflush(stdout);
6752 #endif /* if defined(WIN_STDIO) */
6753 fprint_stopped (stdout, r);                         /* print msg */
6754 if (sim_log && (sim_log != stdout))                 /* log if enabled */
6755     fprint_stopped (sim_log, r);
6756 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6757     fprint_stopped (sim_deb, r);
6758 #if defined(WIN_STDIO)
6759 (void)fflush(stderr);
6760 (void)fflush(stdout);
6761 #endif /* if defined(WIN_STDIO) */
6762 }
6763 
6764 /* Common setup for RUN or BOOT */
6765 
6766 t_stat sim_run_boot_prep (int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
6767 {
6768 UNIT *uptr;
6769 t_stat r;
6770 
6771 sim_interval = 0;                                       /* reset queue */
6772 sim_time = sim_rtime = 0;
6773 noqueue_time = 0;
6774 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6775     sim_clock_queue = uptr->next;
6776     uptr->next = NULL;
6777     }
6778 r = reset_all (0);
6779 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6780     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6781         sim_printf ("Resetting all devices...  This may not have been your intention.\r\n");
6782         sim_printf ("The GO and CONTINUE commands do not reset devices.\r\n");
6783         }
6784     run_cmd_did_reset = TRUE;
6785     }
6786 return r;
6787 }
6788 
6789 /* Print stopped message
6790  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6791  * call the indicated routine to print additional information after the message
6792  * and before the PC value is printed.  If the routine returns FALSE, skip
6793  * printing the PC and its related instruction.
6794  */
6795 
6796 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6797 {
6798 int32 i;
6799 t_stat r = 0;
6800 t_addr k;
6801 t_value pcval;
6802 
6803 fputc ('\n', st);                                       /* start on a new line */
6804 
6805 if (v >= SCPE_BASE)                                     /* SCP error? */
6806     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
6807 else {                                                  /* VM error */
6808     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6809 
6810     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6811         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6812         return;                                         /*     we're done */
6813     }
6814 
6815 (void)fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6816 
6817 pcval = get_rval (pc, 0);
6818 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6819     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6820 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6821     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6822 if ((dptr != NULL) && (dptr->examine != NULL)) {
6823     for (i = 0; i < sim_emax; i++)
6824         sim_eval[i] = 0;
6825     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6826         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6827             break;
6828         }
6829     if ((r == SCPE_OK) || (i > 0)) {
6830         (void)fprintf (st, " (");
6831         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6832             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6833         (void)fprintf (st, ")");
6834         }
6835     }
6836 (void)fprintf (st, "\r\n");
6837 return;
6838 }
6839 
6840 void fprint_stopped (FILE *st, t_stat v)
     /* [previous][next][first][last][top][bottom][index][help] */
6841 {
6842 #if defined(WIN_STDIO)
6843 (void)fflush(stderr);
6844 (void)fflush(stdout);
6845 #endif /* if defined(WIN_STDIO) */
6846 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6847 return;
6848 }
6849 
6850 /* Unit service for step timeout, originally scheduled by STEP n command
6851    Return step timeout SCP code, will cause simulation to stop */
6852 
6853 t_stat step_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6854 {
6855 return SCPE_STEP;
6856 }
6857 
6858 /* Unit service to facilitate expect matching to stop simulation.
6859    Return expect SCP code, will cause simulation to stop */
6860 
6861 t_stat expect_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6862 {
6863 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6864 }
6865 
6866 /* Signal handler for ^C signal - set stop simulation flag */
6867 
6868 void int_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help] */
6869 {
6870 stop_cpu = 1;
6871 return;
6872 }
6873 
6874 /* Examine/deposit commands */
6875 
6876 t_stat exdep_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6877 {
6878 char gbuf[CBUFSIZE];
6879 CONST char *gptr;
6880 CONST char *tptr = NULL;
6881 int32 opt;
6882 t_addr low, high;
6883 t_stat reason = SCPE_IERR;
6884 DEVICE *tdptr;
6885 REG *lowr, *highr;
6886 FILE *ofile;
6887 
6888 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6889 if (flag == EX_E)                                       /* extra for EX */
6890     opt = opt | CMD_OPT_OF;
6891 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6892 if (NULL == cptr)                                       /* error? */
6893     return reason;
6894 if (*cptr == 0)                                         /* must be more */
6895     return SCPE_2FARG;
6896 if (sim_dfunit == NULL)                                 /* got a unit? */
6897     return SCPE_NXUN;
6898 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6899 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6900 
6901     return SCPE_2FARG;
6902 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6903 
6904 for (gptr = gbuf, reason = SCPE_OK;
6905     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6906     tdptr = sim_dfdev;                                  /* working dptr */
6907     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6908         tptr = gptr + strlen ("STATE");
6909         if (*tptr && (*tptr++ != ','))
6910             return SCPE_ARG;
6911         if ((lowr = sim_dfdev->registers) == NULL)
6912             return SCPE_NXREG;
6913         for (highr = lowr; highr->name != NULL; highr++) ;
6914         sim_switches = sim_switches | SIM_SW_HIDE;
6915         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6916             lowr, --highr, 0, 0);
6917         continue;
6918         }
6919 
6920     /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
6921     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6922         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6923         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6924         low = high = 0;
6925         if ((*tptr == '-') || (*tptr == ':')) {
6926             highr = find_reg (tptr + 1, &tptr, tdptr);
6927             if (highr == NULL)
6928                 return SCPE_NXREG;
6929             }
6930         else {
6931             highr = lowr;
6932             if (*tptr == '[') {
6933                 if (lowr->depth <= 1)
6934                     return SCPE_ARG;
6935                 tptr = get_range (NULL, tptr + 1, &low, &high,
6936                     10, lowr->depth - 1, ']');
6937                 if (tptr == NULL)
6938                     return SCPE_ARG;
6939                 }
6940             }
6941         if (*tptr && (*tptr++ != ','))
6942             return SCPE_ARG;
6943         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6944             lowr, highr, (uint32) low, (uint32) high);
6945         continue;
6946         }
6947 
6948     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6949         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6950         sim_dfunit->capac - sim_dfdev->aincr), 0);
6951     if (tptr == NULL)
6952         return SCPE_ARG;
6953     if (*tptr && (*tptr++ != ','))
6954         return SCPE_ARG;
6955     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6956         sim_dfdev, sim_dfunit);
6957     }                                                   /* end for */
6958 if (sim_ofile)                                          /* close output file */
6959     fclose (sim_ofile);
6960 return reason;
6961 }
6962 
6963 /* Loop controllers for examine/deposit
6964 
6965    exdep_reg_loop       examine/deposit range of registers
6966    exdep_addr_loop      examine/deposit range of addresses
6967 */
6968 
6969 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6970     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6971 {
6972 t_stat reason;
6973 uint32 idx, val_start=lows;
6974 t_value val, last_val;
6975 REG *rptr;
6976 
6977 if ((lowr == NULL) || (highr == NULL))
6978     return SCPE_IERR;
6979 if (lowr > highr)
6980     return SCPE_ARG;
6981 for (rptr = lowr; rptr <= highr; rptr++) {
6982     if ((sim_switches & SIM_SW_HIDE) &&
6983         (rptr->flags & REG_HIDDEN))
6984         continue;
6985     val = last_val = 0;
6986     for (idx = lows; idx <= highs; idx++) {
6987         if (idx >= rptr->depth)
6988             return SCPE_SUB;
6989         val = get_rval (rptr, idx);
6990         if (schptr && !test_search (&val, schptr))
6991             continue;
6992         if (flag == EX_E) {
6993             if ((idx > lows) && (val == last_val))
6994                 continue;
6995             if (idx > val_start+1) {
6996                 if (idx-1 == val_start+1) {
6997                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6998                     if (reason != SCPE_OK)
6999                         return reason;
7000                     if (sim_log && (ofile == stdout))
7001                         ex_reg (sim_log, val, flag, rptr, idx-1);
7002                     }
7003                 else {
7004                     if (val_start+1 != idx-1) { //-V547
7005                         (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\r\n", rptr->name, val_start+1, rptr->name, idx-1);
7006                         if (sim_log && (ofile == stdout))
7007                             (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\r\n", rptr->name, val_start+1, rptr->name, idx-1);
7008                         }
7009                     else {
7010                         (void)Fprintf (ofile, "%s[%d]: same as above\r\n", rptr->name, val_start+1);
7011                         if (sim_log && (ofile == stdout))
7012                             (void)Fprintf (sim_log, "%s[%d]: same as above\r\n", rptr->name, val_start+1);
7013                         }
7014                     }
7015                 }
7016             sim_last_val = last_val = val;
7017             val_start = idx;
7018             reason = ex_reg (ofile, val, flag, rptr, idx);
7019             if (reason != SCPE_OK)
7020                 return reason;
7021             if (sim_log && (ofile == stdout))
7022                 ex_reg (sim_log, val, flag, rptr, idx);
7023             }
7024         if (flag != EX_E) {
7025             reason = dep_reg (flag, cptr, rptr, idx);
7026             if (reason != SCPE_OK)
7027                 return reason;
7028             }
7029         }
7030     if ((flag == EX_E) && (val_start != highs)) {
7031         if (highs == val_start+1) {
7032             reason = ex_reg (ofile, val, flag, rptr, highs);
7033             if (reason != SCPE_OK)
7034                 return reason;
7035             if (sim_log && (ofile == stdout))
7036                 ex_reg (sim_log, val, flag, rptr, highs);
7037             }
7038         else {
7039             if (val_start+1 != highs) { //-V547
7040                 (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\r\n", rptr->name, val_start+1, rptr->name, highs);
7041                 if (sim_log && (ofile == stdout))
7042                     (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\r\n", rptr->name, val_start+1, rptr->name, highs);
7043                 }
7044             else {
7045                 (void)Fprintf (ofile, "%s[%d]: same as above\r\n", rptr->name, val_start+1);
7046                 if (sim_log && (ofile == stdout))
7047                     (void)Fprintf (sim_log, "%s[%d]: same as above\r\n", rptr->name, val_start+1);
7048                 }
7049             }
7050         }
7051     }
7052 return SCPE_OK;
7053 }
7054 
7055 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
7056     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
7057 {
7058 t_addr i, mask;
7059 t_stat reason;
7060 
7061 if (uptr->flags & UNIT_DIS)                             /* disabled? */
7062     return SCPE_UDIS;
7063 mask = (t_addr) width_mask[dptr->awidth];
7064 if ((low > mask) || (high > mask) || (low > high))
7065     return SCPE_ARG;
7066 for (i = low; i <= high; ) {                            /* all paths must incr!! */
7067     reason = get_aval (i, dptr, uptr);                  /* get data */
7068     if (reason != SCPE_OK)                              /* return if error */
7069         return reason;
7070     if (schptr && !test_search (sim_eval, schptr))
7071         i = i + dptr->aincr;                            /* sch fails, incr */
7072     else {                                              /* no sch or success */
7073         if (flag != EX_D) {                             /* ex, ie, or id? */
7074             reason = ex_addr (ofile, flag, i, dptr, uptr);
7075             if (reason > SCPE_OK)
7076                 return reason;
7077             if (sim_log && (ofile == stdout))
7078                 ex_addr (sim_log, flag, i, dptr, uptr);
7079             }
7080         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
7081         if (flag != EX_E) {                             /* ie, id, or d? */
7082             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
7083             if (reason > SCPE_OK)
7084                 return reason;
7085             }
7086         i = i + (1 - reason);                           /* incr */
7087         }
7088     }
7089 return SCPE_OK;
7090 }
7091 
7092 /* Examine register routine
7093 
7094    Inputs:
7095         ofile   =       output stream
7096         val     =       current register value
7097         flag    =       type of ex/mod command (ex, iex, idep)
7098         rptr    =       pointer to register descriptor
7099         idx     =       index
7100    Outputs:
7101         return  =       error status
7102 */
7103 
7104 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
7105 {
7106 int32 rdx;
7107 
7108 if (rptr == NULL)
7109     return SCPE_IERR;
7110 if (rptr->depth > 1)
7111     (void)Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
7112 else
7113     (void)Fprintf (ofile, "%s:\t", rptr->name);
7114 if (!(flag & EX_E))
7115     return SCPE_OK;
7116 GET_RADIX (rdx, rptr->radix);
7117 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
7118     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
7119 else if (!(rptr->flags & REG_VMFLAGS) ||
7120     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
7121                  NULL, sim_switches | SIM_SW_REG) > 0)) {
7122         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
7123         if (rptr->fields) {
7124             (void)Fprintf (ofile, "\t");
7125             fprint_fields (ofile, val, val, rptr->fields);
7126             }
7127         }
7128 if (flag & EX_I)
7129     (void)Fprintf (ofile, "\t");
7130 else
7131     (void)Fprintf (ofile, "\r\n");
7132 return SCPE_OK;
7133 }
7134 
7135 /* Get register value
7136 
7137    Inputs:
7138         rptr    =       pointer to register descriptor
7139         idx     =       index
7140    Outputs:
7141         return  =       register value
7142 */
7143 
7144 t_value get_rval (REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
7145 {
7146 size_t sz;
7147 t_value val;
7148 uint32 *ptr;
7149 
7150 sz = SZ_R (rptr);
7151 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
7152     idx = idx + rptr->qptr;
7153     if (idx >= rptr->depth) idx = idx - rptr->depth;
7154     }
7155 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
7156     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
7157     if (sz <= sizeof (uint32))
7158         val = *ptr;
7159     else val = *((t_uint64 *) ptr); //-V1032
7160     }
7161 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
7162     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
7163     if (sz <= sizeof (uint32))
7164         val = *ptr;
7165     else val = *((t_uint64 *) ptr);
7166     }
7167 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7168     (sz == sizeof (uint8)))
7169     val = *(((uint8 *) rptr->loc) + idx);
7170 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7171     (sz == sizeof (uint16)))
7172     val = *(((uint16 *) rptr->loc) + idx);
7173 else if (sz <= sizeof (uint32))
7174      val = *(((uint32 *) rptr->loc) + idx);
7175 else val = *(((t_uint64 *) rptr->loc) + idx);
7176 val = (val >> rptr->offset) & width_mask[rptr->width];
7177 return val;
7178 }
7179 
7180 /* Deposit register routine
7181 
7182    Inputs:
7183         flag    =       type of deposit (normal/interactive)
7184         cptr    =       pointer to input string
7185         rptr    =       pointer to register descriptor
7186         idx     =       index
7187    Outputs:
7188         return  =       error status
7189 */
7190 
7191 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
7192 {
7193 t_stat r;
7194 t_value val, mask;
7195 int32 rdx;
7196 CONST char *tptr;
7197 char gbuf[CBUFSIZE];
7198 
7199 if ((cptr == NULL) || (rptr == NULL))
7200     return SCPE_IERR;
7201 if (rptr->flags & REG_RO)
7202     return SCPE_RO;
7203 if (flag & EX_I) {
7204     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7205     if (sim_log)
7206         (void)fprintf (sim_log, "%s\r\n", cptr? cptr: "");
7207     if (cptr == NULL)                                   /* force exit */
7208         return 1;
7209     if (*cptr == 0)                                     /* success */
7210         return SCPE_OK;
7211     }
7212 mask = width_mask[rptr->width];
7213 GET_RADIX (rdx, rptr->radix);
7214 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
7215     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
7216     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
7217         return SCPE_ARG;
7218     }
7219 else
7220     if (!(rptr->flags & REG_VMFLAGS) ||                 /* don't use sym? */
7221         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
7222                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
7223     val = get_uint (cptr, rdx, mask, &r);
7224     if (r != SCPE_OK)
7225         return SCPE_ARG;
7226     }
7227 if ((rptr->flags & REG_NZ) && (val == 0))
7228     return SCPE_ARG;
7229 put_rval (rptr, idx, val);
7230 return SCPE_OK;
7231 }
7232 
7233 /* Put register value
7234 
7235    Inputs:
7236         rptr    =       pointer to register descriptor
7237         idx     =       index
7238         val     =       new value
7239         mask    =       mask
7240    Outputs:
7241         none
7242 */
7243 
7244 void put_rval (REG *rptr, uint32 idx, t_value val)
     /* [previous][next][first][last][top][bottom][index][help] */
7245 {
7246 size_t sz;
7247 t_value mask;
7248 uint32 *ptr;
7249 
7250 #define PUT_RVAL(sz,rp,id,v,m)            \
7251     *(((sz *) rp->loc) + id) =            \
7252         (sz)((*(((sz *) rp->loc) + id) &  \
7253             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
7254 
7255 if (rptr == sim_PC)
7256     sim_brk_npc (0);
7257 sz = SZ_R (rptr);
7258 mask = width_mask[rptr->width];
7259 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
7260     idx = idx + rptr->qptr;
7261     if (idx >= rptr->depth)
7262         idx = idx - rptr->depth;
7263     }
7264 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
7265     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
7266     if (sz <= sizeof (uint32))
7267         *ptr = (*ptr &
7268         ~(((uint32) mask) << rptr->offset)) |
7269         (((uint32) val) << rptr->offset);
7270     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr) //-V1032
7271         & ~(mask << rptr->offset)) | (val << rptr->offset);
7272     }
7273 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
7274     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
7275     if (sz <= sizeof (uint32))
7276         *((uint32 *) ptr) = (*((uint32 *) ptr) &
7277         ~(((uint32) mask) << rptr->offset)) |
7278         (((uint32) val) << rptr->offset);
7279     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
7280         & ~(mask << rptr->offset)) | (val << rptr->offset);
7281     }
7282 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7283     (sz == sizeof (uint8)))
7284     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
7285 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7286     (sz == sizeof (uint16)))
7287     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
7288 else if (sz <= sizeof (uint32))
7289     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
7290 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
7291 return;
7292 }
7293 
7294 /* Examine address routine
7295 
7296    Inputs: (sim_eval is an implicit argument)
7297         ofile   =       output stream
7298         flag    =       type of ex/mod command (ex, iex, idep)
7299         addr    =       address to examine
7300         dptr    =       pointer to device
7301         uptr    =       pointer to unit
7302    Outputs:
7303         return  =       if > 0, error status
7304                         if <= 0,-number of extra addr units retired
7305 */
7306 
7307 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7308 {
7309 t_stat reason;
7310 int32 rdx;
7311 
7312 if (sim_vm_fprint_addr)
7313     sim_vm_fprint_addr (ofile, dptr, addr);
7314 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
7315 (void)Fprintf (ofile, ":\t");
7316 if (!(flag & EX_E))
7317     return (1 - dptr->aincr);
7318 
7319 GET_RADIX (rdx, dptr->dradix);
7320 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
7321     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
7322     reason = 1 - dptr->aincr;
7323     }
7324 if (flag & EX_I)
7325     (void)Fprintf (ofile, "\t");
7326 else
7327     (void)Fprintf (ofile, "\r\n");
7328 return reason;
7329 }
7330 
7331 /* Get address routine
7332 
7333    Inputs:
7334         flag    =       type of ex/mod command (ex, iex, idep)
7335         addr    =       address to examine
7336         dptr    =       pointer to device
7337         uptr    =       pointer to unit
7338    Outputs: (sim_eval is an implicit output)
7339         return  =       error status
7340 */
7341 
7342 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7343 {
7344 int32 i;
7345 t_value mask;
7346 t_addr j, loc;
7347 size_t sz;
7348 t_stat reason = SCPE_OK;
7349 
7350 if ((dptr == NULL) || (uptr == NULL))
7351     return SCPE_IERR;
7352 mask = width_mask[dptr->dwidth];
7353 for (i = 0; i < sim_emax; i++)
7354     sim_eval[i] = 0;
7355 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
7356     if (dptr->examine != NULL) {
7357         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
7358         if (reason != SCPE_OK)
7359             break;
7360         }
7361     else {
7362         if (!(uptr->flags & UNIT_ATT))
7363             return SCPE_UNATT;
7364         if (uptr->dynflags & UNIT_NO_FIO)
7365             return SCPE_NOFNC;
7366         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
7367             reason = SCPE_NXM;
7368             break;
7369             }
7370         sz = SZ_D (dptr);
7371         loc = j / dptr->aincr;
7372         if (uptr->flags & UNIT_BUF) {
7373             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
7374             }
7375         else {
7376             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7377             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
7378             if ((feof (uptr->fileref)) &&
7379                !(uptr->flags & UNIT_FIX)) {
7380                 reason = SCPE_EOF;
7381                 break;
7382                 }
7383             else if (ferror (uptr->fileref)) {
7384                 clearerr (uptr->fileref);
7385                 reason = SCPE_IOERR;
7386                 break;
7387                 }
7388             }
7389         }
7390     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
7391     }
7392 if ((reason != SCPE_OK) && (i == 0))
7393     return reason;
7394 return SCPE_OK;
7395 }
7396 
7397 /* Deposit address routine
7398 
7399    Inputs:
7400         flag    =       type of deposit (normal/interactive)
7401         cptr    =       pointer to input string
7402         addr    =       address to examine
7403         dptr    =       pointer to device
7404         uptr    =       pointer to unit
7405         dfltinc =       value to return on cr input
7406    Outputs:
7407         return  =       if > 0, error status
7408                         if <= 0, -number of extra address units retired
7409 */
7410 
7411 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
7412     UNIT *uptr, int32 dfltinc)
7413 {
7414 int32 i, count, rdx;
7415 t_addr j, loc;
7416 t_stat r, reason;
7417 t_value mask;
7418 size_t sz;
7419 char gbuf[CBUFSIZE];
7420 
7421 if (dptr == NULL)
7422     return SCPE_IERR;
7423 if (flag & EX_I) {
7424     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7425     if (sim_log)
7426         (void)fprintf (sim_log, "%s\r\n", cptr? cptr: "");
7427     if (cptr == NULL)                                   /* force exit */
7428         return 1;
7429     if (*cptr == 0)                                     /* success */
7430         return dfltinc;
7431     }
7432 if (uptr->flags & UNIT_RO)                              /* read only? */
7433     return SCPE_RO;
7434 mask = width_mask[dptr->dwidth];
7435 
7436 GET_RADIX (rdx, dptr->dradix);
7437 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
7438     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
7439     if (reason != SCPE_OK)
7440         return reason;
7441     reason = dfltinc;
7442     }
7443 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
7444 
7445 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
7446     sim_eval[i] = sim_eval[i] & mask;
7447     if (dptr->deposit != NULL) {
7448         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
7449         if (r != SCPE_OK)
7450             return r;
7451         }
7452     else {
7453         if (!(uptr->flags & UNIT_ATT))
7454             return SCPE_UNATT;
7455         if (uptr->dynflags & UNIT_NO_FIO)
7456             return SCPE_NOFNC;
7457         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
7458             return SCPE_NXM;
7459         sz = SZ_D (dptr);
7460         loc = j / dptr->aincr;
7461         if (uptr->flags & UNIT_BUF) {
7462             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
7463             if (loc >= uptr->hwmark)
7464                 uptr->hwmark = (uint32) loc + 1;
7465             }
7466         else {
7467             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7468             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
7469             if (ferror (uptr->fileref)) {
7470                 clearerr (uptr->fileref);
7471                 return SCPE_IOERR;
7472                 }
7473             }
7474         }
7475     }
7476 return reason;
7477 }
7478 
7479 /* Evaluate command */
7480 
7481 t_stat eval_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7482 {
7483 if (!sim_dflt_dev)
7484   return SCPE_ARG;
7485 DEVICE *dptr = sim_dflt_dev;
7486 int32 i, rdx, a, lim;
7487 t_stat r;
7488 
7489 GET_SWITCHES (cptr);
7490 GET_RADIX (rdx, dptr->dradix);
7491 for (i = 0; i < sim_emax; i++)
7492 sim_eval[i] = 0;
7493 if (*cptr == 0)
7494     return SCPE_2FARG;
7495 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
7496     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
7497     if (r != SCPE_OK)
7498         return r;
7499     }
7500 lim = 1 - r;
7501 for (i = a = 0; a < lim; ) {
7502     sim_printf ("%d:\t", a);
7503     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7504         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7505     if (sim_log) {
7506         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7507             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7508         }
7509     sim_printf ("\r\n");
7510     if (r < 0)
7511         a = a + 1 - r;
7512     else a = a + dptr->aincr;
7513     i = a / dptr->aincr;
7514     }
7515 return SCPE_OK;
7516 }
7517 
7518 /* String processing routines
7519 
7520    read_line            read line
7521 
7522    Inputs:
7523         cptr    =       pointer to buffer
7524         size    =       maximum size
7525         stream  =       pointer to input stream
7526    Outputs:
7527         optr    =       pointer to first non-blank character
7528                         NULL if EOF
7529 */
7530 
7531 char *read_line (char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7532 {
7533 return read_line_p (NULL, cptr, size, stream);
7534 }
7535 
7536 /* read_line_p          read line with prompt
7537 
7538    Inputs:
7539         prompt  =       pointer to prompt string
7540         cptr    =       pointer to buffer
7541         size    =       maximum size
7542         stream  =       pointer to input stream
7543    Outputs:
7544         optr    =       pointer to first non-blank character
7545                         NULL if EOF
7546 */
7547 
7548 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7549 {
7550 char *tptr;
7551 
7552 if (prompt) {                                           /* interactive? */
7553 #if defined(HAVE_LINEHISTORY)
7554         char *tmpc = linenoise (prompt);                /* get cmd line */
7555         if (tmpc == NULL)                               /* bad result? */
7556             cptr = NULL;
7557         else {
7558             strncpy (cptr, tmpc, size-1);               /* copy result */
7559             linenoiseHistoryAdd (tmpc);                 /* add to history */
7560             FREE (tmpc);                                /* free temp */
7561             }
7562         }
7563 #else
7564         (void)fflush (stdout);                          /* flush output */
7565         (void)printf ("%s", prompt);                    /* display prompt */
7566         (void)fflush (stdout);                          /* flush output */
7567         cptr = fgets (cptr, size, stream);              /* get cmd line */
7568         }
7569 #endif /* if defined(HAVE_LINEHISTORY) */
7570 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
7571 
7572 if (cptr == NULL) {
7573     clearerr (stream);                                  /* clear error */
7574     return NULL;                                        /* ignore EOF */
7575     }
7576 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
7577     if ((*tptr == '\n') || (*tptr == '\r') ||
7578         (tptr == (cptr + size - 1))) {                  /* str max length? */
7579         *tptr = 0;                                      /* terminate */
7580         break;
7581         }
7582     }
7583 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
7584     memmove (cptr, cptr + 3, strlen (cptr + 3));
7585 while (sim_isspace (*cptr))                             /* trim leading spc */
7586     cptr++;
7587 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
7588     if (sim_do_echo)                                    /* echo comments if -v */
7589         sim_printf("%s> %s\r\n", do_position(), cptr);
7590     *cptr = 0;
7591     }
7592 
7593 return cptr;
7594 }
7595 
7596 /* get_glyph            get next glyph (force upper case)
7597    get_glyph_nc         get next glyph (no conversion)
7598    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
7599    get_glyph_cmd        get command glyph (force upper case, extract leading !)
7600    get_glyph_gen        get next glyph (general case)
7601 
7602    Inputs:
7603         iptr        =   pointer to input string
7604         optr        =   pointer to output string
7605         mchar       =   optional end of glyph character
7606         uc          =   TRUE for convert to upper case (_gen only)
7607         quote       =   TRUE to allow quote enclosing values (_gen only)
7608         escape_char =   optional escape character within quoted strings (_gen only)
7609 
7610    Outputs
7611         result      =   pointer to next character in input string
7612 */
7613 
7614 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] */
7615 {
7616 t_bool quoting = FALSE;
7617 t_bool escaping = FALSE;
7618 char quote_char = 0;
7619 
7620 while ((*iptr != 0) &&
7621        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
7622     if (quote) {
7623         if (quoting) {
7624             if (!escaping) {
7625                 if (*iptr == escape_char)
7626                     escaping = TRUE;
7627                 else
7628                     if (*iptr == quote_char)
7629                         quoting = FALSE;
7630                 }
7631             else
7632                 escaping = FALSE;
7633             }
7634         else {
7635             if ((*iptr == '"') || (*iptr == '\'')) {
7636                 quoting = TRUE;
7637                 quote_char = *iptr;
7638                 }
7639             }
7640         }
7641     if (sim_islower (*iptr) && uc)
7642         *optr = (char)toupper (*iptr);
7643     else *optr = *iptr;
7644     iptr++; optr++;
7645     }
7646 if (mchar && (*iptr == mchar))              /* skip input terminator */
7647     iptr++;
7648 *optr = 0;                                  /* terminate result string */
7649 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
7650     iptr++;
7651 return iptr;
7652 }
7653 
7654 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7655 {
7656 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7657 }
7658 
7659 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7660 {
7661 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7662 }
7663 
7664 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7665 {
7666 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7667 }
7668 
7669 CONST char *get_glyph_cmd (const char *iptr, char *optr)
     /* [previous][next][first][last][top][bottom][index][help] */
7670 {
7671 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7672 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7673     strcpy (optr, "!");                     /* return ! as command glyph */
7674     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
7675     }
7676 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7677 }
7678 
7679 /* Trim trailing spaces from a string
7680 
7681     Inputs:
7682         cptr    =       pointer to string
7683     Outputs:
7684         cptr    =       pointer to string
7685 */
7686 
7687 char *sim_trim_endspc (char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7688 {
7689 char *tptr;
7690 
7691 tptr = cptr + strlen (cptr);
7692 while ((--tptr >= cptr) && sim_isspace (*tptr))
7693     *tptr = 0;
7694 return cptr;
7695 }
7696 
7697 int sim_isspace (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7698 {
7699 return (c & 0x80) ? 0 : isspace (c);
7700 }
7701 
7702 int sim_islower (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7703 {
7704 return (c & 0x80) ? 0 : islower (c);
7705 }
7706 
7707 int sim_isalpha (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7708 {
7709 return (c & 0x80) ? 0 : isalpha (c);
7710 }
7711 
7712 int sim_isprint (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7713 {
7714 return (c & 0x80) ? 0 : isprint (c);
7715 }
7716 
7717 int sim_isdigit (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7718 {
7719 return (c & 0x80) ? 0 : isdigit (c);
7720 }
7721 
7722 int sim_isgraph (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7723 {
7724 return (c & 0x80) ? 0 : isgraph (c);
7725 }
7726 
7727 int sim_isalnum (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7728 {
7729 return (c & 0x80) ? 0 : isalnum (c);
7730 }
7731 
7732 /* get_uint             unsigned number
7733 
7734    Inputs:
7735         cptr    =       pointer to input string
7736         radix   =       input radix
7737         max     =       maximum acceptable value
7738         *status =       pointer to error status
7739    Outputs:
7740         val     =       value
7741 */
7742 
7743 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
7744 {
7745 t_value val;
7746 CONST char *tptr;
7747 
7748 *status = SCPE_OK;
7749 val = strtotv ((CONST char *)cptr, &tptr, radix);
7750 if ((cptr == tptr) || (val > max))
7751     *status = SCPE_ARG;
7752 else {
7753     while (sim_isspace (*tptr)) tptr++;
7754     if (*tptr != 0)
7755         *status = SCPE_ARG;
7756     }
7757 return val;
7758 }
7759 
7760 /* get_range            range specification
7761 
7762    Inputs:
7763         dptr    =       pointer to device (NULL if none)
7764         cptr    =       pointer to input string
7765         *lo     =       pointer to low result
7766         *hi     =       pointer to high result
7767         aradix  =       radix
7768         max     =       default high value
7769         term    =       terminating character, 0 if none
7770    Outputs:
7771         tptr    =       input pointer after processing
7772                         NULL if error
7773 */
7774 
7775 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
     /* [previous][next][first][last][top][bottom][index][help] */
7776     uint32 rdx, t_addr max, char term)
7777 {
7778 CONST char *tptr;
7779 
7780 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
7781     tptr = cptr + strlen ("ALL");
7782     *lo = 0;
7783     *hi = max;
7784     }
7785 else {
7786     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
7787         ((cptr[1] == '\0') ||
7788          (cptr[1] == '-')  ||
7789          (cptr[1] == ':')  ||
7790          (cptr[1] == '/'))) {
7791         tptr = cptr + strlen (".");
7792         *lo = *hi = sim_last_addr;
7793         }
7794     else {
7795         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
7796             tptr = cptr + strlen ("$");
7797             *hi = *lo = (t_addr)sim_last_val;
7798             }
7799         else {
7800             if (dptr && sim_vm_parse_addr)                      /* get low */
7801                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7802             else
7803                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7804             if (cptr == tptr)                                   /* error? */
7805                     return NULL;
7806             }
7807         }
7808     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
7809         cptr = tptr + 1;
7810         if (dptr && sim_vm_parse_addr)                  /* get high */
7811             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7812         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7813         if (cptr == tptr)
7814             return NULL;
7815         if (*lo > *hi)
7816             return NULL;
7817         }
7818     else if (*tptr == '/') {                            /* relative? */
7819         cptr = tptr + 1;
7820         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7821         if ((cptr == tptr) || (*hi == 0))
7822             return NULL;
7823         *hi = *lo + *hi - 1;
7824         }
7825     else *hi = *lo;
7826     }
7827 sim_last_addr = *hi;
7828 if (term && (*tptr++ != term))
7829     return NULL;
7830 return tptr;
7831 }
7832 
7833 /* sim_decode_quoted_string
7834 
7835    Inputs:
7836         iptr        =   pointer to input string
7837         optr        =   pointer to output buffer
7838                         the output buffer must be allocated by the caller
7839                         and to avoid overrunat it must be at least as big
7840                         as the input string.
7841 
7842    Outputs
7843         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7844         osize       =   size of the data in the optr buffer
7845 
7846    The input string must be quoted.  Quotes may be either single or
7847    double but the opening anc closing quote characters must match.
7848    Within quotes C style character escapes are allowed.
7849 */
7850 
7851 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
     /* [previous][next][first][last][top][bottom][index][help] */
7852 {
7853 char quote_char;
7854 uint8 *ostart = optr;
7855 
7856 *osize = 0;
7857 if ((strlen(iptr) == 1) ||
7858     (iptr[0] != iptr[strlen(iptr)-1]) ||
7859     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7860     return SCPE_ARG;            /* String must be quote delimited */
7861 quote_char = *iptr++;           /* Save quote character */
7862 while (iptr[1]) {               /* Skip trailing quote */
7863     if (*iptr != '\\') {
7864         if (*iptr == quote_char)
7865             return SCPE_ARG;    /* Embedded quotes must be escaped */
7866         *(optr++) = (uint8)(*(iptr++));
7867         continue;
7868         }
7869     ++iptr; /* Skip backslash */
7870     switch (*iptr) {
7871         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7872             *(optr++) = 13; ++iptr;
7873             break;
7874         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7875             *(optr++) = 10; ++iptr;
7876             break;
7877         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7878             *(optr++) = 12; ++iptr;
7879             break;
7880         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7881             *(optr++) = 9; ++iptr;
7882             break;
7883         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7884             *(optr++) = 11; ++iptr;
7885             break;
7886         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7887             *(optr++) = 8; ++iptr;
7888             break;
7889         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7890             *(optr++) = 92; ++iptr;
7891             break;
7892         case 'e':   /* ASCII Escape character (Decimal value 27) */
7893             *(optr++) = 27; ++iptr;
7894             break;
7895         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7896             *(optr++) = 39; ++iptr;
7897             break;
7898         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7899             *(optr++) = 34; ++iptr;
7900             break;
7901         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7902             *(optr++) = 63; ++iptr;
7903             break;
7904         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7905             *optr = *(iptr++) - '0';
7906             if ((*iptr >= '0') && (*iptr <= '7'))
7907                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7908             if ((*iptr >= '0') && (*iptr <= '7'))
7909                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7910             ++optr;
7911             break;
7912         case 'x':
7913             if (1) {
7914                 static const char *hex_digits = "0123456789ABCDEF";
7915                 const char *c;
7916 
7917                 ++iptr;
7918                 *optr = 0;
7919                 c = strchr (hex_digits, toupper(*iptr));
7920                 if (c) {
7921                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7922                     ++iptr;
7923                     }
7924                 c = strchr (hex_digits, toupper(*iptr));
7925                 if (c) {
7926                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7927                     ++iptr;
7928                     }
7929                 ++optr;
7930                 }
7931             break;
7932         default:
7933             return SCPE_ARG;    /* Invalid escape */
7934         }
7935     }
7936 *optr = '\0';
7937 *osize = (uint32)(optr-ostart);
7938 return SCPE_OK;
7939 }
7940 
7941 /* sim_encode_quoted_string
7942 
7943    Inputs:
7944         iptr        =   pointer to input buffer
7945         size        =   number of bytes of data in the buffer
7946 
7947    Outputs
7948         optr        =   pointer to output buffer
7949                         the output buffer must be freed by the caller
7950 
7951    The input data will be encoded into a simply printable form.
7952 */
7953 
7954 char *sim_encode_quoted_string (const uint8 *iptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
7955 {
7956 size_t i;
7957 t_bool double_quote_found = FALSE;
7958 t_bool single_quote_found = FALSE;
7959 char quote = '"';
7960 char *tptr, *optr;
7961 
7962 optr = (char *)malloc (4*size + 3);
7963 if (optr == NULL)
7964     return NULL;
7965 tptr = optr;
7966 for (i=0; i<size; i++)
7967     switch ((char)iptr[i]) {
7968         case '"':
7969             double_quote_found = TRUE;
7970             break;
7971         case '\'':
7972             single_quote_found = TRUE;
7973             break;
7974         }
7975 if (double_quote_found && (!single_quote_found))
7976     quote = '\'';
7977 *tptr++ = quote;
7978 while (size--) {
7979     switch (*iptr) {
7980         case '\r':
7981             *tptr++ = '\\'; *tptr++ = 'r'; break;
7982         case '\n':
7983             *tptr++ = '\\'; *tptr++ = 'n'; break;
7984         case '\f':
7985             *tptr++ = '\\'; *tptr++ = 'f'; break;
7986         case '\t':
7987             *tptr++ = '\\'; *tptr++ = 't'; break;
7988         case '\v':
7989             *tptr++ = '\\'; *tptr++ = 'v'; break;
7990         case '\b':
7991             *tptr++ = '\\'; *tptr++ = 'b'; break;
7992         case '\\':
7993             *tptr++ = '\\'; *tptr++ = '\\'; break;
7994         case '"':
7995         case '\'':
7996             if (quote == *iptr)
7997                 *tptr++ = '\\';
7998         /*FALLTHRU*/ /* fall through */ /* fallthrough */
7999         default:
8000             if (sim_isprint (*iptr))
8001                 *tptr++ = *iptr;
8002             else {
8003                 (void)sprintf (tptr, "\\%03o", *iptr);
8004                 tptr += 4;
8005                 }
8006             break;
8007         }
8008     ++iptr;
8009     }
8010 *tptr++ = quote;
8011 *tptr++ = '\0';
8012 return optr;
8013 }
8014 
8015 void fprint_buffer_string (FILE *st, const uint8 *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
8016 {
8017 char *string;
8018 
8019 string = sim_encode_quoted_string (buf, size);
8020 (void)fprintf (st, "%s", string);
8021 FREE (string);
8022 }
8023 
8024 /* Find_device          find device matching input string
8025 
8026    Inputs:
8027         cptr    =       pointer to input string
8028    Outputs:
8029         result  =       pointer to device
8030 */
8031 
8032 DEVICE *find_dev (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8033 {
8034 int32 i;
8035 DEVICE *dptr;
8036 
8037 if (cptr == NULL)
8038     return NULL;
8039 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
8040     if ((strcmp (cptr, dptr->name) == 0) ||
8041         (dptr->lname &&
8042         (strcmp (cptr, dptr->lname) == 0)))
8043         return dptr;
8044     }
8045 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
8046     if ((strcmp (cptr, dptr->name) == 0) ||
8047         (dptr->lname &&
8048         (strcmp (cptr, dptr->lname) == 0)))
8049         return dptr;
8050     }
8051 return NULL;
8052 }
8053 
8054 /* Find_unit            find unit matching input string
8055 
8056    Inputs:
8057         cptr    =       pointer to input string
8058         uptr    =       pointer to unit pointer
8059    Outputs:
8060         result  =       pointer to device (null if no dev)
8061         *iptr   =       pointer to unit (null if nx unit)
8062 
8063 */
8064 
8065 DEVICE *find_unit (const char *cptr, UNIT **uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8066 {
8067 uint32 i, u;
8068 const char *nptr;
8069 const char *tptr;
8070 t_stat r;
8071 DEVICE *dptr;
8072 
8073 if (uptr == NULL)                                       /* arg error? */
8074     return NULL;
8075 *uptr = NULL;
8076 if ((dptr = find_dev (cptr))) {                         /* exact match? */
8077     if (qdisable (dptr))                                /* disabled? */
8078         return NULL;
8079     *uptr = dptr->units;                                /* unit 0 */
8080     return dptr;
8081     }
8082 
8083 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
8084     if (qdisable (dptr))                                /* device disabled? */
8085         continue;
8086     if (dptr->numunits && /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/ /* any units? */
8087         (((nptr = dptr->name) &&
8088           (strncmp (cptr, nptr, strlen (nptr)) == 0)) || /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8089          ((nptr = dptr->lname) &&
8090           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
8091         tptr = cptr + strlen (nptr);
8092         if (sim_isdigit (*tptr)) {
8093             if (qdisable (dptr))                        /* disabled? */
8094                 return NULL;
8095             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
8096             if (r != SCPE_OK)                           /* error? */
8097                 *uptr = NULL;
8098             else
8099                 *uptr = dptr->units + u;
8100             return dptr;
8101             }
8102         }
8103     for (u = 0; u < dptr->numunits; u++) {
8104         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
8105             *uptr = &dptr->units[u];
8106             return dptr;
8107             }
8108         }
8109     }
8110 return NULL;
8111 }
8112 
8113 /* sim_register_internal_device   Add device to internal device list
8114 
8115    Inputs:
8116         dptr    =       pointer to device
8117 */
8118 
8119 t_stat sim_register_internal_device (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8120 {
8121 uint32 i;
8122 
8123 for (i = 0; (sim_devices[i] != NULL); i++)
8124     if (sim_devices[i] == dptr)
8125         return SCPE_OK;
8126 for (i = 0; i < sim_internal_device_count; i++)
8127     if (sim_internal_devices[i] == dptr)
8128         return SCPE_OK;
8129 ++sim_internal_device_count;
8130 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
8131 if (!sim_internal_devices)
8132   {
8133     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8134                    __func__, __FILE__, __LINE__);
8135 #if defined(USE_BACKTRACE)
8136 # if defined(SIGUSR2)
8137     (void)raise(SIGUSR2);
8138     /*NOTREACHED*/ /* unreachable */
8139 # endif /* if defined(SIGUSR2) */
8140 #endif /* if defined(USE_BACKTRACE) */
8141     abort();
8142   }
8143 sim_internal_devices[sim_internal_device_count-1] = dptr;
8144 sim_internal_devices[sim_internal_device_count] = NULL;
8145 return SCPE_OK;
8146 }
8147 
8148 /* Find_dev_from_unit   find device for unit
8149 
8150    Inputs:
8151         uptr    =       pointer to unit
8152    Outputs:
8153         result  =       pointer to device
8154 */
8155 
8156 DEVICE *find_dev_from_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8157 {
8158 DEVICE *dptr;
8159 uint32 i, j;
8160 
8161 if (uptr == NULL)
8162     return NULL;
8163 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
8164     for (j = 0; j < dptr->numunits; j++) {
8165         if (uptr == (dptr->units + j))
8166             return dptr;
8167         }
8168     }
8169 for (i = 0; i<sim_internal_device_count; i++) {
8170     dptr = sim_internal_devices[i];
8171     for (j = 0; j < dptr->numunits; j++) {
8172         if (uptr == (dptr->units + j))
8173             return dptr;
8174         }
8175     }
8176 return NULL;
8177 }
8178 
8179 /* Test for disabled device */
8180 
8181 t_bool qdisable (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8182 {
8183 return (dptr->flags & DEV_DIS? TRUE: FALSE);
8184 }
8185 
8186 /* find_reg_glob        find globally unique register
8187 
8188    Inputs:
8189         cptr    =       pointer to input string
8190         optr    =       pointer to output pointer (can be null)
8191         gdptr   =       pointer to global device
8192    Outputs:
8193         result  =       pointer to register, NULL if error
8194         *optr   =       pointer to next character in input string
8195         *gdptr  =       pointer to device where found
8196 */
8197 
8198 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8199 {
8200 int32 i;
8201 DEVICE *dptr;
8202 REG *rptr, *srptr = NULL;
8203 
8204 *gdptr = NULL;
8205 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
8206     if (dptr->flags & DEV_DIS)                          /* skip disabled */
8207         continue;
8208     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
8209         if (srptr)                                      /* ambig? err */
8210             return NULL;
8211         srptr = rptr;                                   /* save reg */
8212         *gdptr = dptr;                                  /* save unit */
8213         }
8214     }
8215 return srptr;
8216 }
8217 
8218 /* find_reg             find register matching input string
8219 
8220    Inputs:
8221         cptr    =       pointer to input string
8222         optr    =       pointer to output pointer (can be null)
8223         dptr    =       pointer to device
8224    Outputs:
8225         result  =       pointer to register, NULL if error
8226         *optr   =       pointer to next character in input string
8227 */
8228 
8229 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8230 {
8231 CONST char *tptr;
8232 REG *rptr;
8233 size_t slnt;
8234 
8235 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
8236     return NULL;
8237 tptr = cptr;
8238 do {
8239     tptr++;
8240     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
8241 slnt = tptr - cptr;
8242 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
8243     if ((slnt == strlen (rptr->name)) &&
8244         (strncmp (cptr, rptr->name, slnt) == 0)) {
8245         if (optr != NULL)
8246             *optr = tptr;
8247         return rptr;
8248         }
8249     }
8250 return NULL;
8251 }
8252 
8253 /* get_switches         get switches from input string
8254 
8255    Inputs:
8256         cptr    =       pointer to input string
8257    Outputs:
8258         sw      =       switch bit mask
8259                         0 if no switches, -1 if error
8260 */
8261 
8262 int32 get_switches (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8263 {
8264 int32 sw;
8265 
8266 if (*cptr != '-')
8267     return 0;
8268 sw = 0;
8269 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
8270     if (sim_isalpha (*cptr) == 0)
8271         return -1;
8272     sw = sw | SWMASK (toupper (*cptr));
8273     }
8274 return sw;
8275 }
8276 
8277 /* get_sim_sw           accumulate sim_switches
8278 
8279    Inputs:
8280         cptr    =       pointer to input string
8281    Outputs:
8282         ptr     =       pointer to first non-string glyph
8283                         NULL if error
8284 */
8285 
8286 CONST char *get_sim_sw (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8287 {
8288 int32 lsw;
8289 char gbuf[CBUFSIZE];
8290 
8291 while (*cptr == '-') {                                  /* while switches */
8292     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
8293     lsw = get_switches (gbuf);                          /* parse */
8294     if (lsw <= 0)                                       /* invalid? */
8295         return NULL;
8296     sim_switches = sim_switches | lsw;                  /* accumulate */
8297     }
8298 return cptr;
8299 }
8300 
8301 /* get_sim_opt          get simulator command options
8302 
8303    Inputs:
8304         opt     =       command options
8305         cptr    =       pointer to input string
8306    Outputs:
8307         ptr     =       pointer to next glyph, NULL if error
8308         *stat   =       error status
8309 */
8310 
8311 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
     /* [previous][next][first][last][top][bottom][index][help] */
8312 {
8313 int32 t;
8314 char gbuf[CBUFSIZE];
8315 CONST char *svptr;
8316 DEVICE *tdptr;
8317 UNIT *tuptr;
8318 
8319 sim_switches = 0;                                       /* no switches */
8320 sim_ofile = NULL;                                       /* no output file */
8321 sim_schrptr = NULL;                                     /* no search */
8322 sim_schaptr = NULL;                                     /* no search */
8323 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
8324 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
8325 sim_stabr.count = 1;
8326 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
8327 if (!sim_stabr.mask)
8328   {
8329     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8330                    __func__, __FILE__, __LINE__);
8331 #if defined(USE_BACKTRACE)
8332 # if defined(SIGUSR2)
8333     (void)raise(SIGUSR2);
8334     /*NOTREACHED*/ /* unreachable */
8335 # endif /* if defined(SIGUSR2) */
8336 #endif /* if defined(USE_BACKTRACE) */
8337     abort();
8338   }
8339 (void)memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
8340 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
8341 if (!sim_stabr.comp)
8342   {
8343     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8344                    __func__, __FILE__, __LINE__);
8345 #if defined(USE_BACKTRACE)
8346 # if defined(SIGUSR2)
8347     (void)raise(SIGUSR2);
8348     /*NOTREACHED*/ /* unreachable */
8349 # endif /* if defined(SIGUSR2) */
8350 #endif /* if defined(USE_BACKTRACE) */
8351     abort();
8352   }
8353 (void)memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
8354 sim_staba.count = sim_emax;
8355 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
8356 if (!sim_staba.mask)
8357   {
8358     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8359                    __func__, __FILE__, __LINE__);
8360 #if defined(USE_BACKTRACE)
8361 # if defined(SIGUSR2)
8362     (void)raise(SIGUSR2);
8363     /*NOTREACHED*/ /* unreachable */
8364 # endif /* if defined(SIGUSR2) */
8365 #endif /* if defined(USE_BACKTRACE) */
8366     abort();
8367   }
8368 (void)memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
8369 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
8370 if (!sim_staba.comp)
8371   {
8372     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8373                    __func__, __FILE__, __LINE__);
8374 #if defined(USE_BACKTRACE)
8375 # if defined(SIGUSR2)
8376     (void)raise(SIGUSR2);
8377     /*NOTREACHED*/ /* unreachable */
8378 # endif /* if defined(SIGUSR2) */
8379 #endif /* if defined(USE_BACKTRACE) */
8380     abort();
8381   }
8382 (void)memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
8383 if (! sim_dflt_dev)
8384   return NULL;
8385 sim_dfdev = sim_dflt_dev;
8386 sim_dfunit = sim_dfdev->units;
8387 sim_opt_out = 0;                                        /* no options yet */
8388 *st = SCPE_OK;
8389 while (*cptr) {                                         /* loop through modifiers */
8390     svptr = cptr;                                       /* save current position */
8391     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
8392         if (sim_ofile) {                                /* already got one? */
8393             fclose (sim_ofile);                         /* one per customer */
8394             *st = SCPE_ARG;
8395             return NULL;
8396             }
8397         cptr = get_glyph (cptr + 1, gbuf, 0);
8398         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
8399         if (sim_ofile == NULL) {                        /* open failed? */
8400             *st = SCPE_OPENERR;
8401             return NULL;
8402             }
8403         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
8404         continue;
8405         }
8406     cptr = get_glyph (cptr, gbuf, 0);
8407     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
8408         if (t < 0) {                                    /* err if bad switch */
8409             *st = SCPE_INVSW;
8410             return NULL;
8411             }
8412         sim_switches = sim_switches | t;                /* or in new switches */
8413         }
8414     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
8415         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
8416         sim_schrptr = &sim_stabr;                       /* set search */
8417         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
8418         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
8419         }
8420     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
8421         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
8422         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
8423         (tuptr != NULL)) {
8424         sim_dfdev = tdptr;                              /* set as default */
8425         sim_dfunit = tuptr;
8426         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
8427         }
8428     else return svptr;                                  /* not rec, break out */
8429     }
8430 return cptr;
8431 }
8432 
8433 /* put_switches         put switches into string
8434 
8435    Inputs:
8436         buf     =       pointer to string buffer
8437         bufsize =       size of string buffer
8438         sw      =       switch bit mask
8439    Outputs:
8440         buf     =       buffer with switches converted to text
8441 */
8442 
8443 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8444 {
8445 char *optr = buf;
8446 int32 bit;
8447 
8448 (void)memset (buf, 0, bufsize);
8449 if ((sw == 0) || (bufsize < 3))
8450     return buf;
8451 --bufsize;                          /* leave room for terminating NUL */
8452 *optr++ = '-';
8453 for (bit=0; bit <= ('Z'-'A'); bit++)
8454     if (sw & (1 << bit))
8455         if ((size_t)(optr - buf) < bufsize)
8456             *optr++ = 'A' + bit;
8457 return buf;
8458 }
8459 
8460 /* Get register search specification
8461 
8462    Inputs:
8463         cptr    =       pointer to input string
8464         radix   =       radix for numbers
8465         schptr =        pointer to search table
8466    Outputs:
8467         return =        NULL if error
8468                         schptr if valid search specification
8469 */
8470 
8471 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8472 {
8473 int32 c;
8474 size_t logop, cmpop;
8475 t_value logval, cmpval;
8476 const char *sptr;
8477 CONST char *tptr;
8478 const char logstr[] = "|&^", cmpstr[] = "=!><";
8479 /* Using a const instead of a #define. */
8480 const size_t invalid_op = (size_t) -1;
8481 
8482 logval = cmpval = 0;
8483 if (*cptr == 0)                                         /* check for clause */
8484     return NULL;
8485 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8486 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8487     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
8488         logop = sptr - logstr;
8489         logval = strtotv (cptr, &tptr, radix);
8490         if (cptr == tptr)
8491             return NULL;
8492         cptr = tptr;
8493         }
8494     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
8495         cmpop = sptr - cmpstr;
8496         if (*cptr == '=') {
8497             cmpop = cmpop + strlen (cmpstr);
8498             cptr++;
8499             }
8500         cmpval = strtotv (cptr, &tptr, radix);
8501         if (cptr == tptr)
8502             return NULL;
8503         cptr = tptr;
8504         }
8505     else return NULL;
8506     }                                                   /* end for */
8507 if (schptr->count != 1) {
8508     FREE (schptr->mask);
8509     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8510     FREE (schptr->comp);
8511     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8512     }
8513 if (logop != invalid_op) {
8514     schptr->logic = logop;
8515     schptr->mask[0] = logval;
8516     }
8517 if (cmpop != invalid_op) {
8518     schptr->boolop = cmpop;
8519     schptr->comp[0] = cmpval;
8520     }
8521 schptr->count = 1;
8522 return schptr;
8523 }
8524 
8525 /* Get memory search specification
8526 
8527    Inputs:
8528         cptr    =       pointer to input string
8529         radix   =       radix for numbers
8530         schptr =        pointer to search table
8531    Outputs:
8532         return =        NULL if error
8533                         schptr if valid search specification
8534 */
8535 
8536 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8537 {
8538 int32 c;
8539 size_t logop, cmpop;
8540 t_value *logval, *cmpval;
8541 t_stat reason = SCPE_OK;
8542 CONST char *ocptr = cptr;
8543 const char *sptr;
8544 char gbuf[CBUFSIZE];
8545 const char logstr[] = "|&^", cmpstr[] = "=!><";
8546 /* Using a const instead of a #define. */
8547 const size_t invalid_op = (size_t) -1;
8548 
8549 if (*cptr == 0)                                         /* check for clause */
8550     return NULL;
8551 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
8552 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
8553 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8554 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8555     if (NULL != (sptr = strchr (logstr, c))) {          /* check for mask */
8556         logop = sptr - logstr;
8557         cptr = get_glyph (cptr, gbuf, 0);
8558         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
8559         if (reason > 0) {
8560             FREE (logval);
8561             FREE (cmpval);
8562             return get_rsearch (ocptr, radix, schptr);
8563             }
8564         }
8565     else if (NULL != (sptr = strchr (cmpstr, c))) {     /* check for boolop */
8566         cmpop = sptr - cmpstr;
8567         if (*cptr == '=') {
8568             cmpop = cmpop + strlen (cmpstr);
8569             cptr++;
8570             }
8571         cptr = get_glyph (cptr, gbuf, 0);
8572         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
8573         if (reason > 0) {
8574             FREE (logval);
8575             FREE (cmpval);
8576             return get_rsearch (ocptr, radix, schptr);
8577             }
8578         }
8579     else {
8580         FREE (logval);
8581         FREE (cmpval);
8582         return NULL;
8583         }
8584     }                                                   /* end for */
8585 if (schptr->count != (uint32)(1 - reason)) {
8586     schptr->count = 1 - reason;
8587     FREE (schptr->mask);
8588     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8589     FREE (schptr->comp);
8590     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8591     }
8592 if (logop != invalid_op) {
8593     schptr->logic = logop;
8594     FREE (schptr->mask);
8595     schptr->mask = logval;
8596     }
8597 else {
8598     FREE (logval);
8599     }
8600 if (cmpop != invalid_op) {
8601     schptr->boolop = cmpop;
8602     FREE (schptr->comp);
8603     schptr->comp = cmpval;
8604     }
8605 else {
8606     FREE (cmpval);
8607     }
8608 return schptr;
8609 }
8610 
8611 /* Test value against search specification
8612 
8613    Inputs:
8614         val    =        value list to test
8615         schptr =        pointer to search table
8616    Outputs:
8617         return =        1 if value passes search criteria, 0 if not
8618 */
8619 
8620 int32 test_search (t_value *values, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8621 {
8622 t_value *val = NULL;
8623 int32 i, updown;
8624 int32 ret = 0;
8625 
8626 if (schptr == NULL)
8627     return ret;
8628 
8629 val = (t_value *)malloc (schptr->count * sizeof (*values));
8630 if (!val)
8631   {
8632     (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8633                   __func__, __FILE__, __LINE__);
8634 #if defined(USE_BACKTRACE)
8635 # if defined(SIGUSR2)
8636     (void)raise(SIGUSR2);
8637     /*NOTREACHED*/ /* unreachable */
8638 # endif /* if defined(SIGUSR2) */
8639 #endif /* if defined(USE_BACKTRACE) */
8640     abort();
8641   }
8642 for (i=0; i<(int32)schptr->count; i++) {
8643     val[i] = values[i];
8644     switch (schptr->logic) {                            /* case on logical */
8645 
8646         case SCH_OR:
8647             val[i] = val[i] | schptr->mask[i];
8648             break;
8649 
8650         case SCH_AND:
8651             val[i] = val[i] & schptr->mask[i];
8652             break;
8653 
8654         case SCH_XOR:
8655             val[i] = val[i] ^ schptr->mask[i];
8656             break;
8657             }
8658     }
8659 
8660 ret = 1;
8661 if (1) {    /* Little Endian VM */
8662     updown = -1;
8663     i=schptr->count-1;
8664     }
8665 else {      /* Big Endian VM */
8666     updown = 1;
8667     i=0;
8668     }
8669 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
8670     switch (schptr->boolop) {                           /* case on comparison */
8671 
8672         case SCH_E: case SCH_EE:
8673             if (val[i] != schptr->comp[i])
8674                 ret = 0;
8675             break;
8676 
8677         case SCH_N: case SCH_NE:
8678             if (val[i] == schptr->comp[i])
8679                 ret = 0;
8680             break;
8681 
8682         case SCH_G:
8683             if (val[i] <= schptr->comp[i])
8684                 ret = 0;
8685             break;
8686 
8687         case SCH_GE:
8688             if (val[i] < schptr->comp[i])
8689                 ret = 0;
8690             break;
8691 
8692         case SCH_L:
8693             if (val[i] >= schptr->comp[i])
8694                 ret = 0;
8695             break;
8696 
8697         case SCH_LE:
8698             if (val[i] > schptr->comp[i])
8699                 ret = 0;
8700             break;
8701         }
8702     }
8703 FREE (val);
8704 return ret;
8705 }
8706 
8707 /* Radix independent input/output package
8708 
8709    strtotv - general radix input routine
8710 
8711    Inputs:
8712         inptr   =       string to convert
8713         endptr  =       pointer to first unconverted character
8714         radix   =       radix for input
8715    Outputs:
8716         value   =       converted value
8717 
8718    On an error, the endptr will equal the inptr.
8719 */
8720 
8721 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
     /* [previous][next][first][last][top][bottom][index][help] */
8722 {
8723 int32 nodigit;
8724 t_value val;
8725 uint32 c, digit;
8726 
8727 *endptr = inptr;                                        /* assume fails */
8728 if ((radix < 2) || (radix > 36))
8729     return 0;
8730 while (sim_isspace (*inptr))                            /* bypass white space */
8731     inptr++;
8732 val = 0;
8733 nodigit = 1;
8734 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
8735     if (sim_islower (c))
8736         c = toupper (c);
8737     if (sim_isdigit (c))                                /* digit? */
8738         digit = c - (uint32) '0';
8739     else if (radix <= 10)                               /* stop if not expected */
8740         break;
8741     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
8742     if (digit >= radix)                                 /* valid in radix? */
8743         return 0;
8744     val = (val * radix) + digit;                        /* add to value */
8745     nodigit = 0;
8746     }
8747 if (nodigit)                                            /* no digits? */
8748     return 0;
8749 *endptr = inptr;                                        /* result pointer */
8750 return val;
8751 }
8752 
8753 /* fprint_val - general radix printing routine
8754 
8755    Inputs:
8756         stream  =       stream designator
8757         val     =       value to print
8758         radix   =       radix to print
8759         width   =       width to print
8760         format  =       leading zeroes format
8761    Outputs:
8762         status  =       error status
8763         if stream is NULL, returns length of output that would
8764         have been generated.
8765 */
8766 
8767 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8768                    size_t width, uint32 format)
8769 {
8770 #define MAX_WIDTH ((CHAR_BIT * sizeof (t_value) * 4 + 3) / 3)
8771 t_value owtest, wtest;
8772 size_t d;
8773 size_t digit;
8774 size_t ndigits;
8775 size_t commas = 0;
8776 char dbuf[MAX_WIDTH + 1];
8777 
8778 for (d = 0; d < MAX_WIDTH; d++)
8779     dbuf[d] = (format == PV_RZRO)? '0': ' ';
8780 dbuf[MAX_WIDTH] = 0;
8781 d = MAX_WIDTH;
8782 do {
8783     d = d - 1;
8784     digit = val % radix;
8785     val = val / radix;
8786     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8787     } while ((d > 0) && (val != 0));
8788 
8789 switch (format) {
8790     case PV_LEFT:
8791         break;
8792     case PV_RZRO:
8793         wtest = owtest = radix;
8794         ndigits = 1;
8795         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8796             owtest = wtest;
8797             wtest = wtest * radix;
8798             ndigits = ndigits + 1;
8799             }
8800         if ((MAX_WIDTH - (ndigits + commas)) < d)
8801             d = MAX_WIDTH - (ndigits + commas);
8802         break;
8803     }
8804 if (NULL == buffer)                   /* Potentially unsafe wraparound if */
8805     return ((t_stat) strlen(dbuf+d)); /*  sizeof(t_stat) < sizeof(size_t) */
8806 *buffer = '\0';
8807 if (width < strlen(dbuf+d))
8808     return SCPE_IOERR;
8809 strcpy(buffer, dbuf+d);
8810 return SCPE_OK;
8811 }
8812 
8813 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8814     uint32 width, uint32 format)
8815 {
8816 char dbuf[MAX_WIDTH + 1];
8817 
8818 if (!stream)
8819     return sprint_val (NULL, val, radix, width, format);
8820 if (width > MAX_WIDTH)
8821     width = MAX_WIDTH;
8822 sprint_val (dbuf, val, radix, width, format);
8823 if (Fprintf (stream, "%s", dbuf) < 0)
8824     return SCPE_IOERR;
8825 return SCPE_OK;
8826 }
8827 
8828 const char *sim_fmt_secs (double seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
8829 {
8830 static char buf[60];
8831 char frac[16] = "";
8832 const char *sign = "";
8833 double val = seconds;
8834 double days, hours, mins, secs, msecs, usecs;
8835 
8836 if (val == 0.0)
8837     return "";
8838 if (val < 0.0) {
8839     sign = "-";
8840     val = -val;
8841     }
8842 days = floor (val / (24.0*60.0*60.0));
8843 val -= (days * 24.0*60.0*60.0);
8844 hours = floor (val / (60.0*60.0));
8845 val -= (hours * 60.0 * 60.0);
8846 mins = floor (val / 60.0);
8847 val -= (mins * 60.0);
8848 secs = floor (val);
8849 val -= secs;
8850 val *= 1000.0;
8851 msecs = floor (val);
8852 val -= msecs;
8853 val *= 1000.0;
8854 usecs = floor (val+0.5);
8855 if (usecs == 1000.0) {
8856     usecs = 0.0;
8857     msecs += 1;
8858     }
8859 if ((msecs > 0.0) || (usecs > 0.0)) {
8860     (void)sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8861     while (frac[strlen (frac) - 1] == '0') //-V557
8862         frac[strlen (frac) - 1] = '\0'; //-V557
8863     if (strlen (frac) == 1)
8864         frac[0] = '\0';
8865     }
8866 if (days > 0)
8867     (void)sprintf (buf, "%s%.0f day%s %02.0f:%02.0f:%02.0f%s hour%s",
8868                    sign, days, (days != 1) ? "s" : "", hours, mins,
8869                    secs, frac, (days == 1) ? "s" : "");
8870 else
8871     if (hours > 0)
8872         (void)sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour",
8873                        sign, hours, mins, secs, frac);
8874     else
8875         if (mins > 0)
8876             (void)sprintf (buf, "%s%.0f:%02.0f%s minute",
8877                            sign, mins, secs, frac);
8878         else
8879             if (secs > 0)
8880                 (void)sprintf (buf, "%s%.0f%s second",
8881                                sign, secs, frac);
8882             else
8883                 if (msecs > 0) {
8884                     if (usecs > 0)
8885                         (void)sprintf (buf, "%s%.0f.%s msec",
8886                                        sign, msecs, frac+4);
8887                     else
8888                         (void)sprintf (buf, "%s%.0f msec",
8889                                        sign, msecs);
8890                     }
8891                 else
8892                     (void)sprintf (buf, "%s%.0f usec",
8893                                    sign, usecs);
8894 if (0 != strncmp ("1 ", buf, 2))
8895     strcpy (&buf[strlen (buf)], "s");
8896 return buf;
8897 }
8898 
8899 /*
8900  * Event queue package
8901  *
8902  *      sim_activate            add entry to event queue
8903  *      sim_activate_abs        add entry to event queue even if event already scheduled
8904  *      sim_activate_after      add entry to event queue after a specified amount of wall time
8905  *      sim_cancel              remove entry from event queue
8906  *      sim_process_event       process entries on event queue
8907  *      sim_is_active           see if entry is on event queue
8908  *      sim_activate_time       return time until activation
8909  *      sim_atime               return absolute time for an entry
8910  *      sim_gtime               return global time
8911  *      sim_qcount              return event queue entry count
8912  */
8913 
8914 t_stat sim_process_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8915 {
8916 UNIT *uptr;
8917 t_stat reason;
8918 #if defined(TESTING)
8919 cpu_state_t * cpup = _cpup;
8920 #endif
8921 
8922 if (stop_cpu)                                           /* stop CPU? */
8923   {
8924 #if defined(WIN_STDIO)
8925     (void)fflush(stdout);
8926     (void)fflush(stderr);
8927 #endif /* if defined(WIN_STDIO) */
8928     return SCPE_STOP;
8929   }
8930 UPDATE_SIM_TIME;                                        /* update sim time */
8931 
8932 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8933     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8934     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8935                "Queue Empty New Interval = %d\r\n",
8936                sim_interval);
8937     return SCPE_OK;
8938     }
8939 sim_processing_event = TRUE;
8940 do {
8941     uptr = sim_clock_queue;                             /* get first */
8942     sim_clock_queue = uptr->next;                       /* remove first */
8943     uptr->next = NULL;                                  /* hygiene */
8944     sim_interval -= uptr->time;
8945     uptr->time = 0;
8946     if (sim_clock_queue != QUEUE_LIST_END)
8947         sim_interval = sim_clock_queue->time;
8948     else
8949         sim_interval = noqueue_time = NOQUEUE_WAIT;
8950     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8951                "Processing Event for %s\r\n", sim_uname (uptr));
8952     if (uptr->action != NULL)
8953         reason = uptr->action (uptr);
8954     else
8955         reason = SCPE_OK;
8956     } while ((reason == SCPE_OK) &&
8957              (sim_interval <= 0) &&
8958              (sim_clock_queue != QUEUE_LIST_END) &&
8959              (!stop_cpu));
8960 
8961 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8962     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8963     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8964                "Processing Queue Complete New Interval = %d\r\n",
8965                sim_interval);
8966     }
8967 else
8968     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8969                "Processing Queue Complete New Interval = %d(%s)\r\n",
8970                sim_interval, sim_uname(sim_clock_queue));
8971 
8972 if ((reason == SCPE_OK) && stop_cpu)
8973   {
8974 #if defined(WIN_STDIO)
8975     (void)fflush(stdout);
8976     (void)fflush(stderr);
8977 #endif /* if defined(WIN_STDIO) */
8978     reason = SCPE_STOP;
8979   }
8980 sim_processing_event = FALSE;
8981 return reason;
8982 }
8983 
8984 /* sim_activate - activate (queue) event
8985 
8986    Inputs:
8987         uptr    =       pointer to unit
8988         event_time =    relative timeout
8989    Outputs:
8990         reason  =       result (SCPE_OK if ok)
8991 */
8992 
8993 t_stat sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8994 {
8995 if (uptr->dynflags & UNIT_TMR_UNIT)
8996     return sim_timer_activate (uptr, event_time);
8997 return _sim_activate (uptr, event_time);
8998 }
8999 
9000 t_stat _sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
9001 {
9002 UNIT *cptr, *prvptr;
9003 int32 accum;
9004 #if defined(TESTING)
9005 cpu_state_t * cpup = _cpup;
9006 #endif
9007 
9008 if (sim_is_active (uptr))                               /* already active? */
9009     return SCPE_OK;
9010 UPDATE_SIM_TIME;                                        /* update sim time */
9011 
9012 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\r\n", sim_uname (uptr), event_time);
9013 
9014 prvptr = NULL;
9015 accum = 0;
9016 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
9017     if (event_time < (accum + cptr->time))
9018         break;
9019     accum = accum + cptr->time;
9020     prvptr = cptr;
9021     }
9022 if (prvptr == NULL) {                                   /* insert at head */
9023     cptr = uptr->next = sim_clock_queue;
9024     sim_clock_queue = uptr;
9025     }
9026 else {
9027     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
9028     prvptr->next = uptr;
9029     }
9030 uptr->time = event_time - accum;
9031 if (cptr != QUEUE_LIST_END)
9032     cptr->time = cptr->time - uptr->time;
9033 sim_interval = sim_clock_queue->time;
9034 return SCPE_OK;
9035 }
9036 
9037 /* sim_activate_abs - activate (queue) event even if event already scheduled
9038 
9039    Inputs:
9040         uptr    =       pointer to unit
9041         event_time =    relative timeout
9042    Outputs:
9043         reason  =       result (SCPE_OK if ok)
9044 */
9045 
9046 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
9047 {
9048 sim_cancel (uptr);
9049 return _sim_activate (uptr, event_time);
9050 }
9051 
9052 /* sim_activate_after - activate (queue) event
9053 
9054    Inputs:
9055         uptr    =       pointer to unit
9056         usec_delay =    relative timeout (in microseconds)
9057    Outputs:
9058         reason  =       result (SCPE_OK if ok)
9059 */
9060 
9061 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
9062 {
9063 return _sim_activate_after (uptr, usec_delay);
9064 }
9065 
9066 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
9067 {
9068 if (sim_is_active (uptr))                               /* already active? */
9069     return SCPE_OK;
9070 return sim_timer_activate_after (uptr, usec_delay);
9071 }
9072 
9073 /* sim_cancel - cancel (dequeue) event
9074 
9075    Inputs:
9076         uptr    =       pointer to unit
9077    Outputs:
9078         reason  =       result (SCPE_OK if ok)
9079 
9080 */
9081 
9082 t_stat sim_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9083 {
9084 UNIT *cptr, *nptr;
9085 #if defined(TESTING)
9086 cpu_state_t * cpup = _cpup;
9087 #endif
9088 
9089 if (sim_clock_queue == QUEUE_LIST_END)
9090     return SCPE_OK;
9091 if (!sim_is_active (uptr))
9092     return SCPE_OK;
9093 UPDATE_SIM_TIME;                                        /* update sim time */
9094 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\r\n", sim_uname(uptr));
9095 nptr = QUEUE_LIST_END;
9096 
9097 if (sim_clock_queue == uptr) {
9098     nptr = sim_clock_queue = uptr->next;
9099     uptr->next = NULL;                                  /* hygiene */
9100     }
9101 else {
9102     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
9103         if (cptr->next == uptr) {
9104             nptr = cptr->next = uptr->next;
9105             uptr->next = NULL;                          /* hygiene */
9106             break;                                      /* end queue scan */
9107             }
9108         }
9109     }
9110 if (nptr != QUEUE_LIST_END)
9111     nptr->time += (uptr->next) ? 0 : uptr->time;
9112 if (!uptr->next)
9113     uptr->time = 0;
9114 if (sim_clock_queue != QUEUE_LIST_END)
9115     sim_interval = sim_clock_queue->time;
9116 else sim_interval = noqueue_time = NOQUEUE_WAIT;
9117 if (uptr->next) {
9118     sim_printf ("\rCancel failed for '%s'!\r\n", sim_uname(uptr));
9119     if (sim_deb)
9120         fclose(sim_deb);
9121     (void)fprintf (stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
9122                    __func__, __FILE__, __LINE__);
9123     abort ();
9124     }
9125 return SCPE_OK;
9126 }
9127 
9128 /* sim_is_active - test for entry in queue
9129 
9130    Inputs:
9131         uptr    =       pointer to unit
9132    Outputs:
9133         result =        TRUE if unit is busy, FALSE inactive
9134 */
9135 
9136 t_bool sim_is_active (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9137 {
9138 if (uptr->next == NULL)
9139   return FALSE;
9140 else
9141 return TRUE;
9142 }
9143 
9144 /* sim_activate_time - return activation time
9145 
9146    Inputs:
9147         uptr    =       pointer to unit
9148    Outputs:
9149         result =        absolute activation time + 1, 0 if inactive
9150 */
9151 
9152 int32 sim_activate_time (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9153 {
9154 UNIT *cptr;
9155 int32 accum = 0;
9156 
9157 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
9158     if (cptr == sim_clock_queue) {
9159         if (sim_interval > 0)
9160             accum = accum + sim_interval;
9161         }
9162     else
9163         accum = accum + cptr->time;
9164     if (cptr == uptr)
9165         return accum + 1;
9166     }
9167 return 0;
9168 }
9169 
9170 /* sim_gtime - return global time
9171 
9172    Inputs: none
9173    Outputs:
9174         time    =       global time
9175 */
9176 
9177 double sim_gtime (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9178 {
9179 return sim_time;
9180 }
9181 
9182 /* sim_qcount - return queue entry count
9183 
9184    Inputs: none
9185    Outputs:
9186         count   =       number of entries on the queue
9187 */
9188 
9189 int32 sim_qcount (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9190 {
9191 int32 cnt;
9192 UNIT *uptr;
9193 
9194 cnt = 0;
9195 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
9196     cnt++;
9197 return cnt;
9198 }
9199 
9200 /* Breakpoint package.  This module replaces the VM-implemented one
9201    instruction breakpoint capability.
9202 
9203    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
9204    efficient binary searching.  A breakpoint consists of a six entry structure:
9205 
9206         addr                    address of the breakpoint
9207         type                    types of breakpoints set on the address
9208                                 a bit mask representing letters A-Z
9209         cnt                     number of iterations before breakp is taken
9210         action                  pointer command string to be executed
9211                                 when break is taken
9212         next                    list of other breakpoints with the same addr specifier
9213         time_fired              array of when this breakpoint was fired for each class
9214 
9215    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
9216    is the bitwise OR of all the type fields).  A simulator need only check for
9217    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
9218 
9219    The package contains the following public routines:
9220 
9221         sim_brk_init            initialize
9222         sim_brk_set             set breakpoint
9223         sim_brk_clr             clear breakpoint
9224         sim_brk_clrall          clear all breakpoints
9225         sim_brk_show            show breakpoint
9226         sim_brk_showall         show all breakpoints
9227         sim_brk_test            test for breakpoint
9228         sim_brk_npc             PC has been changed
9229         sim_brk_getact          get next action
9230         sim_brk_clract          clear pending actions
9231 
9232    Initialize breakpoint system.
9233 */
9234 
9235 t_stat sim_brk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9236 {
9237 int32 i;
9238 
9239 for (i=0; i<sim_brk_lnt; i++) {
9240     BRKTAB *bp;
9241     if (sim_brk_tab)
9242       {
9243         bp = sim_brk_tab[i];
9244 
9245         while (bp)
9246           {
9247             BRKTAB *bpt = bp->next;
9248 
9249             FREE (bp->act);
9250             FREE (bp);
9251             bp = bpt;
9252           }
9253       }
9254 }
9255 if (sim_brk_tab != NULL)
9256     (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9257 sim_brk_lnt = SIM_BRK_INILNT;
9258 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
9259 if (sim_brk_tab == NULL)
9260     return SCPE_MEM;
9261 (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9262 sim_brk_ent = sim_brk_ins = 0;
9263 sim_brk_clract ();
9264 sim_brk_npc (0);
9265 return SCPE_OK;
9266 }
9267 
9268 /* Search for a breakpoint in the sorted breakpoint table */
9269 
9270 BRKTAB *sim_brk_fnd (t_addr loc)
     /* [previous][next][first][last][top][bottom][index][help] */
9271 {
9272 int32 lo, hi, p;
9273 BRKTAB *bp;
9274 
9275 if (sim_brk_ent == 0) {                                 /* table empty? */
9276     sim_brk_ins = 0;                                    /* insrt at head */
9277     return NULL;                                        /* sch fails */
9278     }
9279 lo = 0;                                                 /* initial bounds */
9280 hi = sim_brk_ent - 1;
9281 do {
9282     p = (lo + hi) >> 1;                                 /* probe */
9283     bp = sim_brk_tab[p];                                /* table addr */
9284     if (loc == bp->addr) {                              /* match? */
9285         sim_brk_ins = p;
9286         return bp;
9287         }
9288     else if (loc < bp->addr)                            /* go down? p is upper */
9289         hi = p - 1;
9290     else lo = p + 1;                                    /* go up? p is lower */
9291     } while (lo <= hi);
9292 if (loc < bp->addr)                                     /* insrt before or */
9293     sim_brk_ins = p;
9294 else sim_brk_ins = p + 1;                               /* after last sch */
9295 return NULL;
9296 }
9297 
9298 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
     /* [previous][next][first][last][top][bottom][index][help] */
9299 {
9300 BRKTAB *bp = sim_brk_fnd (loc);
9301 
9302 while (bp) {
9303     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
9304                   (bp->typ == btyp))
9305         return bp;
9306     bp = bp->next;
9307     }
9308 return bp;
9309 }
9310 
9311 /* Insert a breakpoint */
9312 
9313 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9314 {
9315 int32 i, t;
9316 BRKTAB *bp, **newp;
9317 
9318 if (sim_brk_ins < 0)
9319     return NULL;
9320 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
9321     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
9322     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
9323     if (newp == NULL)                                   /* can't extend */
9324         return NULL;
9325     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
9326     (void)memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
9327     FREE (sim_brk_tab);                                 /* free old table */
9328     sim_brk_tab = newp;                                 /* new base, lnt */
9329     sim_brk_lnt = t;
9330     }
9331 if ((sim_brk_ins == sim_brk_ent) ||
9332     ((sim_brk_ins != sim_brk_ent) && //-V728
9333      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
9334     for (i = sim_brk_ent; i > sim_brk_ins; --i)
9335         sim_brk_tab[i] = sim_brk_tab[i - 1];
9336     sim_brk_tab[sim_brk_ins] = NULL;
9337     }
9338 bp = (BRKTAB *)calloc (1, sizeof (*bp));
9339 if (!bp)
9340   {
9341     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9342                    __func__, __FILE__, __LINE__);
9343 #if defined(USE_BACKTRACE)
9344 # if defined(SIGUSR2)
9345     (void)raise(SIGUSR2);
9346     /*NOTREACHED*/ /* unreachable */
9347 # endif /* if defined(SIGUSR2) */
9348 #endif /* if defined(USE_BACKTRACE) */
9349     abort();
9350   }
9351 bp->next = sim_brk_tab[sim_brk_ins];
9352 sim_brk_tab[sim_brk_ins] = bp;
9353 if (bp->next == NULL)
9354     sim_brk_ent += 1;
9355 bp->addr = loc;
9356 bp->typ = btyp;
9357 bp->cnt = 0;
9358 bp->act = NULL;
9359 for (i = 0; i < SIM_BKPT_N_SPC; i++)
9360     bp->time_fired[i] = -1.0;
9361 return bp;
9362 }
9363 
9364 /* Set a breakpoint of type sw */
9365 
9366 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
9367 {
9368 BRKTAB *bp;
9369 
9370 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
9371     sw |= sim_brk_dflt;
9372 if (~sim_brk_types & sw) {
9373     char gbuf[CBUFSIZE];
9374 
9375     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\r\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
9376     }
9377 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
9378     return SCPE_ARG;
9379 bp = sim_brk_fnd (loc);                                 /* loc present? */
9380 if (!bp)                                                /* no, allocate */
9381     bp = sim_brk_new (loc, sw);
9382 else {
9383     while (bp && (bp->typ != (uint32)sw))
9384         bp = bp->next;
9385     if (!bp)
9386         bp = sim_brk_new (loc, sw);
9387     }
9388 if (!bp)                                                /* still no? mem err */
9389     return SCPE_MEM;
9390 bp->cnt = ncnt;                                         /* set count */
9391 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
9392     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
9393     FREE (bp->act);                                     /* deallocate */
9394     bp->act = NULL;                                     /* now no action */
9395     }
9396 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9397     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
9398     if (newp == NULL)                                   /* mem err? */
9399         return SCPE_MEM;
9400     strncpy (newp, act, CBUFSIZE);                      /* copy action */
9401     bp->act = newp;                                     /* set pointer */
9402     }
9403 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
9404 return SCPE_OK;
9405 }
9406 
9407 /* Clear a breakpoint */
9408 
9409 t_stat sim_brk_clr (t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9410 {
9411 BRKTAB *bpl = NULL;
9412 BRKTAB *bp = sim_brk_fnd (loc);
9413 int32 i;
9414 
9415 if (!bp)                                                /* not there? ok */
9416     return SCPE_OK;
9417 if (sw == 0)
9418     sw = SIM_BRK_ALLTYP;
9419 
9420 #if !defined(__clang_analyzer__)
9421 while (bp) {
9422     if (bp->typ == (bp->typ & sw)) {
9423         FREE (bp->act);                                 /* deallocate action */
9424         if (bp == sim_brk_tab[sim_brk_ins])
9425             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
9426         else
9427           {
9428             if (bpl)
9429               bpl->next = bp->next;
9430           }
9431         FREE (bp);
9432         bp = bpl;
9433         }
9434     else {
9435         bpl = bp;
9436         bp = bp->next;
9437         }
9438     }
9439 #endif /* if !defined(__clang_analyzer__) */
9440 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
9441     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
9442     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
9443         sim_brk_tab[i] = sim_brk_tab[i+1];
9444     }
9445 sim_brk_summ = 0;                                       /* recalc summary */
9446 for (i = 0; i < sim_brk_ent; i++) {
9447     bp = sim_brk_tab[i];
9448     while (bp) {
9449         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
9450         bp = bp->next;
9451         }
9452     }
9453 return SCPE_OK;
9454 }
9455 
9456 /* Clear all breakpoints */
9457 
9458 t_stat sim_brk_clrall (int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9459 {
9460 int32 i;
9461 
9462 if (sw == 0)
9463     sw = SIM_BRK_ALLTYP;
9464 for (i = 0; i < sim_brk_ent;) {
9465     t_addr loc = sim_brk_tab[i]->addr;
9466     sim_brk_clr (loc, sw);
9467     if ((i < sim_brk_ent) &&
9468         (loc == sim_brk_tab[i]->addr)) //-V560
9469         ++i;
9470     }
9471 return SCPE_OK;
9472 }
9473 
9474 /* Show a breakpoint */
9475 
9476 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9477 {
9478 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
9479 DEVICE *dptr;
9480 uint32 i, any;
9481 
9482 if ((sw == 0) || (sw == SWMASK ('C'))) {
9483     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0); }
9484 if (!bp || (!(bp->typ & sw))) {
9485     return SCPE_OK; }
9486 dptr = sim_dflt_dev;
9487 if (dptr == NULL) {
9488     return SCPE_OK; }
9489 if (sw & SWMASK ('C')) {
9490     if (st != NULL) {
9491         (void)fprintf (st, "SET BREAK "); }
9492 } else {
9493     if (sim_vm_fprint_addr) {
9494         sim_vm_fprint_addr
9495             (st, dptr, loc);
9496     } else {
9497         fprint_val
9498             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9499     if (st != NULL) {
9500         (void)fprintf (st, ":\t"); }
9501     }
9502 for (i = any = 0; i < 26; i++) {
9503     if ((bp->typ >> i) & 1) {
9504         if ((sw & SWMASK ('C')) == 0) {
9505             if (any) {
9506                 if (st != NULL) {
9507                     (void)fprintf (st, ", "); } }
9508             if (st != NULL) {
9509                 fputc (i + 'A', st); }
9510             }
9511         } else {
9512             if (st != NULL) {
9513                 (void)fprintf (st, "-%c", i + 'A'); }
9514         any = 1;
9515         }
9516     }
9517 if (sw & SWMASK ('C')) {
9518     if (st != NULL) {
9519         (void)fprintf (st, " "); }
9520     if (sim_vm_fprint_addr) {
9521         if (st != NULL) {
9522             sim_vm_fprint_addr (st, dptr, loc); }
9523     } else {
9524         fprint_val
9525             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9526     }
9527 if (bp->cnt > 0) {
9528     if (st != NULL) {
9529         (void)fprintf (st, "[%d]", bp->cnt); } }
9530 if (bp->act != NULL) {
9531     if (st != NULL) {
9532         (void)fprintf (st, "; %s", bp->act); } }
9533 (void)fprintf (st, "\r\n");
9534 return SCPE_OK;
9535 }
9536 
9537 /* Show all breakpoints */
9538 
9539 t_stat sim_brk_showall (FILE *st, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9540 {
9541 int32 bit, mask, types;
9542 BRKTAB **bpt;
9543 
9544 if ((sw == 0) || (sw == SWMASK ('C')))
9545     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
9546 for (types=bit=0; bit <= ('Z'-'A'); bit++)
9547     if (sim_brk_types & (1 << bit))
9548         ++types;
9549 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
9550     (void)fprintf (st, "Supported Breakpoint Types:"); //-V522
9551     for (bit=0; bit <= ('Z'-'A'); bit++)
9552         if (sim_brk_types & (1 << bit))
9553             (void)fprintf (st, " -%c", 'A' + bit);
9554     (void)fprintf (st, "\r\n");
9555     }
9556 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
9557     mask = (sw & sim_brk_types);
9558     (void)fprintf (st, "Displaying Breakpoint Types:");
9559     for (bit=0; bit <= ('Z'-'A'); bit++)
9560         if (mask & (1 << bit))
9561             (void)fprintf (st, " -%c", 'A' + bit);
9562     (void)fprintf (st, "\r\n");
9563     }
9564 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9565     BRKTAB *prev = NULL;
9566     BRKTAB *cur = *bpt;
9567     BRKTAB *next;
9568     /* First reverse the list */
9569     while (cur) {
9570         next = cur->next;
9571         cur->next = prev;
9572         prev = cur;
9573         cur = next;
9574         }
9575     /* save reversed list in the head pointer so lookups work */
9576     *bpt = prev;
9577     /* Walk the reversed list and print it in the order it was defined in */
9578     cur = prev;
9579     while (cur) {
9580         if (cur->typ & sw)
9581             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
9582         cur = cur->next;
9583         }
9584     /* reversing the list again */
9585     cur = prev;
9586     prev = NULL;
9587     while (cur) {
9588         next = cur->next;
9589         cur->next = prev;
9590         prev = cur;
9591         cur = next;
9592         }
9593     /* restore original list */
9594     *bpt = prev;
9595     }
9596 return SCPE_OK;
9597 }
9598 
9599 /* Test for breakpoint */
9600 
9601 uint32 sim_brk_test (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9602 {
9603 BRKTAB *bp;
9604 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
9605 
9606 if (sim_brk_summ & BRK_TYP_DYN_ALL)
9607     btyp |= BRK_TYP_DYN_ALL;
9608 
9609 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
9610     double s_gtime = sim_gtime ();                      /* get time now */
9611 
9612     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
9613         return 0;
9614     bp->time_fired[spc] = s_gtime;                      /* remember match time */
9615     if (--bp->cnt > 0)                                  /* count > 0? */
9616         return 0;
9617     bp->cnt = 0;                                        /* reset count */
9618     sim_brk_setact (bp->act);                           /* set up actions */
9619     sim_brk_match_type = btyp & bp->typ;                /* set return value */
9620     if (bp->typ & BRK_TYP_TEMP)
9621         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
9622     sim_brk_match_addr = loc;
9623     return sim_brk_match_type;
9624     }
9625 return 0;
9626 }
9627 
9628 /* Get next pending action, if any */
9629 
9630 CONST char *sim_brk_getact (char *buf, int32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
9631 {
9632 char *ep;
9633 size_t lnt;
9634 
9635 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
9636     return NULL;
9637 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
9638     sim_brk_act[sim_do_depth]++;
9639 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
9640     return sim_brk_clract ();
9641     }
9642 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
9643     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
9644     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
9645     buf[lnt] = 0;                                       /* erase ; */
9646     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
9647     }
9648 else {
9649     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
9650     sim_brk_clract ();                                  /* no more */
9651     }
9652 return buf;
9653 }
9654 
9655 /* Clear pending actions */
9656 
9657 char *sim_brk_clract (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9658 {
9659 FREE (sim_brk_act_buf[sim_do_depth]);
9660 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
9661 }
9662 
9663 /* Set up pending actions */
9664 
9665 void sim_brk_setact (const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
9666 {
9667 if (action) {
9668     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
9669     if (!sim_brk_act_buf[sim_do_depth])
9670       {
9671         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9672                        __func__, __FILE__, __LINE__);
9673 #if defined(USE_BACKTRACE)
9674 # if defined(SIGUSR2)
9675         (void)raise(SIGUSR2);
9676         /*NOTREACHED*/ /* unreachable */
9677 # endif /* if defined(SIGUSR2) */
9678 #endif /* if defined(USE_BACKTRACE) */
9679         abort();
9680       }
9681     strcpy (sim_brk_act_buf[sim_do_depth], action);
9682     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
9683     }
9684 else
9685     sim_brk_clract ();
9686 }
9687 
9688 /* New PC */
9689 
9690 void sim_brk_npc (uint32 cnt)
     /* [previous][next][first][last][top][bottom][index][help] */
9691 {
9692 uint32 spc;
9693 BRKTAB **bpt, *bp;
9694 
9695 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
9696     cnt = SIM_BKPT_N_SPC;
9697 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9698     for (bp = *bpt; bp; bp = bp->next) {
9699         for (spc = 0; spc < cnt; spc++)
9700             bp->time_fired[spc] = -1.0;
9701         }
9702     }
9703 }
9704 
9705 /* Clear breakpoint space */
9706 
9707 void sim_brk_clrspc (uint32 spc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9708 {
9709 BRKTAB **bpt, *bp;
9710 
9711 if (spc < SIM_BKPT_N_SPC) {
9712     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9713         for (bp = *bpt; bp; bp = bp->next) {
9714             if (bp->typ & btyp)
9715                 bp->time_fired[spc] = -1.0;
9716             }
9717         }
9718     }
9719 }
9720 
9721 const char *sim_brk_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
9722 {
9723 static char msg[256];
9724 char addr[65] = "";
9725 char buf[32];
9726 
9727 msg[0] = '\0';
9728 if (sim_dflt_dev) {
9729   if (sim_vm_sprint_addr)
9730     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
9731   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
9732 }
9733 if (sim_brk_type_desc) {
9734     BRKTYPTAB *brk = sim_brk_type_desc;
9735 
9736     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
9737         if (brk->btyp == sim_brk_match_type) {
9738             (void)sprintf (msg, "%s: %s", brk->desc, addr);
9739             break;
9740             }
9741         brk++;
9742         }
9743     }
9744 if (!msg[0])
9745     (void)sprintf (msg, "%s Breakpoint at: %s\r\n",
9746                    put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9747 
9748 return msg;
9749 }
9750 
9751 /* Expect package.  This code provides a mechanism to stop and control simulator
9752    execution based on traffic coming out of simulated ports and as well as a means
9753    to inject data into those ports.  It can conceptually viewed as a string
9754    breakpoint package.
9755 
9756    Expect rules are stored in tables associated with each port which can use this
9757    facility.  An expect rule consists of a five entry structure:
9758 
9759         match                   the expect match string
9760         size                    the number of bytes in the match string
9761         match_pattern           the expect match string in display format
9762         cnt                     number of iterations before match is declared
9763         action                  command string to be executed when match occurs
9764 
9765    All active expect rules are contained in an expect match context structure.
9766 
9767         rules                   the match rules
9768         size                    the count of match rules
9769         buf                     the buffer of output data which has been produced
9770         buf_ins                 the buffer insertion point for the next output data
9771         buf_size                the buffer size
9772 
9773    The package contains the following public routines:
9774 
9775         sim_set_expect          expect command parser and initializer
9776         sim_set_noexpect        noexpect command parser
9777         sim_exp_set             set or add an expect rule
9778         sim_exp_clr             clear or delete an expect rule
9779         sim_exp_clrall          clear all expect rules
9780         sim_exp_show            show an expect rule
9781         sim_exp_showall         show all expect rules
9782         sim_exp_check           test for rule match
9783 */
9784 
9785 /* Set expect */
9786 
9787 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9788 {
9789 char gbuf[CBUFSIZE];
9790 CONST char *tptr;
9791 CONST char *c1ptr;
9792 t_bool after_set = FALSE;
9793 uint32 after;
9794 int32 cnt = 0;
9795 t_stat r;
9796 
9797 if (exp == NULL)
9798     return sim_messagef (SCPE_ARG, "Null exp!\r\n");
9799 after = exp->after;
9800 
9801 if ((cptr == NULL) || (*cptr == 0))
9802     return SCPE_2FARG;
9803 if (*cptr == '[') {
9804     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9805     if ((cptr == c1ptr) || (*c1ptr != ']'))
9806         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\r\n");
9807     cptr = c1ptr + 1;
9808     while (sim_isspace(*cptr))
9809         ++cptr;
9810     }
9811 tptr = get_glyph (cptr, gbuf, ',');
9812 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9813     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9814     if (r != SCPE_OK)
9815         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\r\n");
9816     after_set = TRUE;
9817     cptr = tptr;
9818     }
9819 if ((*cptr != '"') && (*cptr != '\''))
9820     return sim_messagef (SCPE_ARG, "String must be quote delimited\r\n");
9821 cptr = get_glyph_quoted (cptr, gbuf, 0);
9822 
9823 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9824 }
9825 
9826 /* Clear expect */
9827 
9828 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9829 {
9830 char gbuf[CBUFSIZE];
9831 
9832 if (NULL == cptr || !*cptr)
9833     return sim_exp_clrall (exp);                    /* clear all rules */
9834 if ((*cptr != '"') && (*cptr != '\''))
9835     return sim_messagef (SCPE_ARG, "String must be quote delimited\r\n");
9836 cptr = get_glyph_quoted (cptr, gbuf, 0);
9837 if (*cptr != '\0')
9838     return SCPE_2MARG;                              /* No more arguments */
9839 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9840 }
9841 
9842 /* Search for an expect rule in an expect context */
9843 
9844 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, size_t start_rule)
     /* [previous][next][first][last][top][bottom][index][help] */
9845 {
9846 size_t i;
9847 
9848 if (NULL == exp->rules)
9849     return NULL;
9850 for (i=start_rule; i<exp->size; i++)
9851     if (!strcmp (exp->rules[i].match_pattern, match))
9852         return &exp->rules[i];
9853 return NULL;
9854 }
9855 
9856 /* Clear (delete) an expect rule */
9857 
9858 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9859 {
9860 size_t i;
9861 
9862 if (NULL == ep)                                         /* not there? ok */
9863     return SCPE_OK;
9864 FREE (ep->match);                                       /* deallocate match string */
9865 FREE (ep->match_pattern);                               /* deallocate the display format match string */
9866 FREE (ep->act);                                         /* deallocate action */
9867 exp->size -= 1;                                         /* decrement count */
9868 #if !defined(__clang_analyzer__)
9869 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9870     exp->rules[i] = exp->rules[i+1];
9871 if (exp->size == 0) {                                   /* No rules left? */
9872     FREE (exp->rules);
9873     exp->rules = NULL;
9874     }
9875 #endif /* if !defined(__clang_analyzer__) */
9876 return SCPE_OK;
9877 }
9878 
9879 t_stat sim_exp_clr (EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9880 {
9881 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9882 
9883 while (ep) {
9884     sim_exp_clr_tab (exp, ep);
9885     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9886     }
9887 return SCPE_OK;
9888 }
9889 
9890 /* Clear all expect rules */
9891 
9892 t_stat sim_exp_clrall (EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9893 {
9894 int32 i;
9895 
9896 for (i=0; i<exp->size; i++) {
9897     FREE (exp->rules[i].match);                         /* deallocate match string */
9898     FREE (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9899     FREE (exp->rules[i].act);                           /* deallocate action */
9900     }
9901 FREE (exp->rules);
9902 exp->rules = NULL;
9903 exp->size = 0;
9904 FREE (exp->buf);
9905 exp->buf = NULL;
9906 exp->buf_size = 0;
9907 exp->buf_ins = 0;
9908 return SCPE_OK;
9909 }
9910 
9911 /* Set/Add an expect rule */
9912 
9913 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] */
9914 {
9915 EXPTAB *ep;
9916 uint8 *match_buf;
9917 uint32 match_size;
9918 size_t i;
9919 
9920 /* Validate the match string */
9921 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9922 if (!match_buf)
9923     return SCPE_MEM;
9924 if (switches & EXP_TYP_REGEX) {
9925     FREE (match_buf);
9926     return sim_messagef (SCPE_ARG, "RegEx support not available\r\n");
9927     }
9928 else {
9929     if (switches & EXP_TYP_REGEX_I) {
9930         FREE (match_buf);
9931         return sim_messagef (SCPE_ARG, "Case independent matching is only valid for RegEx expect rules\r\n");
9932         }
9933     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9934     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9935         FREE (match_buf);
9936         return sim_messagef (SCPE_ARG, "Invalid quoted string\r\n");
9937         }
9938     }
9939 FREE (match_buf);
9940 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9941     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9942         (exp->rules[i].switches & EXP_TYP_PERSIST))
9943         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\r\n");
9944     }
9945 if (after && exp->size)
9946     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\r\n");
9947 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9948 if (!exp->rules)
9949   {
9950     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9951                    __func__, __FILE__, __LINE__);
9952 #if defined(USE_BACKTRACE)
9953 # if defined(SIGUSR2)
9954     (void)raise(SIGUSR2);
9955     /*NOTREACHED*/ /* unreachable */
9956 # endif /* if defined(SIGUSR2) */
9957 #endif /* if defined(USE_BACKTRACE) */
9958     abort();
9959   }
9960 ep = &exp->rules[exp->size];
9961 exp->size += 1;
9962 exp->after = after;                                     /* set halt after value */
9963 (void)memset (ep, 0, sizeof(*ep));
9964 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9965 if (ep->match_pattern)
9966     strcpy (ep->match_pattern, match);
9967 ep->cnt = cnt;                                          /* set proceed count */
9968 ep->switches = switches;                                /* set switches */
9969 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9970 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9971     sim_exp_clr_tab (exp, ep);                          /* clear it */
9972     FREE (match_buf);                                   /* release allocation */
9973     return SCPE_MEM;
9974     }
9975 if (switches & EXP_TYP_REGEX) {
9976     FREE (match_buf);
9977     match_buf = NULL;
9978     }
9979 else {
9980     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9981     sim_decode_quoted_string (match, match_buf, &match_size);
9982     ep->match = match_buf;
9983     ep->size = match_size;
9984     }
9985 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9986 if (!ep->match_pattern)
9987   {
9988     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9989                    __func__, __FILE__, __LINE__);
9990 #if defined(USE_BACKTRACE)
9991 # if defined(SIGUSR2)
9992     (void)raise(SIGUSR2);
9993     /*NOTREACHED*/ /* unreachable */
9994 # endif /* if defined(SIGUSR2) */
9995 #endif /* if defined(USE_BACKTRACE) */
9996     abort();
9997   }
9998 strcpy (ep->match_pattern, match);
9999 if (ep->act) {                                          /* replace old action? */
10000     FREE (ep->act);                                     /* deallocate */
10001     ep->act = NULL;                                     /* now no action */
10002     }
10003 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
10004 if ((act != NULL) && (*act != 0)) {                     /* new action? */
10005     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
10006     if (newp == NULL)                                   /* mem err? */
10007         return SCPE_MEM;
10008     strcpy (newp, act);                                 /* copy action */
10009     ep->act = newp;                                     /* set pointer */
10010     }
10011 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
10012 for (i=0; i<exp->size; i++) {
10013     size_t compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
10014     if (compare_size >= exp->buf_size) {
10015         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
10016         exp->buf_size = compare_size + 1;
10017         }
10018     }
10019 return SCPE_OK;
10020 }
10021 
10022 /* Show an expect rule */
10023 
10024 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
10025 {
10026 if (!ep)
10027     return SCPE_OK;
10028 (void)fprintf (st, "EXPECT");
10029 if (ep->switches & EXP_TYP_PERSIST)
10030     (void)fprintf (st, " -p");
10031 if (ep->switches & EXP_TYP_CLEARALL)
10032     (void)fprintf (st, " -c");
10033 if (ep->switches & EXP_TYP_REGEX)
10034     (void)fprintf (st, " -r");
10035 if (ep->switches & EXP_TYP_REGEX_I)
10036     (void)fprintf (st, " -i");
10037 (void)fprintf (st, " %s", ep->match_pattern);
10038 if (ep->cnt > 0)
10039     (void)fprintf (st, " [%d]", ep->cnt);
10040 if (ep->act)
10041     (void)fprintf (st, " %s", ep->act);
10042 (void)fprintf (st, "\r\n");
10043 return SCPE_OK;
10044 }
10045 
10046 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
10047 {
10048 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
10049 
10050 if (exp->buf_size) {
10051     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
10052 
10053     (void)fprintf (st, "Match Buffer Size: %lld\r\n",
10054                    (long long)exp->buf_size);
10055     (void)fprintf (st, "Buffer Insert Offset: %lld\r\n",
10056                    (long long)exp->buf_ins);
10057     (void)fprintf (st, "Buffer Contents: %s\r\n",
10058                    bstr);
10059     FREE (bstr);
10060     }
10061 if (exp->after)
10062     (void)fprintf (st, "Halt After: %lld instructions\r\n",
10063                    (long long)exp->after);
10064 if (exp->dptr && exp->dbit)
10065     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\r\n",
10066                    sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "",
10067                    exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
10068 (void)fprintf (st, "Match Rules:\r\n");
10069 if (!*match)
10070     return sim_exp_showall (st, exp);
10071 if (!ep) {
10072     (void)fprintf (st, "No Rules match '%s'\r\n", match);
10073     return SCPE_ARG;
10074     }
10075 do {
10076     sim_exp_show_tab (st, exp, ep);
10077     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
10078     } while (ep);
10079 return SCPE_OK;
10080 }
10081 
10082 /* Show all expect rules */
10083 
10084 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
10085 {
10086 size_t i;
10087 
10088 for (i=0; i < exp->size; i++)
10089     sim_exp_show_tab (st, exp, &exp->rules[i]);
10090 return SCPE_OK;
10091 }
10092 
10093 /* Test for expect match */
10094 
10095 t_stat sim_exp_check (EXPECT *exp, uint8 data)
     /* [previous][next][first][last][top][bottom][index][help] */
10096 {
10097 size_t i;
10098 EXPTAB *ep = NULL;
10099 char *tstr = NULL;
10100 #if defined(TESTING)
10101 cpu_state_t * cpup = _cpup;
10102 #endif
10103 
10104 if ((!exp) || (!exp->rules))                            /* Anything to check? */
10105     return SCPE_OK;
10106 
10107 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
10108 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
10109 
10110 for (i=0; i < exp->size; i++) {
10111     ep = &exp->rules[i];
10112     if (ep == NULL) //-V547
10113         break;
10114     if (ep->switches & EXP_TYP_REGEX) {
10115         }
10116     else {
10117         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
10118             /*
10119              * First compare the newly deposited data at the beginning
10120              * of buffer with the end of the match string
10121              */
10122             if (exp->buf_ins > 0) {
10123                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
10124                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
10125                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
10126 
10127                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%lld]: %s\r\n",
10128                                (long long)exp->buf_ins, estr);
10129                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\r\n", mstr);
10130                     FREE (estr);
10131                     FREE (mstr);
10132                     }
10133                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
10134                     continue;
10135                 }
10136             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
10137                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
10138                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
10139 
10140                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\r\n",
10141                            (long long)exp->buf_size-(ep->size-exp->buf_ins),
10142                            (long long)ep->size-exp->buf_ins, estr);
10143                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\r\n", mstr);
10144                 FREE (estr);
10145                 FREE (mstr);
10146                 }
10147             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
10148                 continue;
10149             break;
10150             }
10151         else {
10152             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
10153                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
10154                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
10155 
10156                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\r\n",
10157                            (long long)exp->buf_ins-ep->size,
10158                            (long long)ep->size, estr);
10159                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\r\n", mstr);
10160                 FREE (estr);
10161                 FREE (mstr);
10162                 }
10163             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
10164                 continue;
10165             break;
10166             }
10167         }
10168     }
10169 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
10170         exp->buf_ins = 0;                               /* wrap around to beginning */
10171         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\r\n");
10172     }
10173 if ((ep != NULL) && (i != exp->size)) {                 /* Found? */
10174     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern!\r\n");
10175     if (ep->cnt > 0) {
10176         ep->cnt -= 1;
10177         sim_debug (exp->dbit, exp->dptr, "Waiting for %lld more match%s before stopping\r\n",
10178                    (long long)ep->cnt, (ep->cnt == 1) ? "" : "es");
10179         }
10180     else {
10181         uint32 after   = exp->after;
10182         int32 switches = ep->switches;
10183         if (ep->act && *ep->act) {
10184             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\r\n", ep->act);
10185             }
10186         else {
10187             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\r\n");
10188             }
10189         sim_brk_setact (ep->act);                       /* set up actions */
10190         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
10191             sim_exp_clrall (exp);                       /* delete all rules */
10192         else {
10193             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
10194                 sim_exp_clr_tab (exp, ep);              /* delete it */
10195             }
10196         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
10197                       (switches & EXP_TYP_TIME) ?
10198                             (uint32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
10199                              after);
10200         }
10201     /* Matched data is no longer available for future matching */
10202     exp->buf_ins = 0;
10203     }
10204 if (tstr)  //-V547
10205   FREE (tstr);
10206 return SCPE_OK;
10207 }
10208 
10209 /* Queue input data for sending */
10210 
10211 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
     /* [previous][next][first][last][top][bottom][index][help] */
10212 {
10213 if (snd->extoff != 0) {
10214     if (snd->insoff > snd->extoff)
10215         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10216     snd->insoff -= snd->extoff;
10217     snd->extoff  = 0;
10218     }
10219 if (snd->insoff+size > snd->bufsize) {
10220     snd->bufsize = snd->insoff+size;
10221     snd->buffer  = (uint8 *)realloc(snd->buffer, snd->bufsize);
10222     if (!snd->buffer)
10223       {
10224         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10225                        __func__, __FILE__, __LINE__);
10226 #if defined(USE_BACKTRACE)
10227 # if defined(SIGUSR2)
10228         (void)raise(SIGUSR2);
10229         /*NOTREACHED*/ /* unreachable */
10230 # endif /* if defined(SIGUSR2) */
10231 #endif /* if defined(USE_BACKTRACE) */
10232         abort();
10233       }
10234     }
10235 memcpy(snd->buffer+snd->insoff, data, size);
10236 snd->insoff += size;
10237 if (delay)
10238     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
10239 if (after)
10240     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
10241 if (snd->after == 0)
10242     snd->after = snd->delay;
10243 snd->next_time = sim_gtime() + snd->after;
10244 return SCPE_OK;
10245 }
10246 
10247 /* Cancel Queued input data */
10248 t_stat sim_send_clear (SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10249 {
10250 snd->insoff = 0;
10251 snd->extoff = 0;
10252 return SCPE_OK;
10253 }
10254 
10255 /* Display console Queued input data status */
10256 
10257 t_stat sim_show_send_input (FILE *st, const SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10258 {
10259 if (snd->extoff < snd->insoff) {
10260     (void)fprintf (st, "%lld bytes of pending input Data:\r\n    ",
10261                    (long long)snd->insoff-snd->extoff);
10262     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10263     (void)fprintf (st, "\r\n");
10264     }
10265 else
10266     (void)fprintf (st, "No Pending Input Data\r\n");
10267 if ((snd->next_time - sim_gtime()) > 0) {
10268     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
10269         (void)fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\r\n",
10270                        (int)(snd->next_time - sim_gtime()),
10271         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
10272     else
10273         (void)fprintf (st, "Minimum of %d instructions before sending first character\r\n",
10274                        (int)(snd->next_time - sim_gtime()));
10275     }
10276 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
10277     (void)fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\r\n",
10278                    (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
10279 else
10280     (void)fprintf (st, "Minimum of %d instructions between characters\r\n",
10281                    (int)snd->delay);
10282 if (snd->dptr && snd->dbit)
10283     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\r\n",
10284                    sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "",
10285                    snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
10286 return SCPE_OK;
10287 }
10288 
10289 /* Poll for Queued input data */
10290 
10291 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10292 {
10293 #if defined(TESTING)
10294 cpu_state_t * cpup = _cpup;
10295 #endif
10296 if ((NULL != snd) && (snd->extoff < snd->insoff)) {     /* pending input characters available? */
10297     if (sim_gtime() < snd->next_time) {                 /* too soon? */
10298         *stat = SCPE_OK;
10299         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\r\n");
10300         }
10301     else {
10302         char dstr[8] = "";
10303         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
10304         snd->next_time = sim_gtime() + snd->delay;
10305         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
10306             (void)sprintf (dstr, " '%c'", *stat & 0xFF);
10307         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\r\n", *stat & 0xFF, dstr);
10308         }
10309     return TRUE;
10310     }
10311 return FALSE;
10312 }
10313 
10314 /* Message Text */
10315 
10316 const char *sim_error_text (t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10317 {
10318 static char msgbuf[64];
10319 
10320 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
10321 if (stat == SCPE_OK)
10322     return "No Error";
10323 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
10324     return scp_errors[stat-SCPE_BASE].message;
10325 (void)sprintf(msgbuf, "Error %d", stat);
10326 return msgbuf;
10327 }
10328 
10329 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10330 {
10331 char gbuf[CBUFSIZE];
10332 size_t cond;
10333 
10334 *stat = SCPE_ARG;
10335 cptr = get_glyph (cptr, gbuf, 0);
10336 if (0 == memcmp("SCPE_", gbuf, 5))
10337     memmove (gbuf, gbuf + 5, 1 + strlen (gbuf + 5));  /* skip leading SCPE_ */
10338 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
10339     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
10340         cond += SCPE_BASE;
10341         break;
10342         }
10343 if (0 == strcmp(gbuf, "OK"))
10344     cond = SCPE_OK;
10345 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
10346     unsigned long numeric_cond = strtol(gbuf, NULL, 0);
10347     if (0 == numeric_cond)                    /* try explicit number */
10348         return SCPE_ARG;
10349     cond = (t_stat) numeric_cond;
10350     }
10351 if (cond > SCPE_MAX_ERR)
10352     return SCPE_ARG;
10353 *stat = cond;
10354 return SCPE_OK;
10355 }
10356 
10357 /* Debug printout routines, from Dave Hittner */
10358 
10359 const char* debug_bstates = "01_^";
10360 char debug_line_prefix[256];
10361 int32 debug_unterm  = 0;
10362 
10363 /* Finds debug phrase matching bitmask from from device DEBTAB table */
10364 
10365 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10366 {
10367 static const char *debtab_none    = "DEBTAB_ISNULL";
10368 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
10369 const char *some_match = NULL;
10370 int32 offset = 0;
10371 
10372 if (dptr->debflags == 0)
10373     return debtab_none;
10374 
10375 dbits &= dptr->dctrl;                           /* Look for just the bits that matched */
10376 
10377 /* Find matching words for bitmask */
10378 
10379 while ((offset < 32) && dptr->debflags[offset].name) {
10380     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
10381         return dptr->debflags[offset].name;
10382     if (dptr->debflags[offset].mask & dbits)
10383         some_match = dptr->debflags[offset].name;
10384     offset++;
10385     }
10386 return some_match ? some_match : debtab_nomatch;
10387 }
10388 
10389 /* Prints standard debug prefix unless previous call unterminated */
10390 
10391 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10392 {
10393 const char* debug_type = get_dbg_verb (dbits, dptr);
10394 char tim_t[32] = "";
10395 char tim_a[32] = "";
10396 char  pc_s[64] = "";
10397 struct timespec time_now;
10398 
10399 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
10400     clock_gettime(CLOCK_REALTIME, &time_now);
10401     if (sim_deb_switches & SWMASK ('R'))
10402         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
10403     if (sim_deb_switches & SWMASK ('T')) {
10404         time_t tnow = (time_t)time_now.tv_sec;
10405         struct tm *now = gmtime(&tnow);
10406         (void)sprintf(tim_t, "%02d:%02d:%02d.%03ld ",
10407                       (int)now->tm_hour,
10408                       (int)now->tm_min,
10409                       (int)now->tm_sec,
10410                       (long)(time_now.tv_nsec / 1000000));
10411         }
10412     if (sim_deb_switches & SWMASK ('A')) {
10413         (void)sprintf(tim_t, "%d.%03ld ",
10414                       (int)(time_now.tv_sec),
10415                       (long)(time_now.tv_nsec / 1000000));
10416         }
10417     }
10418 if (sim_deb_switches & SWMASK ('P')) {
10419     t_value val;
10420 
10421     if (sim_vm_pc_value)
10422         val = (*sim_vm_pc_value)();
10423     else
10424         val = get_rval (sim_PC, 0);
10425     (void)sprintf(pc_s, "-%s:", sim_PC->name);
10426     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
10427     }
10428 (void)sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ",
10429               tim_t, tim_a, sim_gtime(), pc_s,
10430               "", dptr->name, debug_type);
10431 return debug_line_prefix;
10432 }
10433 
10434 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
     /* [previous][next][first][last][top][bottom][index][help] */
10435 {
10436 int32 i, fields, offset;
10437 uint32 value, beforevalue, mask;
10438 
10439 for (fields=offset=0; bitdefs[fields].name; ++fields) {
10440     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
10441         bitdefs[fields].offset = offset;
10442     offset += bitdefs[fields].width;
10443     }
10444 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
10445     if (bitdefs[i].name[0] == '\0')
10446         continue;
10447     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
10448         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
10449         (void)Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
10450         }
10451     else {
10452         const char *delta = "";
10453         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
10454         value = (uint32)((after >> bitdefs[i].offset) & mask);
10455         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
10456         if (value < beforevalue)
10457             delta = "_";
10458         if (value > beforevalue)
10459             delta = "^";
10460         if (bitdefs[i].valuenames)
10461             (void)Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
10462         else
10463             if (bitdefs[i].format) {
10464                 (void)Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
10465                 (void)Fprintf(stream, bitdefs[i].format, value);
10466                 (void)Fprintf(stream, " ");
10467                 }
10468             else
10469                 (void)Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
10470         }
10471     }
10472 }
10473 
10474 /* Prints state of a register: bit translation + state (0,1,_,^)
10475    indicating the state and transition of the bit and bitfields. States:
10476    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
10477 
10478 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
     /* [previous][next][first][last][top][bottom][index][help] */
10479     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
10480 {
10481 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
10482     if (!debug_unterm)
10483         (void)fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
10484     if (header)
10485         (void)fprintf(sim_deb, "%s: ", header);
10486     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
10487     if (terminate)
10488         (void)fprintf(sim_deb, "\r\n");
10489     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
10490     }
10491 }
10492 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
     /* [previous][next][first][last][top][bottom][index][help] */
10493     uint32 before, uint32 after, int terminate)
10494 {
10495 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
10496 }
10497 
10498 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10499 void sim_printf (const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10500 {
10501 char stackbuf[STACKBUFSIZE];
10502 int32 bufsize = sizeof(stackbuf);
10503 char *buf = stackbuf;
10504 int32 len;
10505 va_list arglist;
10506 
10507 while (1) {                                         /* format passed string, args */
10508     va_start (arglist, fmt);
10509     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10510     va_end (arglist);
10511 
10512 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10513 
10514     if ((len < 0) || (len >= bufsize-1)) {
10515         if (buf != stackbuf)
10516             FREE (buf);
10517         if (bufsize >= (INT_MAX / 2))
10518             return;                                 /* too big */
10519         bufsize = bufsize * 2;
10520         if (bufsize < len + 2)
10521             bufsize = len + 2;
10522         buf = (char *) malloc (bufsize);
10523         if (buf == NULL)                            /* out of memory */
10524             return;
10525         buf[bufsize-1] = '\0';
10526         continue;
10527         }
10528     break;
10529     }
10530 
10531 if (sim_is_running) {
10532     char *c, *remnant = buf;
10533     while ((c = strchr(remnant, '\n'))) { //-NLOK
10534         if ((c != buf) && (*(c - 1) != '\r')) //-NLOK
10535             (void)printf("%.*s\r\n", (int)(c-remnant), remnant); //-NLOK
10536         else
10537             (void)printf("%.*s\n", (int)(c-remnant), remnant); //-NLOK
10538         remnant = c + 1;
10539         }
10540     (void)printf("%s", remnant);
10541     }
10542 else
10543     (void)printf("%s", buf);
10544 /* sim_printf carries diagnostic/warning text that must not be lost.  When
10545  * stdout is not a TTY (e.g. piped through "tee" for logging, as done by
10546  * per-instance boot scripts), the C library defaults to full block
10547  * buffering instead of line buffering, so infrequent messages -- exactly
10548  * the kind sim_printf is used for -- can sit unflushed for the entire run
10549  * of a long-lived headless daemon. Flush explicitly so every message is
10550  * visible immediately, regardless of how stdout is connected. */
10551 (void)fflush (stdout);
10552 if (sim_log && (sim_log != stdout)) {
10553     (void)fprintf (sim_log, "%s", buf);
10554     (void)fflush (sim_log);
10555     }
10556 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log)) {
10557     (void)fprintf (sim_deb, "%s", buf);
10558     (void)fflush (sim_deb);
10559     }
10560 
10561 if (buf != stackbuf)
10562     FREE (buf);
10563 }
10564 
10565 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10566 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10567 {
10568 char stackbuf[STACKBUFSIZE];
10569 size_t bufsize = sizeof(stackbuf);
10570 char *buf = stackbuf;
10571 size_t len;
10572 va_list arglist;
10573 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
10574 
10575 while (1) {                                         /* format passed string, args */
10576     va_start (arglist, fmt);
10577     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10578     va_end (arglist);
10579 
10580 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10581 
10582     if (len >= bufsize - 1) {
10583         if (buf != stackbuf)
10584             FREE (buf);
10585         bufsize = bufsize * 2;
10586         if (bufsize < len + 2)
10587             bufsize = len + 2;
10588         buf = (char *) malloc (bufsize);
10589         if (buf == NULL)                            /* out of memory */
10590             return SCPE_MEM;
10591         buf[bufsize-1] = '\0';
10592         continue;
10593         }
10594     break;
10595     }
10596 
10597 if (sim_do_ocptr[sim_do_depth]) {
10598     if (!sim_do_echo && !sim_quiet && !inhibit_message)
10599         sim_printf("%s> %s\r\n", do_position(), sim_do_ocptr[sim_do_depth]);
10600     else {
10601         if (sim_deb)                        /* Always put context in debug output */
10602             (void)fprintf (sim_deb, "%s> %s\r\n", do_position(), sim_do_ocptr[sim_do_depth]);
10603         }
10604     }
10605 if (sim_is_running && !inhibit_message) {
10606     char *c, *remnant = buf;
10607     while ((c = strchr(remnant, '\n'))) { //-NLOK
10608         if ((c != buf) && (*(c - 1) != '\r')) //-NLOK
10609             (void)printf("%.*s\r\n", (int)(c-remnant), remnant); //-NLOK
10610         else
10611             (void)printf("%.*s\n", (int)(c-remnant), remnant); //-NLOK
10612         remnant = c + 1;
10613         }
10614     (void)printf("%s", remnant);
10615     }
10616 else {
10617     if (!inhibit_message)
10618         (void)printf("%s", buf);
10619     }
10620 if (sim_log && (sim_log != stdout) && !inhibit_message)
10621     (void)fprintf (sim_log, "%s", buf);
10622 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
10623     (void)fprintf (sim_deb, "%s", buf);
10624 
10625 if (buf != stackbuf)
10626     FREE (buf);
10627 return stat | SCPE_NOMESSAGE;
10628 }
10629 
10630 /* Inline debugging - will print debug message if debug file is
10631    set and the bitmask matches the current device debug options.
10632    Extra returns are added for un*x systems, since the output
10633    device is set into 'raw' mode when the cpu is booted,
10634    and the extra returns don't hurt any other systems.
10635    Callers should be calling sim_debug() which is a macro
10636    defined in scp.h which evaluates the action condition before
10637    incurring call overhead. */
10638 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10639 {
10640 DEVICE *dptr = (DEVICE *)vdptr;
10641 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
10642     char stackbuf[STACKBUFSIZE];
10643     int32 bufsize = sizeof(stackbuf);
10644     char *buf = stackbuf;
10645     va_list arglist;
10646     int32 i, j, len;
10647     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
10648 
10649     buf[bufsize-1] = '\0';
10650     while (1) {                                         /* format passed string, args */
10651         va_start (arglist, fmt);
10652         len = vsnprintf (buf, bufsize-1, fmt, arglist);
10653         va_end (arglist);
10654 
10655 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10656 
10657         if ((len < 0) || (len >= bufsize-1)) {
10658             if (buf != stackbuf)
10659                 FREE (buf);
10660             if (bufsize >= (INT_MAX / 2))
10661                 return;                                 /* too big */
10662             bufsize = bufsize * 2;
10663             if (bufsize < len + 2)
10664                 bufsize = len + 2;
10665             buf = (char *) malloc (bufsize);
10666             if (buf == NULL)                            /* out of memory */
10667                 return;
10668             buf[bufsize-1] = '\0';
10669             continue;
10670             }
10671         break;
10672         }
10673 
10674 /* Output the formatted data expanding newlines where they exist */
10675 
10676     for (i = j = 0; i < len; ++i) {
10677         if ('\n' == buf[i]) {
10678             if (i >= j) {
10679                 if ((i != j) || (i == 0)) {
10680                     if (debug_unterm)
10681                         (void)fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
10682                     else                                /* print prefix when required */
10683                         (void)fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
10684                     }
10685                 debug_unterm = 0;
10686                 }
10687             j = i + 1;
10688             }
10689         }
10690     if (i > j) {
10691         if (debug_unterm)
10692             (void)fprintf (sim_deb, "%.*s", i-j, &buf[j]);
10693         else                                        /* print prefix when required */
10694             (void)fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
10695         }
10696 
10697 /* Set unterminated flag for next time */
10698 
10699     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
10700     if (buf != stackbuf)
10701         FREE (buf);
10702     }
10703 return;
10704 }
10705 
10706 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] */
10707 {
10708 #if defined(TESTING)
10709 cpu_state_t * cpup = _cpup;
10710 #endif
10711 if (sim_deb && (dptr->dctrl & reason)) {
10712     sim_debug (reason, dptr, "%s %s %slen: %08X\r\n", sim_uname(uptr), txt, position, (unsigned int)len);
10713     if (data && len) {
10714         size_t i, same, group, sidx, oidx, ridx, eidx, soff;
10715         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
10716         static char hex[] = "0123456789ABCDEF";
10717         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
10718         static unsigned char ebcdic2ascii[] = {
10719             0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
10720             0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
10721             0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
10722             0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
10723             0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
10724             0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
10725             0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
10726             0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
10727             0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
10728             0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041,
10729             0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
10730             0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136,
10731             0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
10732             0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077,
10733             0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
10734             0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
10735             0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
10736             0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
10737             0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
10738             0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320,
10739             0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170,
10740             0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327,
10741             0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
10742             0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
10743             0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
10744             0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
10745             0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
10746             0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
10747             0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
10748             0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
10749             0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
10750             0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377,
10751             };
10752 
10753         for (i=same=0; i<len; i += 16) {
10754             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
10755                 ++same;
10756                 continue;
10757                 }
10758             if (same > 0) {
10759                 sim_debug (reason, dptr, "%04lx thru %04lx same as above\r\n",
10760                            (unsigned long int)(i - (16*same)),
10761                            (unsigned long int)(i - 1));
10762                 same = 0;
10763                 }
10764             group = (((len - i) > 16) ? 16 : (len - i));
10765             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10766             eidx = strlen(ebcdicbuf);
10767             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10768             ridx = strlen(rad50buf);
10769             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10770             soff = strlen(strbuf);
10771             for (sidx=oidx=0; sidx<group; ++sidx) {
10772                 outbuf[oidx++] = ' ';
10773                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10774                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10775                 if (sim_isprint (data[i+sidx]))
10776                     strbuf[soff+sidx] = data[i+sidx];
10777                 else
10778                     strbuf[soff+sidx] = '.';
10779                 if (ridx && ((sidx&1) == 0)) {
10780                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10781 
10782                     if (word >= 64000) {
10783                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10784                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10785                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10786                         }
10787                     else {
10788                         rad50buf[ridx++] = rad50[word/1600];
10789                         rad50buf[ridx++] = rad50[(word/40)%40];
10790                         rad50buf[ridx++] = rad50[word%40];
10791                         }
10792                     }
10793                 if (eidx) {
10794                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10795                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10796                     else
10797                         ebcdicbuf[eidx++] = '.';
10798                     }
10799                 }
10800             outbuf[oidx] = '\0';
10801             strbuf[soff+sidx] = '\0';
10802             ebcdicbuf[eidx] = '\0';
10803             rad50buf[ridx] = '\0';
10804             sim_debug (reason, dptr, "%04lx%-48s %s%s%s\r\n",
10805                     (unsigned long int)i, outbuf, strbuf, ebcdicbuf, rad50buf);
10806             }
10807         if (same > 0) {
10808             sim_debug (reason, dptr, "%04lx thru %04lx same as above\r\n",
10809                     (unsigned long int)(i-(16*same)),
10810                     (unsigned long int)(len-1));
10811             }
10812         }
10813     }
10814 }
10815 
10816 int Fprintf (FILE *f, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10817 {
10818 int ret = 0;
10819 va_list args;
10820 
10821 va_start (args, fmt);
10822     ret = vfprintf (f, fmt, args);
10823 va_end (args);
10824 return ret;
10825 }
10826 
10827 /* Hierarchical help presentation
10828  *
10829  * Device help can be presented hierarchically by calling
10830  *
10831  * t_stat scp_help (FILE *st, DEVICE *dptr,
10832  *                  UNIT *uptr, int flag, const char *help, char *cptr)
10833  *
10834  * or one of its three cousins from the device HELP routine.
10835  *
10836  * *help is the pointer to the structured help text to be displayed.
10837  *
10838  * The format and usage, and some helper macros can be found in scp_help.h
10839  * If you don't use the macros, it is not necessary to #include "scp_help.h".
10840  *
10841  * Actually, if you don't specify a DEVICE pointer and don't include
10842  * other device references, it can be used for non-device help.
10843  */
10844 
10845 #define blankch(x) ((x) == ' ' || (x) == '\t')
10846 
10847 typedef struct topic {
10848     size_t         level;
10849     char          *title;
10850     char          *label;
10851     struct topic  *parent;
10852     struct topic **children;
10853     uint32         kids;
10854     char          *text;
10855     size_t         len;
10856     uint32         flags;
10857     size_t         kidwid;
10858 #define HLP_MAGIC_TOPIC  1
10859     } TOPIC;
10860 
10861 static volatile struct {
10862     const char *error;
10863     const char *prox;
10864     size_t block;
10865     size_t line;
10866     } help_where = { "", NULL, 0, 0 };
10867 jmp_buf help_env;
10868 
10869 #define FAIL(why,text,here)        \
10870   {                                \
10871     help_where.error = #text;      \
10872     help_where.prox = here;        \
10873     longjmp ( help_env, (why) );   \
10874     /*LINTED E_STMT_NOT_REACHED*/  \
10875   }
10876 
10877 /*
10878  * Add to topic text.
10879  * Expands text buffer as necessary.
10880  */
10881 
10882 static void appendText (TOPIC *topic, const char *text, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
10883 {
10884 char *newt;
10885 
10886 if (!len)
10887     return;
10888 
10889 newt = (char *)realloc (topic->text, topic->len + len +1);
10890 if (!newt) {
10891 #if !defined(SUNLINT)
10892     FAIL (SCPE_MEM, No memory, NULL);
10893 #endif /* if !defined(SUNLINT) */
10894     }
10895 topic->text = newt;
10896 memcpy (newt + topic->len, text, len);
10897 topic->len +=len;
10898 newt[topic->len] = '\0';
10899 return;
10900 }
10901 
10902 /* Release memory held by a topic and its children.
10903  */
10904 static void cleanHelp (TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10905 {
10906 TOPIC *child;
10907 size_t i;
10908 
10909 FREE (topic->title);
10910 FREE (topic->text);
10911 FREE (topic->label);
10912 for (i = 0; i < topic->kids; i++) {
10913     child = topic->children[i];
10914     cleanHelp (child);
10915     FREE (child);
10916     }
10917 FREE (topic->children);
10918 return;
10919 }
10920 
10921 /* Build a help tree from a string.
10922  * Handles substitutions, formatting.
10923  */
10924 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10925                          UNIT *uptr, const char *htext, va_list ap)
10926 {
10927 char *end;
10928 size_t n, ilvl;
10929 #define VSMAX 100
10930 char *vstrings[VSMAX];
10931 size_t vsnum = 0;
10932 char * astrings[VSMAX+1];
10933 size_t asnum = 0;
10934 char *const *hblock;
10935 const char *ep;
10936 t_bool excluded = FALSE;
10937 
10938 /* variable arguments consumed table.
10939  * The scheme used allows arguments to be accessed in random
10940  * order, but for portability, all arguments must be char *.
10941  * If you try to violate this, there ARE machines that WILL break.
10942  */
10943 
10944 (void)memset (vstrings, 0, sizeof (vstrings));
10945 (void)memset (astrings, 0, sizeof (astrings));
10946 astrings[asnum++] = (char *) htext;
10947 
10948 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10949     help_where.block = hblock - astrings;
10950     help_where.line = 0;
10951     while (*htext) {
10952         const char *start;
10953 
10954         help_where.line++;
10955         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10956             if (excluded) {                     /* Excluded topic text */
10957                 while (*htext && *htext != '\n')
10958                     htext++;
10959                 if (*htext)
10960                     ++htext;
10961                 continue;
10962                 }
10963             ilvl = 1;
10964             appendText (topic, "    ", 4);      /* Basic indentation */
10965             if (*htext == '+') {                /* More for each + */
10966                 while (*htext == '+') {
10967                     ilvl++;
10968                     appendText (topic, "    ", 4);
10969                     htext++;
10970                     }
10971                 }
10972             while (*htext && *htext != '\n' && sim_isspace (*htext))
10973                 htext++;
10974             if (!*htext)                        /* Empty after removing leading spaces */
10975                 break;
10976             start = htext;
10977             while (*htext) {                    /* Process line for substitutions */
10978                 if (*htext == '%') {
10979                     appendText (topic, start, htext - start); /* Flush up to escape */
10980                     switch (*++htext) {         /* Evaluate escape */
10981                         case 'U':
10982                             if (dptr) {
10983                                 char buf[129];
10984                                 n = uptr? uptr - dptr->units: 0;
10985                                 (void)sprintf (buf, "%s%u", dptr->name, (int)n);
10986                                 appendText (topic, buf, strlen (buf));
10987                                 }
10988                             break;
10989                         case 'D':
10990                             if (dptr != NULL)
10991                                 appendText (topic, dptr->name, strlen (dptr->name));
10992                             break;
10993                         case 'S':
10994                             appendText (topic, sim_name, strlen (sim_name));
10995                             break;
10996                         case '%':
10997                             appendText (topic, "%", 1);
10998                             break;
10999                         case '+':
11000                             appendText (topic, "+", 1);
11001                             break;
11002                         default:                    /* Check for vararg # */
11003                             if (sim_isdigit (*htext)) {
11004                                 n = 0;
11005                                 while (sim_isdigit (*htext))
11006                                     n += (n * 10) + (*htext++ - '0');
11007                                 if (( *htext != 'H' && *htext != 's') ||
11008                                     n == 0 || n >= VSMAX) {
11009 #if !defined(SUNLINT)
11010                                     FAIL (SCPE_ARG, Invalid escape, htext);
11011 #endif /* if !defined(SUNLINT) */
11012                                     }
11013                                 while (n > vsnum)   /* Get arg pointer if not cached */
11014                                     vstrings[vsnum++] = va_arg (ap, char *);
11015                                 start = vstrings[n-1]; /* Insert selected string */
11016                                 if (*htext == 'H') {   /* Append as more input */
11017                                     if (asnum >= VSMAX) {
11018 #if !defined(SUNLINT)
11019                                         FAIL (SCPE_ARG, Too many blocks, htext);
11020 #endif /* if !defined(SUNLINT) */
11021                                         }
11022                                     astrings[asnum++] = (char *)start;
11023                                     break;
11024                                     }
11025                                 ep = start;
11026                                 while (*ep) {
11027                                     if (*ep == '\n') {
11028                                         ep++;       /* Segment to \n */
11029                                         appendText (topic, start, ep - start);
11030                                         if (*ep) {  /* More past \n, indent */
11031                                             size_t i;
11032                                             for (i = 0; i < ilvl; i++)
11033                                                 appendText (topic, "    ", 4);
11034                                             }
11035                                         start = ep;
11036                                         }
11037                                     else
11038                                         ep++;
11039                                     }
11040                                 appendText (topic, start, ep-start);
11041                                 break;
11042                                 }
11043 #if !defined(SUNLINT)
11044                             FAIL (SCPE_ARG, Invalid escape, htext);
11045 #endif /* if !defined(SUNLINT) */
11046                         } /* switch (escape) */
11047                     start = ++htext;
11048                     continue;                   /* Current line */
11049                     } /* if (escape) */
11050                 if (*htext == '\n') {           /* End of line, append last segment */
11051                     htext++;
11052                     appendText (topic, start, htext - start);
11053                     break;                      /* To next line */
11054                     }
11055                 htext++;                        /* Regular character */
11056                 }
11057             continue;
11058             } /* topic text line */
11059         if (sim_isdigit (*htext)) {             /* Topic heading */
11060             TOPIC **children;
11061             TOPIC *newt;
11062             char nbuf[100];
11063 
11064             n = 0;
11065             start = htext;
11066             while (sim_isdigit (*htext))
11067                 n += (n * 10) + (*htext++ - '0');
11068             if ((htext == start) || !n) {
11069 #if !defined(SUNLINT)
11070                 FAIL (SCPE_ARG, Invalid topic heading, htext);
11071 #endif /* if !defined(SUNLINT) */
11072                 }
11073             if (n <= topic->level) {            /* Find level for new topic */
11074                 while (n <= topic->level)
11075                     topic = topic->parent;
11076                 }
11077             else {
11078                 if (n > topic->level + 1) {     /* Skipping down more than 1 */
11079 #if !defined(SUNLINT)
11080                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
11081 #endif /* if !defined(SUNLINT) */
11082                     }
11083                 }
11084             while (*htext && (*htext != '\n') && sim_isspace (*htext))
11085                 htext++;
11086             if (!*htext || (*htext == '\n')) {  /* Name missing */
11087 #if !defined(SUNLINT)
11088                 FAIL (SCPE_ARG, Missing topic name, htext);
11089 #endif /* if !defined(SUNLINT) */
11090                 }
11091             start = htext;
11092             while (*htext && (*htext != '\n'))
11093                 htext++;
11094             if (start == htext) {               /* Name NULL */
11095 #if !defined(SUNLINT)
11096                 FAIL (SCPE_ARG, Null topic name, htext);
11097 #endif /* if !defined(SUNLINT) */
11098                 }
11099             excluded = FALSE;
11100             if (*start == '?') {                /* Conditional topic? */
11101                 size_t n = 0;
11102                 start++;
11103                 while (sim_isdigit (*start))    /* Get param # */
11104                     n += (n * 10) + (*start++ - '0');
11105                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX) {
11106 #if !defined(SUNLINT)
11107                     FAIL (SCPE_ARG, Invalid parameter number, start);
11108 #endif /* if !defined(SUNLINT) */
11109                     }
11110                 while (n > vsnum)               /* Get arg pointer if not cached */
11111                     vstrings[vsnum++] = va_arg (ap, char *);
11112                 end = vstrings[n-1];            /* Check for True */
11113                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
11114                     excluded = TRUE;            /* False, skip topic this time */
11115                     if (*htext)
11116                         htext++;
11117                     continue;
11118                     }
11119                 }
11120             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
11121             if (!newt) {
11122 #if !defined(SUNLINT)
11123                 FAIL (SCPE_MEM, No memory, NULL);
11124 #endif /* if !defined(SUNLINT) */
11125                 }
11126             size_t len = (htext > start) ? (htext - start) : 0;
11127             newt->title = (char *) malloc(len + 1);
11128             if (!newt->title) {
11129                 FREE (newt);
11130 #if !defined(SUNLINT)
11131                 FAIL (SCPE_MEM, No memory, NULL);
11132 #endif /* if !defined(SUNLINT) */
11133                 }
11134             memcpy (newt->title, start, htext - start);
11135             newt->title[htext - start] = '\0';
11136             if (*htext)
11137                 htext++;
11138 
11139             if (newt->title[0] == '$')
11140                 newt->flags |= HLP_MAGIC_TOPIC;
11141 
11142             children = (TOPIC **) realloc (topic->children,
11143                                            (topic->kids +1) * sizeof (TOPIC *));
11144             if (NULL == children) {
11145                 FREE (newt->title);
11146                 FREE (newt);
11147 #if !defined(SUNLINT)
11148                 FAIL (SCPE_MEM, No memory, NULL);
11149 #endif /* if !defined(SUNLINT) */
11150                 }
11151             topic->children = children;
11152             topic->children[topic->kids++] = newt;
11153             newt->level = n;
11154             newt->parent = topic;
11155             n = strlen (newt->title);
11156             if (n > topic->kidwid)
11157                 topic->kidwid = n;
11158             (void)sprintf (nbuf, ".%u", topic->kids);
11159             n = strlen (topic->label) + strlen (nbuf) + 1;
11160             newt->label = (char *) malloc (n);
11161             if (NULL == newt->label) {
11162                 FREE (newt->title);
11163                 topic->children[topic->kids -1] = NULL;
11164                 FREE (newt);
11165 #if !defined(SUNLINT)
11166                 FAIL (SCPE_MEM, No memory, NULL);
11167 #endif /* if !defined(SUNLINT) */
11168                 }
11169             (void)sprintf (newt->label, "%s%s", topic->label, nbuf);
11170             topic = newt;
11171             continue;
11172             } /* digits introducing a topic */
11173         if (*htext == ';') {                    /* Comment */
11174             while (*htext && *htext != '\n')
11175                 htext++;
11176             continue;
11177             }
11178 #if !defined(SUNLINT)
11179         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
11180 #endif /* if !defined(SUNLINT) */
11181         } /* htext not at end */
11182     (void)memset (vstrings, 0, VSMAX * sizeof (char *));
11183     vsnum = 0;
11184     } /* all strings */
11185 
11186 return topic;
11187 }
11188 
11189 /*
11190  * Create prompt string - top thru current topic
11191  * Add prompt at end.
11192  */
11193 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
     /* [previous][next][first][last][top][bottom][index][help] */
11194 {
11195 char *prefix;
11196 char *newp, *newt;
11197 
11198 if (topic->level == 0) {
11199     prefix = (char *) calloc (2,1);
11200     if (!prefix) {
11201 #if !defined(SUNLINT)
11202         FAIL (SCPE_MEM, No memory, NULL);
11203 #endif /* if !defined(SUNLINT) */
11204         }
11205     prefix[0] = '\n';
11206     }
11207 else
11208     prefix = helpPrompt (topic->parent, "", oneword);
11209 
11210 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
11211                         strlen (pstring) +1);
11212 if (!newp) {
11213     FREE (prefix);
11214 #if !defined(SUNLINT)
11215     FAIL (SCPE_MEM, No memory, NULL);
11216 #endif /* if !defined(SUNLINT) */
11217     }
11218 strcpy (newp, prefix);
11219 if (topic->children) {
11220     if (topic->level != 0)
11221         strcat (newp, " ");
11222     newt = (topic->flags & HLP_MAGIC_TOPIC)?
11223             topic->title+1: topic->title;
11224     if (oneword) {
11225         char *np = newp + strlen (newp);
11226         while (*newt) {
11227             *np++ = blankch (*newt)? '_' : *newt;
11228             newt++;
11229             }
11230         *np = '\0';
11231         }
11232     else
11233         strcat (newp, newt);
11234     if (*pstring && *pstring != '?')
11235         strcat (newp, " ");
11236     }
11237 strcat (newp, pstring);
11238 FREE (prefix);
11239 return newp;
11240 }
11241 
11242 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
11243 {
11244 char tbuf[CBUFSIZE];
11245 size_t i, skiplines;
11246 #if defined(_WIN32)
11247 FILE *tmp;
11248 char *tmpnam;
11249 
11250 do {
11251     int fd;
11252     tmpnam = _tempnam (NULL, "simh");
11253     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
11254     if (fd != -1) {
11255         tmp = _fdopen (fd, "w+");
11256         break;
11257         }
11258     } while (1);
11259 #else
11260 FILE *tmp = tmpfile();
11261 #endif /* if defined(_WIN32) */
11262 
11263 if (!tmp) {
11264     (void)fprintf (st, "Unable to create temporary file: %s (Error %d)\r\n",
11265                    xstrerror_l(errno), errno);
11266     return;
11267     }
11268 
11269 if (topic->title)
11270     (void)fprintf (st, "%s\r\n", topic->title+1);
11271 
11272 skiplines = 0;
11273 if (topic->title) {
11274   if (!strcmp (topic->title+1, "Registers")) {
11275       fprint_reg_help (tmp, dptr) ;
11276       skiplines = 1;
11277       }
11278   else
11279       if (!strcmp (topic->title+1, "Set commands")) {
11280           fprint_set_help (tmp, dptr);
11281           skiplines = 3;
11282           }
11283       else
11284           if (!strcmp (topic->title+1, "Show commands")) {
11285               fprint_show_help (tmp, dptr);
11286               skiplines = 3;
11287               }
11288   }
11289 rewind (tmp);
11290 if (errno) {
11291     (void)fprintf (st, "rewind: error %d\r\n", errno);
11292 }
11293 
11294 /* Discard leading blank lines/redundant titles */
11295 
11296 for (i =0; i < skiplines; i++)
11297     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
11298 
11299 while (fgets (tbuf, sizeof (tbuf), tmp)) {
11300     if (tbuf[0] != '\n')
11301         fputs ("    ", st);
11302     fputs (tbuf, st);
11303     }
11304 fclose (tmp);
11305 #if defined(_WIN32)
11306 remove (tmpnam);
11307 FREE (tmpnam);
11308 #endif /* if defined(_WIN32) */
11309 return;
11310 }
11311 /* Flatten and display help for those who say they prefer it. */
11312 
11313 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11314                                UNIT *uptr, int32 flag,
11315                                TOPIC *topic, va_list ap )
11316 {
11317 size_t i;
11318 
11319 if (topic->flags & HLP_MAGIC_TOPIC) {
11320     (void)fprintf (st, "\r\n%s ", topic->label);
11321     displayMagicTopic (st, dptr, topic);
11322     }
11323 else
11324     (void)fprintf (st, "\r\n%s %s\r\n", topic->label, topic->title);
11325 
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 for (i = 0; i < topic->kids; i++)
11335     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
11336 
11337 return SCPE_OK;
11338 }
11339 
11340 #define HLP_MATCH_AMBIGUOUS (~0u)
11341 #define HLP_MATCH_WILDCARD  (~1U)
11342 #define HLP_MATCH_NONE      0
11343 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
     /* [previous][next][first][last][top][bottom][index][help] */
11344 {
11345 size_t i, match;
11346 char cbuf[CBUFSIZE], *cptr;
11347 
11348 if (!strcmp (token, "*"))
11349     return HLP_MATCH_WILDCARD;
11350 
11351 match = 0;
11352 for (i = 0; i < topic->kids; i++) {
11353     strcpy (cbuf,topic->children[i]->title +
11354             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11355     cptr = cbuf;
11356     while (*cptr) {
11357         if (blankch (*cptr)) {
11358             *cptr++ = '_';
11359             }
11360         else {
11361             *cptr = (char)toupper (*cptr);
11362             cptr++;
11363             }
11364         }
11365     if (!strcmp (cbuf, token))      /* Exact Match */
11366         return i+1;
11367     if (!strncmp (cbuf, token, strlen (token))) {
11368         if (match)
11369             return HLP_MATCH_AMBIGUOUS;
11370         match = i+1;
11371         }
11372     }
11373 return match;
11374 }
11375 
11376 /* Main help routine */
11377 
11378 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11379                   UNIT *uptr, int32 flag,
11380                   const char *help, const char *cptr, va_list ap)
11381 {
11382 TOPIC top;
11383 TOPIC *topic = &top;
11384 int failed;
11385 size_t match;
11386 size_t i;
11387 const char *p;
11388 t_bool flat_help = FALSE;
11389 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
11390 
11391 static const char attach_help[] = { " ATTACH" };
11392 static const char  brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help.\r\n" };
11393 static const char onecmd_help[] = { "%s help.\r\n" };
11394 static const char   help_help[] = {
11395     /****|***********************80 column width guide********************************/
11396     "    To see more HELP information, type the listed subtopic name.  To move\r\n"
11397     "    up a level, just type <CR>.  To review the current subtopic, type \"?\".\r\n"
11398     "    To view all subtopics, type \"*\".  To exit type \"EXIT\", \"^C\", or \"^D\".\r\n\r\n"
11399     };
11400 
11401 (void)memset (&top, 0, sizeof(top));
11402 top.parent = &top;
11403 if ((failed = setjmp (help_env)) != 0) {
11404     (void)fprintf (stderr, "\r\nHELP was unable to process HELP for this device.\r\n"
11405                            "Error in block %u line %u: %s\r\n"
11406                            "%s%*.*s%s\r\n",
11407                    (int)help_where.block, (int)help_where.line, help_where.error, //-V567
11408                    help_where.prox ? "Near '" : "",
11409                    help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
11410                    help_where.prox ? help_where.prox : "",
11411                    help_where.prox ? "'" : "");
11412     cleanHelp (&top);
11413     return failed;
11414     }
11415 
11416 /* Compile string into navigation tree */
11417 
11418 /* Root */
11419 
11420 if (dptr) {
11421     p = dptr->name;
11422     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
11423     }
11424 else
11425     p = sim_name;
11426 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
11427 if (!top.title)
11428   {
11429     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11430                    __func__, __FILE__, __LINE__);
11431 #if defined(USE_BACKTRACE)
11432 # if defined(SIGUSR2)
11433     (void)raise(SIGUSR2);
11434     /*NOTREACHED*/ /* unreachable */
11435 # endif /* if defined(SIGUSR2) */
11436 #endif /* if defined(USE_BACKTRACE) */
11437     abort();
11438   }
11439 for (i = 0; p[i]; i++ )
11440     top.title[i] = (char)toupper (p[i]);
11441 top.title[i] = '\0';
11442 if (flag & SCP_HELP_ATTACH)
11443     strcpy (top.title+i, attach_help);
11444 
11445 top.label = (char *) malloc (sizeof ("1"));
11446 if (!top.label)
11447   {
11448     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11449                    __func__, __FILE__, __LINE__);
11450 #if defined(USE_BACKTRACE)
11451 # if defined(SIGUSR2)
11452     (void)raise(SIGUSR2);
11453     /*NOTREACHED*/ /* unreachable */
11454 # endif /* if defined(SIGUSR2) */
11455 #endif /* if defined(USE_BACKTRACE) */
11456     abort();
11457   }
11458 strcpy (top.label, "1");
11459 
11460 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
11461 
11462 if (flat_help) {
11463     flag |= SCP_HELP_FLAT;
11464     if (sim_ttisatty())
11465         (void)fprintf (st, "%s help.\r\nThis help is also available in hierarchical form.\r\n", top.title);
11466     else
11467         (void)fprintf (st, "%s help.\r\n", top.title);
11468     }
11469 else
11470     (void)fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
11471 
11472 /* Add text and subtopics */
11473 
11474 (void) buildHelp (&top, dptr, uptr, help, ap);
11475 
11476 /* Go to initial topic if provided */
11477 
11478 while (cptr && *cptr) {
11479     cptr = get_glyph (cptr, gbuf, 0);
11480     if (!gbuf[0])
11481         break;
11482     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11483         (void)fprintf (st, "\r\n");
11484         fputs (help_help, st);
11485         break;
11486         }
11487     match =  matchHelpTopicName (topic, gbuf);
11488     if (match == HLP_MATCH_WILDCARD) {
11489         if (dptr)
11490             displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11491         cleanHelp (&top);
11492         return SCPE_OK;
11493         }
11494     if (match == HLP_MATCH_AMBIGUOUS) {
11495         (void)fprintf (st, "\r\n%s is ambiguous in %s\r\n", gbuf, topic->title);
11496         break;
11497         }
11498     if (match == HLP_MATCH_NONE) {
11499         (void)fprintf (st, "\r\n%s is not available in %s\r\n", gbuf, topic->title);
11500         break;
11501         }
11502     topic = topic->children[match-1];
11503     }
11504 cptr = NULL;
11505 
11506 if (flat_help) {
11507     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11508     cleanHelp (&top);
11509     return SCPE_OK;
11510     }
11511 
11512 /* Interactive loop displaying help */
11513 
11514 while (TRUE) {
11515     char *pstring;
11516     const char *prompt[2] = {"? ", "Subtopic? "};
11517 
11518     /* Some magic topic names for help from data structures */
11519 
11520     if (topic->flags & HLP_MAGIC_TOPIC) {
11521         fputc ('\n', st);
11522         displayMagicTopic (st, dptr, topic);
11523         }
11524     else
11525         (void)fprintf (st, "\r\n%s\r\n", topic->title);
11526 
11527     /* Topic text (for magic topics, follows for explanations)
11528      * It's possible/reasonable for a magic topic to have no text.
11529      */
11530 
11531     if (topic->text)
11532         fputs (topic->text, st);
11533 
11534     if (topic->kids) {
11535         size_t w = 0;
11536         char *p;
11537         char tbuf[CBUFSIZE];
11538 
11539         (void)fprintf (st, "\r\n    Additional information available:\r\n\r\n");
11540         for (i = 0; i < topic->kids; i++) {
11541             strcpy (tbuf, topic->children[i]->title +
11542                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11543             for (p = tbuf; *p; p++) {
11544                 if (blankch (*p))
11545                     *p = '_';
11546                 }
11547             w += 4 + topic->kidwid;
11548             if (w > 80) {
11549                 w = 4 + topic->kidwid;
11550                 fputc ('\r', st);
11551                 fputc ('\n', st);
11552                 }
11553             (void)fprintf (st, "    %-*s", (int32_t)topic->kidwid, tbuf);
11554             }
11555         (void)fprintf (st, "\r\n\r\n");
11556         if (flag & SCP_HELP_ONECMD) {
11557             pstring = helpPrompt (topic, "", TRUE);
11558             (void)fprintf (st, "To view additional topics, type HELP %s topicname\r\n", pstring+1);
11559             FREE (pstring);
11560             break;
11561             }
11562         }
11563 
11564     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
11565         break;
11566 
11567   reprompt:
11568     if (NULL == cptr || !*cptr) {
11569         if (topic->kids == 0)
11570             topic = topic->parent;
11571         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
11572 
11573         cptr = read_line_p (pstring+1, cbuf, sizeof (cbuf), stdin);
11574         FREE (pstring);
11575         if ((cptr != NULL) &&                   /* Got something? */
11576             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
11577              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
11578             cptr = NULL;                        /* These are EOF synonyms */
11579         }
11580 
11581     if (NULL == cptr)                           /* EOF, exit help */
11582         break;
11583 
11584     cptr = get_glyph (cptr, gbuf, 0);
11585     if (!strcmp (gbuf, "*")) {              /* Wildcard */
11586         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11587         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
11588         }
11589     if (!gbuf[0]) {                         /* Blank, up a level */
11590         if (topic->level == 0)
11591             break;
11592         topic = topic->parent;
11593         continue;
11594         }
11595     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
11596         continue;
11597     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11598         fputs (help_help, st);
11599         goto reprompt;
11600         }
11601     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
11602         break;
11603 
11604     /* String - look for that topic */
11605 
11606     if (!topic->kids) {
11607         (void)fprintf (st, "No additional help at this level.\r\n");
11608         cptr = NULL;
11609         goto reprompt;
11610         }
11611     match = matchHelpTopicName (topic, gbuf);
11612     if (match == HLP_MATCH_AMBIGUOUS) {
11613         (void)fprintf (st, "%s is ambiguous, please type more of the topic name\r\n", gbuf);
11614         cptr = NULL;
11615         goto reprompt;
11616         }
11617 
11618     if (match == HLP_MATCH_NONE) {
11619         (void)fprintf (st, "Help for %s is not available\r\n", gbuf);
11620         cptr = NULL;
11621         goto reprompt;
11622         }
11623     /* Found, display subtopic */
11624 
11625     topic = topic->children[match-1];
11626     }
11627 
11628 /* Free structures and return */
11629 
11630 cleanHelp (&top);
11631 
11632 return SCPE_OK;
11633 }
11634 
11635 /* variable argument list shell - most commonly used */
11636 
11637 t_stat scp_help (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11638                  UNIT *uptr, int32 flag,
11639                  const char *help, const char *cptr, ...)
11640 {
11641 t_stat r;
11642 va_list ap;
11643 
11644 va_start (ap, cptr);
11645 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
11646 va_end (ap);
11647 
11648 return r;
11649 }
11650 
11651 #if defined(_MSC_VER)
11652 # pragma warning(pop)
11653 #endif

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