root/src/simh/scp.c

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

DEFINITIONS

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

   1 /*
   2  * scp.c: simulator control program
   3  *
   4  * vim: filetype=c:tabstop=4:ai:colorcolumn=84:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: 7cde852c-f62a-11ec-8444-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 1993-2022 Robert M. Supnik
  11  * Copyright (c) 2021-2023 Jeffrey H. Johnson
  12  * Copyright (c) 2006-2025 The DPS8M Development Team
  13  *
  14  * Permission is hereby granted, free of charge, to any person obtaining a
  15  * copy of this software and associated documentation files (the "Software"),
  16  * to deal in the Software without restriction, including without limitation
  17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  18  * and/or sell copies of the Software, and to permit persons to whom the
  19  * Software is furnished to do so, subject to the following conditions:
  20  *
  21  * The above copyright notice and this permission notice shall be included in
  22  * all copies or substantial portions of the Software.
  23  *
  24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  27  * ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  28  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  29  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30  * SOFTWARE.
  31  *
  32  * Except as contained in this notice, the name of Robert M. Supnik shall not
  33  * be used in advertising or otherwise to promote the sale, use or other
  34  * dealings in this Software without prior written authorization from
  35  * Robert M. Supnik.
  36  *
  37  * ---------------------------------------------------------------------------
  38  */
  39 
  40 //-V::701
  41 
  42 #if !defined(__EXTENSIONS__)
  43 # define __EXTENSIONS__
  44 #endif
  45 
  46 #if !defined(__STDC_WANT_LIB_EXT1__)
  47 # define __STDC_WANT_LIB_EXT1__ 1
  48 #endif
  49 
  50 #if !defined(_GNU_SOURCE)
  51 # define _GNU_SOURCE
  52 #endif
  53 
  54 /* Macros and data structures */
  55 
  56 #include "sim_defs.h"
  57 #include "sim_disk.h"
  58 #include "sim_tape.h"
  59 #include "sim_sock.h"
  60 
  61 #include <signal.h>
  62 #include <ctype.h>
  63 #include <time.h>
  64 #include <math.h>
  65 #include <stddef.h>
  66 #if defined(_WIN32)
  67 # if !defined(WIN32_LEAN_AND_MEAN)
  68 #  define WIN32_LEAN_AND_MEAN
  69 # endif /* if !defined(WIN32_LEAN_AND_MEAN) */
  70 # if defined(_MSC_VER)
  71 #  pragma warning(push, 3)
  72 # endif
  73 # include <direct.h>
  74 # include <io.h>
  75 # include <fcntl.h>
  76 #else
  77 # include <unistd.h>
  78 # define HAVE_UNISTD 1
  79 #endif
  80 #include <sys/stat.h>
  81 #include <sys/types.h>
  82 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  83 # include <sys/resource.h>
  84 #endif
  85 #include <setjmp.h>
  86 #include <stdint.h>
  87 #include <limits.h>
  88 #if defined(__APPLE__)
  89 # include <xlocale.h>
  90 #endif
  91 #include <locale.h>
  92 
  93 #include "linehistory.h"
  94 
  95 #if defined(__APPLE__)
  96 # include <sys/sysctl.h>
  97 #endif /* if defined(_APPLE_) */
  98 
  99 #if ( defined(__linux__) || defined(__linux) || defined(_linux) || defined(linux) ) //-V1040
 100 # include <sys/sysinfo.h>
 101 # define LINUX_OS
 102 #endif
 103 
 104 #include <uv.h>
 105 
 106 #if !defined(HAVE_UNISTD)
 107 # undef USE_BACKTRACE
 108 #endif /* if !defined(HAVE_UNISTD) */
 109 
 110 #if defined(USE_BACKTRACE)
 111 # include <string.h>
 112 # include <signal.h>
 113 #endif /* if defined(USE_BACKTRACE) */
 114 
 115 #if defined(__HAIKU__)
 116 # include <OS.h>
 117 #endif /* if defined(__HAIKU__) */
 118 
 119 #if !defined(__CYGWIN__)
 120 # if !defined(__APPLE__)
 121 #  if !defined(_AIX)
 122 #   if !defined(__MINGW32__)
 123 #    if !defined(__MINGW64__)
 124 #     if !defined(CROSS_MINGW32)
 125 #      if !defined(CROSS_MINGW64)
 126 #       if !defined(_WIN32)
 127 #        if !defined(__HAIKU__)
 128 #         include <link.h>
 129 #        endif
 130 #       endif
 131 #      endif
 132 #     endif
 133 #    endif
 134 #   endif
 135 #  endif
 136 # endif
 137 #endif
 138 
 139 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
 140 # include <windows.h>
 141 #endif
 142 
 143 #if defined(__CYGWIN__)
 144 # include <windows.h>
 145 # include <sys/utsname.h>
 146 # include <sys/cygwin.h>
 147 # include <cygwin/version.h>
 148 #endif
 149 
 150 #define DBG_CTR 0
 151 
 152 #include "../dps8/dps8.h"
 153 #include "../dps8/dps8_cpu.h"
 154 #include "../dps8/ver.h"
 155 
 156 #include "../dps8/dps8_iom.h"
 157 #include "../dps8/dps8_fnp2.h"
 158 
 159 #include "../decNumber/decContext.h"
 160 #include "../decNumber/decNumberLocal.h"
 161 
 162 #include "../dps8/dps8_math128.h"
 163 
 164 #include "../dps8/dps8_sir.h"
 165 
 166 #if !defined(__CYGWIN__)
 167 # if !defined(__APPLE__)
 168 #  if !defined(_AIX)
 169 #   if !defined(__MINGW32__)
 170 #    if !defined(__MINGW64__)
 171 #     if !defined(CROSS_MINGW32)
 172 #      if !defined(CROSS_MINGW64)
 173 #       if !defined(_WIN32)
 174 #        if !defined(__HAIKU__)
 175 static unsigned int dl_iterate_phdr_callback_called = 0;
 176 #        endif
 177 #       endif
 178 #      endif
 179 #     endif
 180 #    endif
 181 #   endif
 182 #  endif
 183 # endif
 184 #endif
 185 
 186 #if defined(MAX)
 187 # undef MAX
 188 #endif /* if defined(MAX) */
 189 #define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
 190 
 191 #if defined(FREE)
 192 # undef FREE
 193 #endif /* if defined(FREE) */
 194 #define FREE(p) do  \
 195   {                 \
 196     free((p));      \
 197     (p) = NULL;     \
 198   } while(0)
 199 
 200 /* search logical and boolean ops */
 201 
 202 #define SCH_OR          0                               /* search logicals */
 203 #define SCH_AND         1
 204 #define SCH_XOR         2
 205 #define SCH_E           0                               /* search booleans */
 206 #define SCH_N           1
 207 #define SCH_G           2
 208 #define SCH_L           3
 209 #define SCH_EE          4
 210 #define SCH_NE          5
 211 #define SCH_GE          6
 212 #define SCH_LE          7
 213 
 214 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
 215 #define SRBSIZ          1024                            /* save/restore buffer */
 216 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
 217 #define SIM_BRK_ALLTYP  0xFFFFFFFB
 218 
 219 #define UPDATE_SIM_TIME                                         \
 220     if (1) {                                                    \
 221         int32 _x;                                               \
 222         if (sim_clock_queue == QUEUE_LIST_END)                  \
 223             _x = noqueue_time;                                  \
 224         else                                                    \
 225             _x = sim_clock_queue->time;                         \
 226         sim_time = sim_time + (_x - sim_interval);              \
 227         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
 228         if (sim_clock_queue == QUEUE_LIST_END)                  \
 229             noqueue_time = sim_interval;                        \
 230         else                                                    \
 231             sim_clock_queue->time = sim_interval;               \
 232         }                                                       \
 233     else                                                        \
 234         (void)0
 235 
 236 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
 237 
 238 #define SZ_R(rp) \
 239     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
 240 
 241 #define SZ_LOAD(sz,v,mb,j)                                                 \
 242     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j));        \
 243     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
 244     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
 245     else v = *(((t_uint64 *) mb) + ((uint32) j));
 246 
 247 #define SZ_STORE(sz,v,mb,j)                                                         \
 248     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v;                    \
 249     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
 250     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
 251     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
 252 
 253 #define GET_SWITCHES(cp) \
 254     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
 255 
 256 #define GET_RADIX(val,dft)                          \
 257     if (sim_switches & SWMASK ('O')) val = 8;       \
 258     else if (sim_switches & SWMASK ('D')) val = 10; \
 259     else if (sim_switches & SWMASK ('H')) val = 16; \
 260     else val = dft;
 261 
 262 /*
 263  * The per-simulator init routine is a weak global that defaults to NULL
 264  * The other per-simulator pointers can be overridden by the init routine
 265  */
 266 
 267 t_bool sim_asynch_enabled = FALSE;
 268 t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
 269 t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
 270 extern void (*sim_vm_init) (void);
 271 extern void (*sim_vm_exit) (void);
 272 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
 273 void (*sim_vm_post) (t_bool from_scp) = NULL;
 274 CTAB *sim_vm_cmd = NULL;
 275 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
 276 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
 277 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
 278 t_value (*sim_vm_pc_value) (void) = NULL;
 279 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
 280 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
 281 unsigned int nprocs;
 282 
 283 /* Prototypes */
 284 
 285 /* Set and show command processors */
 286 
 287 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 288 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 289 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 290 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 291 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
 292 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
 293 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 294 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 295 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 296 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 297 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 298 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 299 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 300 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 301 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 302 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 303 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 304 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 305 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
 306 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 307 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 308 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 309 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 310 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 311 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 312 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 313 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
 314 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
 315 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
 316 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
 317 t_stat sim_save (FILE *sfile);
 318 t_stat sim_rest (FILE *rfile);
 319 
 320 /* Breakpoint package */
 321 
 322 t_stat sim_brk_init (void);
 323 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
 324 t_stat sim_brk_clr (t_addr loc, int32 sw);
 325 t_stat sim_brk_clrall (int32 sw);
 326 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
 327 t_stat sim_brk_showall (FILE *st, uint32 sw);
 328 CONST char *sim_brk_getact (char *buf, int32 size);
 329 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
 330 char *sim_brk_clract (void);
 331 
 332 FILE *stdnul;
 333 
 334 /* Command support routines */
 335 
 336 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 337 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 338 int32 test_search (t_value *val, SCHTAB *schptr);
 339 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
 340 int32 get_switches (const char *cptr);
 341 CONST char *get_sim_sw (CONST char *cptr);
 342 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
 343 t_value get_rval (REG *rptr, uint32 idx);
 344 void put_rval (REG *rptr, uint32 idx, t_value val);
 345 void fprint_help (FILE *st);
 346 void fprint_stopped (FILE *st, t_stat r);
 347 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
 348 void fprint_sep (FILE *st, int32 *tokens);
 349 char *read_line (char *ptr, int32 size, FILE *stream);
 350 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
 351 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
 352 char *sim_trim_endspc (char *cptr);
 353 
 354 /* Forward references */
 355 
 356 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
 357 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
 358 t_bool qdisable (DEVICE *dptr);
 359 t_stat attach_err (UNIT *uptr, t_stat stat);
 360 t_stat detach_all (int32 start_device, t_bool shutdown);
 361 t_stat assign_device (DEVICE *dptr, const char *cptr);
 362 t_stat deassign_device (DEVICE *dptr);
 363 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
 364 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
 365     REG *lowr, REG *highr, uint32 lows, uint32 highs);
 366 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
 367 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
 368 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
 369     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
 370 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
 371 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
 372     UNIT *uptr, int32 dfltinc);
 373 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
 374 t_stat step_svc (UNIT *ptr);
 375 t_stat expect_svc (UNIT *ptr);
 376 t_stat set_on (int32 flag, CONST char *cptr);
 377 t_stat set_verify (int32 flag, CONST char *cptr);
 378 t_stat set_message (int32 flag, CONST char *cptr);
 379 t_stat set_quiet (int32 flag, CONST char *cptr);
 380 t_stat set_localopc (int32 flag, CONST char *cptr);
 381 t_stat set_asynch (int32 flag, CONST char *cptr);
 382 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 383 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
 384 void int_handler (int signal);
 385 t_stat set_prompt (int32 flag, CONST char *cptr);
 386 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
 387 t_stat sim_set_environment (int32 flag, CONST char *cptr);
 388 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
 389 
 390 /* Global data */
 391 
 392 DEVICE *sim_dflt_dev             = NULL;
 393 UNIT *sim_clock_queue            = QUEUE_LIST_END;
 394 int32 sim_interval               = 0;
 395 int32 sim_switches               = 0;
 396 FILE *sim_ofile                  = NULL;
 397 SCHTAB *sim_schrptr              = NULL;
 398 SCHTAB *sim_schaptr              = NULL;
 399 DEVICE *sim_dfdev                = NULL;
 400 UNIT *sim_dfunit                 = NULL;
 401 DEVICE **sim_internal_devices    = NULL;
 402 uint32 sim_internal_device_count = 0;
 403 int32 sim_opt_out                = 0;
 404 int32 sim_is_running             = 0;
 405 t_bool sim_processing_event      = FALSE;
 406 uint32 sim_brk_summ              = 0;
 407 uint32 sim_brk_types             = 0;
 408 BRKTYPTAB *sim_brk_type_desc     = NULL;         /* type descriptions */
 409 uint32 sim_brk_dflt              = 0;
 410 uint32 sim_brk_match_type;
 411 t_addr sim_brk_match_addr;
 412 char *sim_brk_act[MAX_DO_NEST_LVL];
 413 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
 414 BRKTAB **sim_brk_tab             = NULL;
 415 int32 sim_brk_ent                = 0;
 416 int32 sim_brk_lnt                = 0;
 417 int32 sim_brk_ins                = 0;
 418 int32 sim_iglock                 = 0;
 419 int32 sim_nolock                 = 0;
 420 int32 sim_quiet                  = 0;
 421 int32 sim_localopc               = 1;
 422 int32 sim_randompst              = 0;
 423 int32 sim_randstate              = 0;
 424 int32 sim_step                   = 0;
 425 int nodist                       = 0;
 426 #if defined(PERF_STRIP)
 427 int32 sim_nostate                = 1;
 428 #else
 429 int32 sim_nostate                = 0;
 430 #endif /* if defined(PERF_STRIP) */
 431 static double sim_time;
 432 static uint32 sim_rtime;
 433 static int32 noqueue_time;
 434 volatile int32 stop_cpu          = 0;
 435 t_value *sim_eval                = NULL;
 436 static t_value sim_last_val;
 437 static t_addr sim_last_addr;
 438 FILE *sim_log                    = NULL;         /* log file */
 439 FILEREF *sim_log_ref             = NULL;         /* log file file reference */
 440 FILE *sim_deb                    = NULL;         /* debug file */
 441 FILEREF *sim_deb_ref             = NULL;         /* debug file file reference */
 442 int32 sim_deb_switches           = 0;            /* debug switches */
 443 struct timespec sim_deb_basetime;                /* debug timestamp relative base time */
 444 char *sim_prompt                 = NULL;         /* prompt string */
 445 static FILE *sim_gotofile;                       /* the currently open do file */
 446 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];   /* the current line number in the currently open do file */
 447 static int32 sim_do_echo         = 0;            /* the echo status of the currently open do file */
 448 static int32 sim_show_message    = 1;            /* the message display status of the currently open do file */
 449 static int32 sim_on_inherit      = 0;            /* the inherit status of on state and conditions when executing do files */
 450 static int32 sim_do_depth        = 0;
 451 
 452 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
 453 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
 454 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
 455 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
 456 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
 457 
 458 t_stat sim_last_cmd_stat;                        /* Command Status */
 459 
 460 static SCHTAB sim_stabr;                         /* Register search specifier */
 461 static SCHTAB sim_staba;                         /* Memory search specifier */
 462 
 463 static UNIT sim_step_unit   = { UDATA (&step_svc, 0, 0)  };
 464 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
 465 
 466 /* Tables and strings */
 467 
 468 const char save_vercur[] = "V4.1";
 469 const char save_ver40[]  = "V4.0";
 470 const char save_ver35[]  = "V3.5";
 471 const char save_ver32[]  = "V3.2";
 472 const char save_ver30[]  = "V3.0";
 473 const struct scp_error {
 474     const char *code;
 475     const char *message;
 476     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
 477         {{"NXM",     "Address space exceeded"},
 478          {"UNATT",   "Unit not attached"},
 479          {"IOERR",   "I/O error"},
 480          {"CSUM",    "Checksum error"},
 481          {"FMT",     "Format error"},
 482          {"NOATT",   "Unit not attachable"},
 483          {"OPENERR", "File open error"},
 484          {"MEM",     "Memory exhausted"},
 485          {"ARG",     "Invalid argument"},
 486          {"STEP",    "Step expired"},
 487          {"UNK",     "Unknown command"},
 488          {"RO",      "Read only argument"},
 489          {"INCOMP",  "Command not completed"},
 490          {"STOP",    "Simulation stopped"},
 491          {"EXIT",    "Goodbye"},
 492          {"TTIERR",  "Console input I/O error"},
 493          {"TTOERR",  "Console output I/O error"},
 494          {"EOF",     "End of file"},
 495          {"REL",     "Relocation error"},
 496          {"NOPARAM", "No settable parameters"},
 497          {"ALATT",   "Unit already attached"},
 498          {"TIMER",   "Hardware timer error"},
 499          {"SIGERR",  "Signal handler setup error"},
 500          {"TTYERR",  "Console terminal setup error"},
 501          {"SUB",     "Subscript out of range"},
 502          {"NOFNC",   "Command not allowed"},
 503          {"UDIS",    "Unit disabled"},
 504          {"NORO",    "Read only operation not allowed"},
 505          {"INVSW",   "Invalid switch"},
 506          {"MISVAL",  "Missing value"},
 507          {"2FARG",   "Too few arguments"},
 508          {"2MARG",   "Too many arguments"},
 509          {"NXDEV",   "Non-existent device"},
 510          {"NXUN",    "Non-existent unit"},
 511          {"NXREG",   "Non-existent register"},
 512          {"NXPAR",   "Non-existent parameter"},
 513          {"NEST",    "Nested DO command limit exceeded"},
 514          {"IERR",    "Internal error"},
 515          {"MTRLNT",  "Invalid magtape record length"},
 516          {"LOST",    "Console Telnet connection lost"},
 517          {"TTMO",    "Console Telnet connection timed out"},
 518          {"STALL",   "Console Telnet output stall"},
 519          {"AFAIL",   "Assertion failed"},
 520          {"INVREM",  "Invalid remote console command"},
 521          {"NOTATT",  "Not attached"},
 522          {"EXPECT",  "Expect matched"},
 523          {"REMOTE",  "Remote console command"},
 524     };
 525 
 526 const size_t size_map[] = { sizeof (int8),
 527     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
 528     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
 529 };
 530 
 531 const t_value width_mask[] = { 0,
 532     0x1, 0x3, 0x7, 0xF,
 533     0x1F, 0x3F, 0x7F, 0xFF,
 534     0x1FF, 0x3FF, 0x7FF, 0xFFF,
 535     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
 536     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
 537     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
 538     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
 539     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
 540     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
 541     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
 542     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
 543     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
 544     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
 545     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
 546     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
 547     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
 548     };
 549 
 550 static const char simh_help[] =
 551        /***************** 80 character line width template *************************/
 552       "1Commands\n"
 553 #define HLP_RESET       "*Commands Resetting Devices"
 554        /***************** 80 character line width template *************************/
 555       "2Resetting Devices\n"
 556       " The `RESET` command (*abbreviated* `RE`) resets a device or the entire\n"
 557       " simulator to a predefined condition.  If the switch \"`-p`\" is specified,\n"
 558       " the device is reset to its initial power-on state:\n\n"
 559       "++RESET                  resets all devices\n"
 560       "++RESET -p               power-cycle all devices\n"
 561       "++RESET ALL              resets all devices\n"
 562       "++RESET <device>         resets the specified <device>\n\n"
 563       " * Typically, `RESET` *aborts* in-progress I/O operations, *clears* any\n"
 564       " interrupt requests, and returns the device to a quiescent state.\n\n"
 565       " * It does **NOT** clear the main memory or affect associated I/O\n"
 566       " connections.\n"
 567 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
 568 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
 569 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
 570 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
 571        /***************** 80 character line width template *************************/
 572       "2Examining and Changing State\n"
 573       " There are four commands to examine and change state:\n\n"
 574       " * `EXAMINE` (*abbreviated* `E`) examines state\n"
 575       " * `DEPOSIT` (*abbreviated* `D`) changes state\n"
 576       " * `IEXAMINE` (\"interactive examine\", *abbreviated* `IE`) examines state\n"
 577       "    and allows the user to interactively change it\n"
 578       " * `IDEPOSIT` (interactive deposit, *abbreviated* `ID`) allows the user to\n"
 579       "    interactively change state\n\n"
 580       " All four commands take the form:\n\n"
 581       "++command {modifiers} <object list>\n\n"
 582       " The `DEPOSIT` command requires the deposit value at the end of the command.\n\n"
 583       " There are four kinds of modifiers: **switches**, **device/unit name**,\n"
 584       " **search specifier**, and for `EXAMINE`, **output file**.\n\n"
 585       " * **Switches** have been described previously.\n"
 586       " * A **device/unit name** identifies the device and unit whose address\n"
 587       " space is to be examined or modified. If no device is specified, the CPU\n"
 588       " main memory is selected. If a device but no unit is specified, unit `0`\n"
 589       " of the specified device is selected automatically.\n"
 590       " * The **search specifier** provides criteria for testing addresses or\n"
 591       " registers to see if they should be processed.  The search specifier\n"
 592       " consists of a \"<`logical operator`>\", a \"<`relational operator`>\", or\n"
 593       " both, optionally separated by spaces:\n\n"
 594       "++{ < logical op >  < value > }  < relational op >  < value >\n\n"
 595        /***************** 80 character line width template *************************/
 596       " * * The \"<`logical operator`>\" may be \"`&`\" (*and*), \"`|`\" (*or*),\n"
 597       " or \"`^`\" (*exclusive or*), and the \"<`relational operator`>\" may\n"
 598       " be \"`=`\" or \"`==`\" (*equal*), \"`!`\" or \"`!=`\" (*not\n"
 599       " equal*), \">=\" (*greater than or equal*), \">\" (*greater\n"
 600       " than*), \"<=\" (*less than or equal*), or \"<\" (*less than*).\n"
 601       " * * If any \"<`logical operator`>\" is specified without\n"
 602       " a \"<`relational operator`>\", it is ignored.\n"
 603       " * * If any \"<`relational operator`>\" is specified without\n"
 604       " a \"<`logical operator`>\", no logical operation is performed.\n"
 605       " * * All comparisons are unsigned.\n\n"
 606       " * The **output file** modifier redirects the command output to a file\n"
 607       " instead of the console.  The **output file** modifier is specified with\n"
 608       " the \"`@`\" (*commercial-at*) character, followed by a valid file name.\n\n"
 609       " **NOTE**: Modifiers may be specified in any order.  If multiple\n"
 610       " modifiers of the same type are specified, later modifiers override earlier\n"
 611       " modifiers. If the **device/unit name** comes *after* the search specifier,\n"
 612       " the search values will interpreted in the *radix of the CPU*, rather than\n"
 613       " of the device/unit.\n\n"
 614       " The \"<`object list`>\" argument consists of one or more of the following,\n"
 615       " separated by commas:\n\n"
 616        /***************** 80 character line width template *************************/
 617       "++register                the specified register\n"
 618       "++register[sub1-sub2]     the specified register array locations,\n"
 619       "++++++++                  starting at location sub1 up to and\n"
 620       "++++++++                  including location sub2\n"
 621       "++register[sub1/length]   the specified register array locations,\n"
 622       "++++++++                  starting at location sub1 up to but\n"
 623       "++++++++                  not including sub1+length\n"
 624       "++register[ALL]           all locations in the specified register\n"
 625       "++++++++                  array\n"
 626       "++register1-register2     all the registers starting at register1\n"
 627       "++++++++                  up to and including register2\n"
 628       "++address                 the specified location\n"
 629       "++address1-address2       all locations starting at address1 up to\n"
 630       "++++++++                  and including address2\n"
 631       "++address/length          all location starting at address up to\n"
 632       "++++++++                  but not including address+length\n"
 633       "++STATE                   all registers in the device\n"
 634       "++ALL                     all locations in the unit\n"
 635       "++$                       the last value displayed by an EXAMINE\n"
 636       "++++++++                  command interpreted as an address\n"
 637       "3Switches\n"
 638       "4Formatting Control\n"
 639       " Switches can be used to control the format of the displayed information:\n\n"
 640        /***************** 80 character line width template *************************/
 641       "5-a\n"
 642       " display as ASCII\n"
 643       "5-c\n"
 644       " display as character string\n"
 645       "5-m\n"
 646       " display as instruction mnemonics\n"
 647       "5-o\n"
 648       " display as octal\n"
 649       "5-d\n"
 650       " display as decimal\n"
 651       "5-h\n"
 652       " display as hexadecimal\n\n"
 653       "3Examples\n"
 654       "++ex 1000-1100                examine 1000 to 1100\n"
 655       "++de PC 1040                  set PC to 1040\n"
 656       "++ie 40-50                    interactively examine 40:50\n"
 657       "++ie >1000 40-50              interactively examine the subset\n"
 658       "+++++++++                     of locations 40:50 that are >1000\n"
 659       "++ex rx0 50060                examine 50060, RX unit 0\n"
 660       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\n"
 661       "++de all 0                    set main memory to 0\n"
 662       "++de &77>0 0                  set all addresses whose low order\n"
 663       "+++++++++                     bits are non-zero to 0\n"
 664       "++ex -m @memdump.txt 0-7777   dump memory to file\n\n"
 665       " * **NOTE**: To terminate an interactive command, simply type any bad value\n"
 666       "           (*e.g.* `XYZ`) when input is requested.\n"
 667 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
 668        /***************** 80 character line width template *************************/
 669       "2Evaluating Instructions\n"
 670       " The `EVAL` command evaluates a symbolic expression and returns the\n"
 671       " equivalent numeric value.\n\n"
 672        /***************** 80 character line width template *************************/
 673       "2Running A Simulated Program\n"
 674 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
 675       "3RUN\n"
 676       " The `RUN` command (*abbreviated* `RU`) resets all devices, deposits its\n"
 677       " argument, if given, in the PC (program counter), and starts execution.\n"
 678       " If no argument is given execution starts at the current PC.\n"
 679 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
 680       "3GO\n"
 681       " The `GO` command does *not* reset devices, deposits its argument (if\n"
 682       " given) in the PC, and starts execution.  If no argument is given,\n"
 683       " execution starts at the current PC (program counter).\n"
 684 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program Continuing_Execution"
 685       "3Continuing Execution\n"
 686       " The `CONTINUE` command (*abbreviated* `CONT` or `CO`) resumes execution\n"
 687       " (if execution was stopped, possibly due to hitting a breakpoint) at the\n"
 688       " current program counter without resetting any devices.\n"
 689 #define HLP_STEP        "*Commands Running_A_Simulated_Program Step_Execution"
 690       "3Step Execution\n"
 691       " The `STEP` command (*abbreviated* `S`) resumes execution at the current\n"
 692       " PC for the number of instructions given by its argument.  If no argument\n"
 693       " is supplied, one instruction is executed.\n"
 694       "4Switches\n"
 695       "5`-T`\n"
 696       " If the `STEP` command is invoked with the \"`-T`\" switch, the step\n"
 697       " command will cause execution to run for *microseconds* rather than\n"
 698       " instructions.\n"
 699 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
 700       "3NEXT\n"
 701       " The `NEXT` command (*abbreviated* `N`) resumes execution at the current PC\n"
 702       " for one instruction, attempting to execute *through* subroutine calls.\n"
 703       " If the next instruction to be executed is *not* a subroutine call, then\n"
 704       " one instruction is executed.\n"
 705 #define HLP_BOOT        "*Commands Running_A_Simulated_Program Booting_the_system"
 706       "3Booting the system\n"
 707       " The `BOOT` command (*abbreviated* `BO`) resets all devices and bootstraps\n"
 708       " the device and unit given by its argument. If no unit is supplied,\n"
 709       " unit `0` is bootstrapped.  The specified unit must be `ATTACH`'ed.\n\n"
 710       " When booting Multics, the boot device should always be `iom0`.\n"
 711       " Assuming a tape is attached to the `tape0` device, it will be bootstrapped\n"
 712       " into memory and the system will transfer control to the boot record.\n\n"
 713       " **Example**\n\n"
 714       "++; Boot Multics using iom0\n"
 715       "++boot iom0\n\n"
 716        /***************** 80 character line width template *************************/
 717       "2Stopping The Simulator\n"
 718       " The simulator runs until the simulated hardware encounters an error, or\n"
 719       " until the user forces a stop condition.\n"
 720       "3Simulator Detected Stop Conditions\n"
 721       " These simulator-detected conditions stop simulation:\n\n"
 722       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\n\n"
 723       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\n"
 724       "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
 725       "+++usually stops.\n\n"
 726       "++-  Processor condition.  Certain processor conditions can stop\n"
 727       "+++the simulation.\n"
 728       "3User Specified Stop Conditions\n"
 729       " Typing the interrupt character stops simulation.  The interrupt character\n"
 730       " is defined by the `WRU` (*Where aRe yoU*) console option, and is initially\n"
 731       " set to `005` (`^E`).\n\n"
 732        /***************** 80 character line width template *************************/
 733 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 734 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 735       "4Breakpoints\n"
 736       " The simulator offers breakpoint capability for debugging. Users may define\n"
 737       " breakpoints of different types, identified by letter (for example, `E`\n"
 738       " for *execution*, `R` for *read*, `W` for *write*, etc).\n\n"
 739       " Associated with each breakpoint is a count and, optionally, one or more\n"
 740       " actions.  Each time a breakpoint occurs, the associated count\n"
 741       " is *decremented*.  If the count is less than or equal to `0`, the breakpoint\n"
 742       " occurs; otherwise, it is deferred.  When the breakpoint occurs, any\n"
 743       " optional actions are automatically executed.\n\n"
 744       " A breakpoint is set by the `BREAK` (or `SET BREAK`) command:\n\n"
 745       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
 746       " If no type is specified, the default breakpoint type (`E`, *execution*) is\n"
 747       " used.  If no address range is specified, the current PC is used.  As\n"
 748       " with `EXAMINE` and `DEPOSIT`, an address range may be a single address, a\n"
 749       " range of addresses low-high, or a relative range of address/length.\n"
 750        /***************** 80 character line width template *************************/
 751       "5Displaying Breakpoints\n"
 752       " Currently set breakpoints can be displayed with the `SHOW BREAK` command:\n\n"
 753       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
 754       " Locations with breakpoints of the specified type are displayed.\n\n"
 755       " The \"`-C`\" switch displays the selected breakpoint(s) formatted as\n"
 756       " commands which may be subsequently used to establish the same\n"
 757       " breakpoint(s).\n\n"
 758       "5Removing Breakpoints\n"
 759       " Breakpoints can be cleared by the `NOBREAK` or the `SET NOBREAK` commands.\n"
 760       "5Examples\n"
 761       " The following examples illustrate breakpoint usage:\n\n"
 762       "++BREAK                      set E break at current PC\n"
 763       "++BREAK -e 200               set E break at 200\n"
 764       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\n"
 765       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\n"
 766       "+++++++++D MQ 0\n"
 767       "++BREAK 100;                 delete action on break at 100\n\n"
 768        /***************** 80 character line width template *************************/
 769       "2Connecting and Disconnecting Devices\n"
 770       " Units are simulated as files on the host file system.  Before using any\n"
 771       " simulated unit, the user must specify the file to be accessed by that unit.\n"
 772 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices Attaching_devices"
 773       "3Attaching devices\n"
 774       " The `ATTACH` (*abbreviation* `AT`) command associates a unit and a file:\n\n"
 775       "++ATTACH <unit> <filename>\n\n"
 776       " Some devices have more detailed or specific help available with:\n\n"
 777       "++HELP <device> ATTACH\n\n"
 778       "4Switches\n"
 779       "5-n\n"
 780       " If the \"`-n`\" switch is specified when `ATTACH` is executed, a new\n"
 781       " file will be created when the filename specified does not exist, or an\n"
 782       " existing file will have it's size truncated to zero, and an appropriate\n"
 783       " message is printed.\n"
 784       "5-e\n"
 785       " If the file does not exist, and the \"`-e`\" switch *was not* specified,\n"
 786       " a new file is created, and an appropriate message is printed.  If\n"
 787       " the \"`-e`\" switch *was* specified, a new file is *not* created, and an\n"
 788       " error message is printed.\n"
 789       "5-r\n"
 790       " If the \"`-r`\" switch is specified, or the file is write protected by\n"
 791       " host operating system, `ATTACH` tries to open the file in read only mode.\n"
 792       " If the file does not exist, or the unit does not support read only\n"
 793       " operation, an error occurs.  Input-only devices, such as card readers, or\n"
 794       " storage devices with write locking switches, such as disks or tapes,\n"
 795       " support read only operation - other devices do not.  If a file is\n"
 796       " attached read only, its contents can be examined but not modified.\n"
 797       "5-q\n"
 798       " If the \"`-q`\" switch is specified when creating a new file (\"`-n`\")\n"
 799       " or opening one read only (\"`-r`\"), the message announcing this fact\n"
 800       " is suppressed.\n"
 801       "5-f\n"
 802       " For simulated magnetic tapes, the `ATTACH` command can specify the format\n"
 803       " of the attached tape image file:\n\n"
 804       "++ATTACH -f <tape_unit> <format> <filename>\n\n"
 805       " * The currently supported magnetic tape image file formats are:\n\n"
 806       " |                  |                                                      |\n"
 807       " | ----------------:|:---------------------------------------------------- |\n"
 808       " | \"**`SIMH`**\"   | The **SIMH** / **DPS8M** native portable tape format |\n"
 809       " | \"**`E11`**\"    | The *D Bit* **Ersatz-11** simulator format           |\n"
 810       " | \"**`TPC`**\"    | The **TPC** format (*used by _SIMH_ prior to V2.3*)  |\n"
 811       " | \"**`P7B`**\"    | The **Paul Pierce** `7`-track tape archive format    |\n\n"
 812        /***************** 80 character line width template *************************/
 813       " * The default tape format can also be specified with the `SET` command\n"
 814       " prior to using the `ATTACH` command:\n\n"
 815       "++SET <tape_unit> FORMAT=<format>\n"
 816       "++ATTACH <tape_unit> <filename>\n\n"
 817       " * The format of a currently attached tape image can be displayed with\n"
 818       "   the `SHOW FORMAT` command:\n\n"
 819       "++SHOW <unit> FORMAT\n\n"
 820       " **Examples**\n\n"
 821       " The following example illustrates common `ATTACH` usage:\n"
 822       "++; Associate the tape image file \"12.7MULTICS.tap\" with the tape0 unit\n"
 823       "++; in read-only mode, where tape0 corresponds to the first tape device.\n"
 824       "++ATTACH -r tape0 12.7MULTICS.tap\n\n"
 825       "++; Associate the disk image file \"root.dsk\" with the disk0 unit.\n"
 826       "++; The disk0 unit corresponds to the first disk device.\n"
 827       "++ATTACH disk0 root.dsk\n\n"
 828        /***************** 80 character line width template *************************/
 829 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices Detaching_devices"
 830       "3Detaching devices\n"
 831       " The `DETACH` (*abbreviation* `DET`) command breaks the association between\n"
 832       " a unit and its backing file or device:\n\n"
 833       "++DETACH ALL             Detach all units\n"
 834       "++DETACH <unit>          Detach specified unit\n\n"
 835       " * **NOTE:** The `EXIT` command performs an automatic `DETACH ALL`.\n"
 836 #define HLP_SET         "*Commands SET"
 837       "2SET\n"
 838        /***************** 80 character line width template *************************/
 839 #define HLP_SET_LOG    "*Commands SET Logging"
 840       "3Logging\n"
 841       " Interactions with the simulator session can be recorded to a log file.\n\n"
 842       "+SET LOG log_file            Specify the log destination\n"
 843       "++++++++                     (STDOUT, DEBUG, or filename)\n"
 844       "+SET NOLOG                   Disables any currently active logging\n"
 845       "4Switches\n"
 846       "5`-N`\n"
 847       " By default, log output is written at the *end* of the specified log file.\n"
 848       " A new log file can created if the \"`-N`\" switch is used on the command\n"
 849       " line.\n\n"
 850       "5`-B`\n"
 851       " By default, log output is written in *text* mode.  The log file can be\n"
 852       " opened for *binary* mode writing if the \"`-B`\" switch is used on the\n"
 853       " command line.\n"
 854 #define HLP_SET_DEBUG  "*Commands SET Debug_Messages"
 855        /***************** 80 character line width template *************************/
 856       "3Debug Messages\n"
 857       "+SET DEBUG debug_file        Specify the debug destination\n"
 858       "++++++++                     (STDOUT, STDERR, LOG, or filename)\n"
 859       "+SET NODEBUG                 Disables any currently active debug output\n"
 860       "4Switches\n"
 861       " Debug message output contains a timestamp which indicates the number of\n"
 862       " simulated instructions which have been executed prior to the debug event.\n\n"
 863       " Debug message output can be enhanced to contain additional, potentially\n"
 864       " useful information.\n\n\n"
 865       " **NOTE**: If neither \"`-T`\" or \"`-A`\" is specified, \"`-T`\" is implied.\n"
 866       "5-T\n"
 867       " The \"`-T`\" switch causes debug output to contain a time of day displayed\n"
 868       " as `hh:mm:ss.msec`.\n"
 869       "5-A\n"
 870       " The \"`-A`\" switch causes debug output to contain a time of day displayed\n"
 871       " as `seconds.msec`.\n"
 872       "5-R\n"
 873       " The \"`-R`\" switch causes timing to be relative to the start of debugging.\n"
 874       "5-P\n"
 875       " The \"`-P`\" switch adds the output of the PC (program counter) to each\n"
 876       " debug message.\n"
 877       "5-N\n"
 878       " The \"`-N`\" switch causes a new (empty) file to be written to.\n"
 879       " (The default is to append to an existing debug log file).\n"
 880       "5-D\n"
 881       " The \"`-D`\" switch causes data blob output to also display the data\n"
 882       " as **`RADIX-50`** characters.\n"
 883       "5-E\n"
 884       " The \"`-E`\" switch causes data blob output to also display the data\n"
 885       " as \"**EBCDIC**\" characters.\n"
 886        /***************** 80 character line width template *************************/
 887 #define HLP_SET_ENVIRON "*Commands SET Environment_Variables"
 888       "3Environment Variables\n"
 889       "+SET ENVIRONMENT NAME=val    Set environment variable\n"
 890       "+SET ENVIRONMENT NAME        Clear environment variable\n"
 891 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
 892       "3Command Status Trap Dispatching\n"
 893       "+SET ON                      Enables error checking command execution\n"
 894       "+SET NOON                    Disables error checking command execution\n"
 895       "+SET ON INHERIT              Enables inheritance of ON state and actions\n"
 896       "+SET ON NOINHERIT            Disables inheritance of ON state and actions\n"
 897 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
 898       "3Command Execution Display\n"
 899       "+SET VERIFY                  Enables display of processed script commands\n"
 900       "+SET VERBOSE                 Enables display of processed script commands\n"
 901       "+SET NOVERIFY                Disables display of processed script commands\n"
 902       "+SET NOVERBOSE               Disables display of processed script commands\n"
 903 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
 904       "3Command Error Status Display\n"
 905       "+SET MESSAGE                 Re-enables display of script error messages\n"
 906       "+SET NOMESSAGE               Disables display of script error messages\n"
 907 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
 908       "3Command Output Display\n"
 909       "+SET QUIET                   Disables suppression of some messages\n"
 910       "+SET NOQUIET                 Re-enables suppression of some messages\n"
 911 #define HLP_SET_LOCALOPC "*Commands SET Local_Operator_Console"
 912       "3Local Operator Console\n"
 913       "+SET LOCALOPC                Enables local operator console\n"
 914       "+SET NOLOCALOPC              Disables local operator console\n"
 915 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
 916       "3Command Prompt\n"
 917       "+SET PROMPT \"string\"         Sets an alternate simulator prompt string\n"
 918       "3Device and Unit Settings\n"
 919       "+SET <dev> OCT|DEC|HEX       Set device display radix\n"
 920       "+SET <dev> ENABLED           Enable device\n"
 921       "+SET <dev> DISABLED          Disable device\n"
 922       "+SET <dev> DEBUG{=arg}       Set device debug flags\n"
 923       "+SET <dev> NODEBUG={arg}     Clear device debug flags\n"
 924       "+SET <dev> arg{,arg...}      Set device parameters\n"
 925       "+SET <unit> ENABLED          Enable unit\n"
 926       "+SET <unit> DISABLED         Disable unit\n"
 927       "+SET <unit> arg{,arg...}     Set unit parameters\n"
 928       "+HELP <dev> SET              Displays any device specific SET commands\n"
 929       " \n\n"
 930       " See the Omnibus documentation for a complete SET command reference.\n"
 931        /***************** 80 character line width template *************************/
 932 #define HLP_SHOW        "*Commands SHOW"
 933       "2SHOW\n"
 934       "+SH{OW} B{UILDINFO}               Show build-time compilation information\n"
 935       "+SH{OW} CL{OCKS}                  Show wall clock and timer information\n"
 936       "+SH{OW} C{ONFIGURATION}           Show simulator configuration\n"
 937       "+SH{OW} D{EFAULT_BASE_SYSTEM}     Show default base system script\n"
 938       "+SH{OW} DEV{ICES}                 Show devices\n"
 939       "+SH{OW} M{ODIFIERS}               Show SET commands for all devices\n"
 940       "+SH{OW} O{N}                      Show ON condition actions\n"
 941       "+SH{OW} P{ROM}                    Show CPU ID PROM initialization data\n"
 942       "+SH{OW} Q{UEUE}                   Show event queue\n"
 943       "+SH{OW} S{HOW}                    Show SHOW commands for all devices\n"
 944       "+SH{OW} T{IME}                    Show simulated timer\n"
 945       "+SH{OW} VE{RSION}                 Show simulator version\n"
 946       "+H{ELP} <dev> SHOW                Show device-specific SHOW commands\n"
 947       "+SH{OW} <dev> {arg,...}           Show device parameters\n"
 948       "+SH{OW} <dev> DEBUG               Show device debug flags\n"
 949       "+SH{OW} <dev> MODIFIERS           Show device modifiers\n"
 950       "+SH{OW} <dev> RADIX               Show device display radix\n"
 951       "+SH{OW} <dev> SHOW                Show device SHOW commands\n"
 952       "+SH{OW} <unit> {arg,...}          Show unit parameters\n\n"
 953       " See the Omnibus documentation for a complete SHOW command reference.\n\n"
 954 #define HLP_SHOW_CONFIG         "*Commands SHOW"
 955 #define HLP_SHOW_DEVICES        "*Commands SHOW"
 956 #define HLP_SHOW_FEATURES       "*Commands SHOW"
 957 #define HLP_SHOW_QUEUE          "*Commands SHOW"
 958 #define HLP_SHOW_TIME           "*Commands SHOW"
 959 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
 960 #define HLP_SHOW_NAMES          "*Commands SHOW"
 961 #define HLP_SHOW_SHOW           "*Commands SHOW"
 962 #define HLP_SHOW_VERSION        "*Commands SHOW"
 963 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
 964 #define HLP_SHOW_PROM           "*Commands SHOW"
 965 #define HLP_SHOW_DBS            "*Commands SHOW"
 966 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
 967 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
 968 #define HLP_SHOW_REMOTE         "*Commands SHOW"
 969 #define HLP_SHOW_BREAK          "*Commands SHOW"
 970 #define HLP_SHOW_LOG            "*Commands SHOW"
 971 #define HLP_SHOW_DEBUG          "*Commands SHOW"
 972 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
 973 #define HLP_SHOW_ON             "*Commands SHOW"
 974 #define HLP_SHOW_SEND           "*Commands SHOW"
 975 #define HLP_SHOW_EXPECT         "*Commands SHOW"
 976 #define HLP_HELP                "*Commands HELP"
 977        /***************** 80 character line width template *************************/
 978       "2HELP\n"
 979       "+H{ELP}                      Show this message\n"
 980       "+H{ELP} <command>            Show help for command\n"
 981       "+H{ELP} <dev>                Show help for device\n"
 982       "+H{ELP} <dev> REGISTERS      Show help for device register variables\n"
 983       "+H{ELP} <dev> ATTACH         Show help for device specific ATTACH command\n"
 984       "+H{ELP} <dev> SET            Show help for device specific SET commands\n"
 985       "+H{ELP} <dev> SHOW           Show help for device specific SHOW commands\n"
 986       "+H{ELP} <dev> <command>      Show help for device specific <command> command\n"
 987        /***************** 80 character line width template *************************/
 988       "2Altering The Simulated Configuration\n"
 989       " The \"SET <device> DISABLED\" command removes a device from the configuration.\n"
 990       " A `DISABLED` device is invisible to running programs.  The device can still\n"
 991       " be `RESET`, but it cannot be `ATTACH`ed, `DETACH`ed, or `BOOT`ed.\n\n"
 992       " The \"SET <device> ENABLED\" command restores a disabled device to a\n"
 993       " configuration.\n\n"
 994       " Most multi-unit devices allow units to be enabled or disabled:\n\n"
 995       "++SET <unit> ENABLED\n"
 996       "++SET <unit> DISABLED\n\n"
 997       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
 998        /***************** 80 character line width template *************************/
 999 #define HLP_DO          "*Commands Executing_Command_Files Processing_Command_Files"
1000       "2Executing Command Files\n"
1001       "3Processing Command Files\n"
1002       " The simulator can invoke another script file with the \"`DO`\" command:\n\n"
1003       "++DO <filename> {arguments...}       execute commands in specified file\n\n"
1004       " The \"`DO`\" command allows command files to contain substitutable\n"
1005       " arguments. The string \"`%%n`\", where \"`n`\" is a number\n"
1006       " between \"`1`\" and \"`9`\", is replaced with argument \"`n`\" from\n"
1007       " the \"`DO`\" command line. (*i.e.* \"`%%0`\", \"`%%1`\", \"`%%2`\", etc.).\n"
1008       " The string \"`%%0`\" is replaced with \"<`filename`>\".\n The\n"
1009       " sequences \"`\\%%`\" and \"`\\\\`\" are replaced with the literal\n"
1010       " characters \"`%%`\" and \"`\\`\", respectively. Arguments with spaces must\n"
1011       " be enclosed in matching single or double quotation marks.\n\n"
1012       " * **NOTE**: Nested \"`DO`\" commands are supported, up to ten invocations\n"
1013       " deep.\n\n"
1014       "4Switches\n"
1015       "5`-v`\n\n"
1016       " If the switch \"`-v`\" is specified, commands in the command file are\n"
1017       " echoed *before* they are executed.\n\n"
1018       "5`-e`\n\n"
1019       " If the switch \"`-e`\" is specified, command processing (including nested\n"
1020       " command invocations) will be aborted if any command error is encountered.\n"
1021       " (A simulation stop **never** aborts processing; use `ASSERT` to catch\n"
1022       " unexpected stops.) Without this switch, all errors except `ASSERT` failures\n"
1023       " will be ignored, and command processing will continue.\n\n"
1024       "5`-o`\n\n"
1025       " If the switch \"`-o`\" is specified, the `ON` conditions and actions from\n"
1026       " the calling command file will be inherited by the command file being\n"
1027       " invoked.\n"
1028       "5`-q`\n\n"
1029       " If the switch \"`-q`\" is specified, *quiet mode* will be explicitly\n"
1030       " enabled for the called command file, otherwise the *quiet mode* setting\n"
1031       " is inherited from the calling context.\n"
1032        /***************** 80 character line width template *************************/
1033 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
1034       "3GOTO\n"
1035       " Commands in a command file execute in sequence until either an error\n"
1036       " trap occurs (when a command completes with an error status), or when an\n"
1037       " explicit request is made to start command execution elsewhere with\n"
1038       " the `GOTO` command:\n\n"
1039       "++GOTO <label>\n\n"
1040       " * Labels are lines in a command file which the first non-whitespace\n"
1041       " character is a \"`:`\".\n"
1042       " * The target of a `GOTO` is the first matching label in the current `DO`\n"
1043       " command file which is encountered.\n\n"
1044       " **Example**\n\n"
1045       " The following example illustrates usage of the `GOTO` command (by\n"
1046       " creating an infinite loop):\n\n"
1047       "++:Label\n"
1048       "++:: This is a loop.\n"
1049       "++GOTO Label\n\n"
1050 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
1051        /***************** 80 character line width template *************************/
1052       "3RETURN\n"
1053       " The `RETURN` command causes the current procedure call to be restored to\n"
1054       " the calling context, possibly returning a specific return status.\n"
1055       " If no return status is specified, the return status from the last command\n"
1056       " executed will be returned.  The calling context may have `ON` traps defined\n"
1057       " which may redirect command flow in that context.\n\n"
1058       "++RETURN                 return from command file with last command status\n"
1059       "++RETURN {-Q} <status>   return from command file with specific status\n\n"
1060       " * The status return can be any numeric value or one of the standard SCPE_\n"
1061       " condition names.\n\n"
1062       " * The \"`-Q`\" switch on the `RETURN` command will cause the specified\n"
1063       " status to be returned, but normal error status message printing to be\n"
1064       " suppressed.\n\n"
1065       " **Condition Names**\n\n"
1066       " The available standard SCPE_ condition names and their meanings are:\n\n"
1067       " | Name    | Meaning                         | Name    | Meaning                             |\n"
1068       " | ------- | --------------------------------| ------- | ----------------------------------- |\n"
1069       " | NXM     | Address space exceeded          | UNATT   | Unit not attached                   |\n"
1070       " | IOERR   | I/O error                       | CSUM    | Checksum error                      |\n"
1071       " | FMT     | Format error                    | NOATT   | Unit not attachable                 |\n"
1072       " | OPENERR | File open error                 | MEM     | Memory exhausted                    |\n"
1073       " | ARG     | Invalid argument                | STEP    | Step expired                        |\n"
1074       " | UNK     | Unknown command                 | RO      | Read only argument                  |\n"
1075       " | INCOMP  | Command not completed           | STOP    | Simulation stopped                  |\n"
1076       " | EXIT    | Goodbye                         | TTIERR  | Console input I/O error             |\n"
1077       " | TTOERR  | Console output I/O error        | EOF     | End of file                         |\n"
1078       " | REL     | Relocation error                | NOPARAM | No settable parameters              |\n"
1079       " | ALATT   | Unit already attached           | TIMER   | Hardware timer error                |\n"
1080       " | SIGERR  | Signal handler setup error      | TTYERR  | Console terminal setup error        |\n"
1081       " | NOFNC   | Command not allowed             | UDIS    | Unit disabled                       |\n"
1082       " | NORO    | Read only operation not allowed | INVSW   | Invalid switch                      |\n"
1083       " | MISVAL  | Missing value                   | 2FARG   | Too few arguments                   |\n"
1084       " | 2MARG   | Too many arguments              | NXDEV   | Non-existent device                 |\n"
1085       " | NXUN    | Non-existent unit               | NXREG   | Non-existent register               |\n"
1086       " | NXPAR   | Non-existent parameter          | NEST    | Nested DO command limit exceeded    |\n"
1087       " | IERR    | Internal error                  | MTRLNT  | Invalid magtape record length       |\n"
1088       " | LOST    | Console Telnet connection lost  | TTMO    | Console Telnet connection timed out |\n"
1089       " | STALL   | Console Telnet output stall     | AFAIL   | Assertion failed                    |\n"
1090       " | INVREM  | Invalid remote console command  |         |                                     |\n"
1091       "\n\n"
1092 #define HLP_SHIFT       "*Commands Executing_Command_Files Shift_Parameters"
1093       "3Shift Parameters\n"
1094       " Shift the command files positional parameters\n"
1095 #define HLP_CALL        "*Commands Executing_Command_Files Call_a_subroutine"
1096       "3Call a subroutine\n"
1097       " Control can be transferred to a labeled subroutine using `CALL`.\n\n"
1098       " **Example**\n\n"
1099       "++CALL routine\n"
1100       "++BYE\n"
1101       "++\n"
1102       "++:routine\n"
1103       "++ECHO routine called\n"
1104       "++RETURN\n\n"
1105 #define HLP_ON          "*Commands Executing_Command_Files ON"
1106       "3ON\n"
1107       " The `ON` command performs actions after a condition, or clears a condition.\n"
1108       "++ON <condition> <action>  Perform action after condition\n"
1109       "++ON <condition>           Clears action of specified condition\n"
1110 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1111 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1112        /***************** 80 character line width template *************************/
1113       "3PROCEED or IGNORE\n"
1114       " The `PROCEED` (or `IGNORE`) command does nothing.  It is potentially\n"
1115       " useful as a placeholder for any `ON` action condition that should be\n"
1116       " explicitly ignored, allowing command file execution to continue without\n"
1117       " taking any specific action.\n"
1118 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1119        /***************** 80 character line width template *************************/
1120       "3Displaying Arbitrary Text\n"
1121       " The `ECHO` command is a useful way of annotating command files.  `ECHO`\n"
1122       " prints out its arguments to the console (and to any applicable log file):\n\n"
1123       "++ECHO <string>      Output string to console\n\n"
1124       " **NOTE**: If no arguments are specified, `ECHO` prints a blank line.\n"
1125       " This may be used to provide spacing for console messages or log file\n"
1126       " output.\n"
1127        /***************** 80 character line width template *************************/
1128 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Assertions"
1129       "3Testing Assertions\n"
1130       " The `ASSERT` command tests a simulator state condition and halts command\n"
1131       " file execution if the condition is false:\n\n"
1132       "++ASSERT <Simulator State Expressions>\n\n"
1133       " * If the indicated expression evaluates to false, the command completes\n"
1134       " with an `AFAIL` condition.  By default, when a command file encounters a\n"
1135       " command which returns the `AFAIL` condition, it will exit the running\n"
1136       " command file with the `AFAIL` status to the calling command file.  This\n"
1137       " behavior can be changed with the `ON` command as well as switches to the\n"
1138       " invoking `DO` command.\n\n"
1139       " **Examples**\n\n"
1140       " The command file below might be used to bootstrap a hypothetical system\n"
1141       " that halts after the initial load from disk. The `ASSERT` command can then\n"
1142       " be used to confirm that the load completed successfully by examining the\n"
1143       " CPU's \"`A`\" register for the expected value:\n\n"
1144       "++; Example INI file\n"
1145       "++BOOT\n"
1146       "++; A register contains error code; 0 = good boot\n"
1147       "++ASSERT A=0\n"
1148       "++RUN\n\n"
1149        /***************** 80 character line width template *************************/
1150       " * In the above example, if the \"`A`\" register is *not* `0`,\n"
1151       " the \"`ASSERT A=0`\" command will be displayed to the user, and the\n"
1152       " command file will be aborted with an \"`Assertion failed`\" message.\n"
1153       " Otherwise, the command file will continue to bring up the system.\n\n"
1154       " * See the **`IF`** command documentation for more information and details\n"
1155       " regarding simulator state expressions.\n\n"
1156 #define HLP_IF          "*Commands Executing_Command_Files Testing_Conditions"
1157       "3Testing Conditions\n"
1158       " The `IF` command tests a simulator state condition and executes additional\n"
1159       " commands if the condition is true:\n\n"
1160       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommand}...\n\n"
1161       " **Examples**\n\n"
1162       " The command file below might be used to bootstrap a hypothetical system\n"
1163       " that halts after the initial load from disk. The `IF` command can then\n"
1164       " be used to confirm that the load completed successfully by examining the\n"
1165       " CPU's \"`A`\" register for an expected value:\n\n"
1166       "++; Example INI file\n"
1167       "++BOOT\n"
1168       "++; A register contains error code; 0 = good boot\n"
1169       "++IF NOT A=0 echo Boot failed - Failure Code ; EX A; exit AFAIL\n"
1170       "++RUN\n\n"
1171        /***************** 80 character line width template *************************/
1172       " * In the above example, if the \"`A`\" register is *not* `0`, the\n"
1173       " message \"`Boot failed - Failure Code `\" will be displayed, the contents\n"
1174       " of the \"`A`\" register will be displayed, and the command file will be\n"
1175       " aborted with an \"`Assertion failed`\" message.  Otherwise, the command\n"
1176       " file will continue to bring up the system.\n"
1177       "4Conditional Expressions\n"
1178       " The `IF` and `ASSERT` commands evaluate the following two different forms\n"
1179       " of conditional expressions.\n\n"
1180       "5Simulator State Expressions\n"
1181       "  &nbsp;\n \n"
1182       " The values of simulator registers can be evaluated with:\n\n"
1183       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1184       " * If \"<`dev`>\" is not specified, `CPU` is assumed.  \"<`reg`>\" is a\n"
1185       " register belonging to the indicated device.\n"
1186       " * The \"<`addr`>\" is an address in the address space of the indicated\n"
1187       " device.\n"
1188       " * The \"<`conditional-op`>\" and optional \"<`logical-op`>\" are\n"
1189       " the same as those used for \"search specifiers\" by the `EXAMINE` and\n"
1190       " `DEPOSIT` commands.\n"
1191       " The \"<`value`>\" is expressed in the radix specified for \"<`reg`>\",\n"
1192       " not in the radix for the device when referencing a register; when an\n"
1193       " address is referenced the device radix is used as the default.\n\n"
1194       " * If \"<`logical-op`>\" and \"<`value`>\" are specified, the target\n"
1195       " register value is first altered as indicated.  The result is then compared\n"
1196       " to the \"<`value`>\" via the \"<`conditional-op`>\".\n"
1197       " * * If the result is *true*, the command(s) are executed before proceeding\n"
1198       " to the next line in the command file.\n"
1199       " * * If the result is *false*, the next command in the command file is\n"
1200       " processed.\n\n"
1201       "5String Comparison Expressions\n"
1202       "  &nbsp;\n \n"
1203       " String Values can be compared with:\n\n"
1204       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1205       " * The \"`-i`\" switch, if present, causes a comparison to be case\n"
1206       "   insensitive.\n"
1207       " * The \"<`string1`>\" and \"<`string2`>\" arguments are quoted string\n"
1208       "   values which may have environment variables substituted as desired.\n"
1209       " * The \"<`compare-op`>\" may be one of:\n\n"
1210       " |                |                        |\n"
1211       " | --------------:|:---------------------- |\n"
1212       " | \"**`==`**\"     |  equal                 |\n"
1213       " | \"**`EQU`**\"    |  equal                 |\n"
1214       " | \"**`!=`**\"     |  not equal             |\n"
1215       " | \"**`NEQ`**\"    |  not equal             |\n"
1216       " | \"**<**\"        |  less than             |\n"
1217       " | \"**`LSS`**\"    |  less than             |\n"
1218       " | \"**<=**\"       |  less than or equal    |\n"
1219       " | \"**`LEQ`**\"    |  less than or equal    |\n"
1220       " | \"**>**\"        |  greater than          |\n"
1221       " | \"**`GTR`**\"    |  greater than          |\n"
1222       " | \"**>=**\"       |  greater than or equal |\n"
1223       " | \"**`GEQ`**\"    |  greater than or equal |\n"
1224       " * **NOTE**: Comparisons are *generic*.  This means that if\n"
1225       " both \"<`string1`>\" and \"<`string2`>\" are comprised of all numeric\n"
1226       " digits, then the strings are converted to numbers and a numeric\n"
1227       " comparison is performed.  For example, the comparison\n"
1228       " '`\"+1\"` `EQU` `\"1\"`' evaluates to *true*.\n"
1229        /***************** 80 character line width template *************************/
1230 #define HLP_EXIT        "*Commands Exiting_the_Simulator"
1231       "2Exiting the Simulator\n"
1232       " The `EXIT` command (*synonyms* `QUIT` *and* `BYE`) exits the simulator,\n"
1233       " returning control to the host operating system.\n"
1234        /***************** 80 character line width template *************************/
1235 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1236       "2Executing System Commands\n"
1237       " * The simulator can execute host operating system commands with\n"
1238       " the \"`!`\" (*spawn*) command.\n\n"
1239       " |                         |                                             |\n"
1240       " |:----------------------- |:------------------------------------------- |\n"
1241       " | \"**`!`**\"             | Spawn the hosts default command interpreter |\n"
1242       " | \"**`!`** <`command`>\" | Execute the host operating system `command` |\n\n"
1243       " * **NOTE**: The *exit status* from the command which was executed is set\n"
1244       " as the *command completion status* for the \"`!`\" command.  This may\n"
1245       " influence any enabled `ON` condition traps.\n" ;
1246        /***************** 80 character line width template *************************/
1247 
1248 static CTAB cmd_table[] = {
1249     { "RESET",    &reset_cmd,  0,         HLP_RESET     },
1250     { "EXAMINE",  &exdep_cmd,  EX_E,      HLP_EXAMINE   },
1251     { "IEXAMINE", &exdep_cmd,  EX_E+EX_I, HLP_IEXAMINE  },
1252     { "DEPOSIT",  &exdep_cmd,  EX_D,      HLP_DEPOSIT   },
1253     { "IDEPOSIT", &exdep_cmd,  EX_D+EX_I, HLP_IDEPOSIT  },
1254     { "EVALUATE", &eval_cmd,   0,         HLP_EVALUATE  },
1255     { "RUN",      &run_cmd,    RU_RUN,    HLP_RUN,      NULL, &run_cmd_message },
1256     { "GO",       &run_cmd,    RU_GO,     HLP_GO,       NULL, &run_cmd_message },
1257     { "STEP",     &run_cmd,    RU_STEP,   HLP_STEP,     NULL, &run_cmd_message },
1258     { "NEXT",     &run_cmd,    RU_NEXT,   HLP_NEXT,     NULL, &run_cmd_message },
1259     { "CONTINUE", &run_cmd,    RU_CONT,   HLP_CONTINUE, NULL, &run_cmd_message },
1260     { "BOOT",     &run_cmd,    RU_BOOT,   HLP_BOOT,     NULL, &run_cmd_message },
1261     { "BREAK",    &brk_cmd,    SSH_ST,    HLP_BREAK     },
1262     { "NOBREAK",  &brk_cmd,    SSH_CL,    HLP_NOBREAK   },
1263     { "ATTACH",   &attach_cmd, 0,         HLP_ATTACH    },
1264     { "DETACH",   &detach_cmd, 0,         HLP_DETACH    },
1265     { "EXIT",     &exit_cmd,   0,         HLP_EXIT      },
1266     { "QUIT",     &exit_cmd,   0,         NULL          },
1267     { "BYE",      &exit_cmd,   0,         NULL          },
1268     { "SET",      &set_cmd,    0,         HLP_SET       },
1269     { "SHOW",     &show_cmd,   0,         HLP_SHOW      },
1270     { "DO",       &do_cmd,     1,         HLP_DO        },
1271     { "GOTO",     &goto_cmd,   1,         HLP_GOTO      },
1272     { "RETURN",   &return_cmd, 0,         HLP_RETURN    },
1273     { "SHIFT",    &shift_cmd,  0,         HLP_SHIFT     },
1274     { "CALL",     &call_cmd,   0,         HLP_CALL      },
1275     { "ON",       &on_cmd,     0,         HLP_ON        },
1276     { "IF",       &assert_cmd, 0,         HLP_IF        },
1277     { "PROCEED",  &noop_cmd,   0,         HLP_PROCEED   },
1278     { "IGNORE",   &noop_cmd,   0,         HLP_IGNORE    },
1279     { "ECHO",     &echo_cmd,   0,         HLP_ECHO      },
1280     { "ASSERT",   &assert_cmd, 1,         HLP_ASSERT    },
1281     { "SEND",     &send_cmd,   0          },            /* deprecated */
1282     { "EXPECT",   &expect_cmd, 1          },            /* deprecated */
1283     { "NOEXPECT", &expect_cmd, 0          },            /* deprecated */
1284     { "!",        &spawn_cmd,  0,         HLP_SPAWN     },
1285     { "HELP",     &help_cmd,   0,         HLP_HELP      },
1286     { NULL,       NULL,        0          }
1287     };
1288 
1289 static CTAB set_glob_tab[] = {
1290     { "CONSOLE",     &sim_set_console,        0      }, /* deprecated */
1291     { "REMOTE",      &sim_set_remote_console, 0      }, /* deprecated */
1292     { "BREAK",       &brk_cmd,                SSH_ST }, /* deprecated */
1293     { "NOBREAK",     &brk_cmd,                SSH_CL }, /* deprecated */
1294     { "TELNET",      &sim_set_telnet,         0      }, /* deprecated */
1295     { "NOTELNET",    &sim_set_notelnet,       0      }, /* deprecated */
1296     { "LOG",         &sim_set_logon,          0,     HLP_SET_LOG      },
1297     { "NOLOG",       &sim_set_logoff,         0,     HLP_SET_LOG      },
1298     { "DEBUG",       &sim_set_debon,          0,     HLP_SET_DEBUG    },
1299     { "NODEBUG",     &sim_set_deboff,         0,     HLP_SET_DEBUG    },
1300     { "ENVIRONMENT", &sim_set_environment,    1,     HLP_SET_ENVIRON  },
1301     { "ON",          &set_on,                 1,     HLP_SET_ON       },
1302     { "NOON",        &set_on,                 0,     HLP_SET_ON       },
1303     { "VERIFY",      &set_verify,             1,     HLP_SET_VERIFY   },
1304     { "VERBOSE",     &set_verify,             1,     HLP_SET_VERIFY   },
1305     { "NOVERIFY",    &set_verify,             0,     HLP_SET_VERIFY   },
1306     { "NOVERBOSE",   &set_verify,             0,     HLP_SET_VERIFY   },
1307     { "MESSAGE",     &set_message,            1,     HLP_SET_MESSAGE  },
1308     { "NOMESSAGE",   &set_message,            0,     HLP_SET_MESSAGE  },
1309     { "QUIET",       &set_quiet,              1,     HLP_SET_QUIET    },
1310     { "NOQUIET",     &set_quiet,              0,     HLP_SET_QUIET    },
1311     { "LOCALOPC",    &set_localopc,           1,     HLP_SET_LOCALOPC },
1312     { "NOLOCALOPC",  &set_localopc,           0,     HLP_SET_LOCALOPC },
1313     { "PROMPT",      &set_prompt,             0,     HLP_SET_PROMPT   },
1314     { NULL,          NULL,                    0      }
1315     };
1316 
1317 static C1TAB set_dev_tab[] = {
1318     { "OCTAL",    &set_dev_radix,   8  },
1319     { "DECIMAL",  &set_dev_radix,  10  },
1320     { "HEX",      &set_dev_radix,  16  },
1321     { "ENABLED",  &set_dev_enbdis,  1  },
1322     { "DISABLED", &set_dev_enbdis,  0  },
1323     { "DEBUG",    &set_dev_debug,   1  },
1324     { "NODEBUG",  &set_dev_debug,   0  },
1325     { NULL,       NULL,             0  }
1326     };
1327 
1328 static C1TAB set_unit_tab[] = {
1329     { "ENABLED",  &set_unit_enbdis, 1 },
1330     { "DISABLED", &set_unit_enbdis, 0 },
1331     { NULL,       NULL,             0 }
1332     };
1333 
1334 static SHTAB show_glob_tab[] = {
1335     { "CONFIGURATION",  &show_config,        0, HLP_SHOW_CONFIG    },
1336     { "DEFAULT_BASE_SYSTEM_SCRIPT",
1337            &show_default_base_system_script, 0, HLP_SHOW_DBS       },
1338     { "DEVICES",   &show_config,             1, HLP_SHOW_DEVICES   },
1339     { "FEATURES",  &show_config,             2, HLP_SHOW_FEATURES  },
1340     { "QUEUE",     &show_queue,              0, HLP_SHOW_QUEUE     },
1341     { "TIME",      &show_time,               0, HLP_SHOW_TIME      },
1342     { "MODIFIERS", &show_mod_names,          0, HLP_SHOW_MODIFIERS },
1343     { "NAMES",     &show_log_names,          0, HLP_SHOW_NAMES     },
1344     { "SHOW",      &show_show_commands,      0, HLP_SHOW_SHOW      },
1345     { "VERSION",   &show_version,            1, HLP_SHOW_VERSION   },
1346     { "BUILDINFO", &show_buildinfo,          1, HLP_SHOW_BUILDINFO },
1347     { "PROM",      &show_prom,               0, HLP_SHOW_PROM      },
1348     { "CONSOLE",   &sim_show_console,        0, HLP_SHOW_CONSOLE   },
1349     { "REMOTE",    &sim_show_remote_console, 0, HLP_SHOW_REMOTE    },
1350     { "BREAK",     &show_break,              0, HLP_SHOW_BREAK     },
1351     { "LOG",       &sim_show_log,            0, HLP_SHOW_LOG       },
1352     { "TELNET",    &sim_show_telnet,         0  },  /* deprecated */
1353     { "DEBUG",     &sim_show_debug,          0, HLP_SHOW_DEBUG     },
1354     { "CLOCKS",    &sim_show_timers,         0, HLP_SHOW_CLOCKS    },
1355     { "SEND",      &sim_show_send,           0, HLP_SHOW_SEND      },
1356     { "EXPECT",    &sim_show_expect,         0, HLP_SHOW_EXPECT    },
1357     { "ON",        &show_on,                 0, HLP_SHOW_ON        },
1358     { NULL,        NULL,                     0  }
1359     };
1360 
1361 static SHTAB show_dev_tab[] = {
1362     { "RADIX",     &show_dev_radix,         0 },
1363     { "DEBUG",     &show_dev_debug,         0 },
1364     { "MODIFIERS", &show_dev_modifiers,     0 },
1365     { "NAMES",     &show_dev_logicals,      0 },
1366     { "SHOW",      &show_dev_show_commands, 0 },
1367     { NULL,        NULL,                    0 }
1368     };
1369 
1370 static SHTAB show_unit_tab[] = {
1371     { NULL, NULL, 0 }
1372     };
1373 
1374 #if defined(_WIN32)
1375 static
1376 int setenv(const char *envname, const char *envval, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
1377 {
1378 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1379 int r;
1380 
1381 (void)sprintf(envstr, "%s=%s", envname, envval);
1382 r = _putenv(envstr);
1383 FREE(envstr);
1384 return r;
1385 }
1386 
1387 static
1388 int unsetenv(const char *envname)
     /* [previous][next][first][last][top][bottom][index][help] */
1389 {
1390 setenv(envname, "", 1);
1391 return 0;
1392 }
1393 #endif /* if defined(_WIN32) */
1394 
1395 #define XSTR_EMAXLEN 32767
1396 
1397 const char
1398 *xstrerror_l(int errnum)
     /* [previous][next][first][last][top][bottom][index][help] */
1399 {
1400   int saved = errno;
1401   const char *ret = NULL;
1402   static __thread char buf[XSTR_EMAXLEN];
1403 
1404 #if defined(__APPLE__) || defined(_AIX) || defined(__MINGW32__) || \
1405     defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1406 # if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1407   if (strerror_s(buf, sizeof(buf), errnum) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1408 # else
1409   if (strerror_r(errnum, buf, sizeof(buf)) == 0) ret = buf; /*LINTOK: xstrerror_l*/
1410 # endif
1411 #else
1412 # if defined(__NetBSD__)
1413   locale_t loc = LC_GLOBAL_LOCALE;
1414 # else
1415   locale_t loc = uselocale((locale_t)0);
1416 # endif
1417   locale_t copy = loc;
1418   if (copy == LC_GLOBAL_LOCALE)
1419     copy = duplocale(copy);
1420 
1421   if (copy != (locale_t)0)
1422     {
1423       ret = strerror_l(errnum, copy); /*LINTOK: xstrerror_l*/
1424       if (loc == LC_GLOBAL_LOCALE)
1425         {
1426           freelocale(copy);
1427         }
1428     }
1429 #endif
1430 
1431   if (!ret)
1432     {
1433       (void)snprintf(buf, sizeof(buf), "Unknown error %d", errnum);
1434       ret = buf;
1435     }
1436 
1437   errno = saved;
1438   return ret;
1439 }
1440 
1441 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1442 
1443 /* Check if running on Rosetta 2 */
1444 
1445 #if defined(__APPLE__)
1446 int processIsTranslated(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1447 {
1448     int ret = 0;
1449     size_t size = sizeof(ret);
1450     if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1451         if (errno == ENOENT)
1452             return 0;
1453         return -1; }
1454     return ret;
1455 }
1456 #endif /* if defined(_APPLE_) */
1457 
1458 /* Substring removal hack */
1459 
1460 char *strremove(char *str, const char *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
1461 {
1462     char *p, *q, *r;
1463     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1464         size_t len = strlen(sub);
1465         while ((r = strstr(p = r + len, sub)) != NULL) {
1466             while (p < r)
1467                 *q++ = *p++;
1468         }
1469         while ((*q++ = *p++) != '\0')
1470             continue;
1471     }
1472     return str;
1473 }
1474 
1475 /* Trim whitespace */
1476 
1477 void strtrimspace (char *str_trimmed, const char *str_untrimmed)
     /* [previous][next][first][last][top][bottom][index][help] */
1478 {
1479     while (*str_untrimmed != '\0') {
1480       if(!isspace((unsigned char)*str_untrimmed)) {
1481         *str_trimmed = (char)*str_untrimmed;
1482         str_trimmed++;
1483       }
1484       str_untrimmed++;
1485     }
1486     *str_trimmed = '\0';
1487 }
1488 
1489 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1490 void allowCores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1491 {
1492   int ret;
1493   struct rlimit limit;
1494 # if defined(RLIMIT_CORE)
1495   ret = getrlimit(RLIMIT_CORE, &limit);
1496   (void)ret;
1497 #  if defined(TESTING)
1498   if (ret != 0)
1499     {
1500       sim_warn ("Failed to query core dump configuration.");
1501       return;
1502     }
1503 #  endif /* if defined(TESTING) */
1504   limit.rlim_cur = limit.rlim_max;
1505   ret = setrlimit(RLIMIT_CORE, &limit);
1506 #  if defined(TESTING)
1507   if (ret != 0)
1508     {
1509       sim_warn ("Failed to enable unlimited core dumps.");
1510       return;
1511     }
1512 #  endif /* if defined(TESTING) */
1513 # else
1514 #  if defined(TESTING)
1515   sim_warn ("Unable to query core dump configuration.");
1516 #  endif /* if defined(TESTING) */
1517 # endif /* if defined(RLIMIT_CORE) */
1518   return;
1519 }
1520 #endif
1521 
1522 #if defined(USE_DUMA)
1523 void CleanDUMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1524 {
1525   (void)fflush(stdout);
1526   DUMA_CHECKALL();
1527   (void)fflush(stderr);
1528 }
1529 # undef USE_DUMA
1530 # define USE_DUMA 1
1531 #endif /* if defined(USE_DUMA) */
1532 
1533 #if !defined(SIG_SETMASK)
1534 # undef USE_BACKTRACE
1535 #endif /* if !defined(SIG_SETMASK) */
1536 
1537 #if defined(PERF_STRIP)
1538 # undef USE_BACKTRACE
1539 #endif /* if defined(PERF_STRIP) */
1540 
1541 #if defined(USE_BACKTRACE)
1542 # include <backtrace.h>
1543 # include <backtrace-supported.h>
1544 # define BACKTRACE_SKIP 1
1545 # define BACKTRACE_MAIN "main"
1546 # undef USE_BACKTRACE
1547 # define USE_BACKTRACE 1
1548 #endif /* if defined(USE_BACKTRACE) */
1549 
1550 #if defined(BACKTRACE_SUPPORTED)
1551 # if defined(BACKTRACE_SUPPORTS_THREADS)
1552 #  if !(BACKTRACE_SUPPORTED)
1553 #   undef USE_BACKTRACE
1554 #  endif /* if !(BACKTRACE_SUPPORTED) */
1555 # else  /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1556 #  undef USE_BACKTRACE
1557 # endif /* if defined(BACKTRACE_SUPPORTS_THREADS) */
1558 #else  /* if defined(BACKTRACE_SUPPORTED) */
1559 # undef USE_BACKTRACE
1560 #endif /* if defined(BACKTRACE_SUPPORTED) */
1561 
1562 #if defined(USE_BACKTRACE)
1563 # if defined(BACKTRACE_SUPPORTED)
1564 #  include "backtrace_func.c"
1565 # endif /* if defined(BACKTRACE_SUPPORTED) */
1566 #endif /* if defined(USE_BACKTRACE) */
1567 
1568 #if !defined(__CYGWIN__)
1569 # if !defined(__APPLE__)
1570 #  if !defined(_AIX)
1571 #   if !defined(__MINGW32__)
1572 #    if !defined(__MINGW64__)
1573 #     if !defined(CROSS_MINGW32)
1574 #      if !defined(CROSS_MINGW64)
1575 #       if !defined(_WIN32)
1576 #        if !defined(__HAIKU__)
1577 static int
1578 dl_iterate_phdr_callback (struct dl_phdr_info *info, size_t size, void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1579 {
1580   (void)size;
1581   (void)data;
1582 
1583   if (strlen(info->dlpi_name) >= 2) {
1584       if (!dl_iterate_phdr_callback_called)
1585           (void)printf ("\r\n Loaded shared objects: ");
1586 
1587       dl_iterate_phdr_callback_called++;
1588       (void)printf ("%s ", info->dlpi_name);
1589   }
1590 
1591   return 0;
1592 }
1593 #        endif
1594 #       endif
1595 #      endif
1596 #     endif
1597 #    endif
1598 #   endif
1599 #  endif
1600 # endif
1601 #endif
1602 
1603 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1604 # if defined(_UCRT)
1605 #  define MINGW_CRT "UCRT"
1606 # else
1607 #  define MINGW_CRT "MSVCRT"
1608 # endif
1609 #endif
1610 
1611 #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
1612 struct UCRTVersion
1613 {
1614   uint16_t ProductVersion[4];
1615 };
1616 
1617 int
1618 GetUCRTVersion (struct UCRTVersion *ucrtversion)
     /* [previous][next][first][last][top][bottom][index][help] */
1619 {
1620 # ifdef _DEBUG
1621   static const wchar_t *DllName = L"ucrtbased.dll";
1622 # else
1623   static const wchar_t *DllName = L"ucrtbase.dll";
1624 # endif
1625 
1626   HMODULE ucrt = GetModuleHandleW (DllName);
1627   if (!ucrt)
1628     return GetLastError ();
1629 
1630   wchar_t path[MAX_PATH];
1631   if (!GetModuleFileNameW (ucrt, path, MAX_PATH))
1632     return GetLastError ();
1633 
1634   DWORD versionInfoSize = GetFileVersionInfoSizeW (path, NULL);
1635   if (!versionInfoSize)
1636     return GetLastError ();
1637 
1638   uint8_t versionInfo[versionInfoSize];
1639 
1640   if (!GetFileVersionInfoW (path, 0, versionInfoSize, versionInfo))
1641     return GetLastError ();
1642 
1643   VS_FIXEDFILEINFO *fixedFileInfo;
1644   UINT fixedFileInfoSize;
1645   if (!VerQueryValueW (versionInfo, L"\\", (void **)&fixedFileInfo, &fixedFileInfoSize))
1646     return GetLastError ();
1647 
1648   memcpy (ucrtversion->ProductVersion, &fixedFileInfo->dwProductVersionMS, sizeof (uint32_t) * 2);
1649 
1650   return 0;
1651 }
1652 #endif
1653 
1654 /* libsir support */
1655 
1656 #if !defined(PERF_STRIP)
1657 static int dps8_sir_report_error(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1658 {
1659   char message[SIR_MAXERROR] = {0};
1660   (void)sir_geterror(message);
1661   (void)fprintf(stderr, SIR_BREDB("libsir error: ") SIR_RED("%s%s"), message, SIR_EOL);
1662   return EXIT_FAILURE;
1663 }
1664 
1665 /* Main command loop */
1666 
1667 int main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1668 {
1669 char *cptr, *cptr2;
1670 char nbuf[PATH_MAX + 7];
1671 char cbuf[4*CBUFSIZE];
1672 char **targv = NULL;
1673 # if defined(USE_BACKTRACE)
1674 #  if defined(BACKTRACE_SUPPORTED)
1675 #   if defined(_INC_BACKTRACE_FUNC)
1676 bt_pid = (long)getpid();
1677 (void)bt_pid;
1678 #   endif /* if defined(_INC_BACKTRACE_FUNC) */
1679 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1680 # endif /* if defined(USE_BACKTRACE) */
1681 int32 i, sw;
1682 t_bool lookswitch;
1683 t_stat stat;
1684 
1685 # if defined(__MINGW32__)
1686 #  undef IS_WINDOWS
1687 #  define IS_WINDOWS 1
1688 #  if !defined(NEED_CONSOLE_SETUP)
1689 #   define NEED_CONSOLE_SETUP
1690 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1691 # endif /* if defined(__MINGW32__)*/
1692 
1693 # if defined(CROSS_MINGW32)
1694 #  undef IS_WINDOWS
1695 #  define IS_WINDOWS 1
1696 #  if !defined(NEED_CONSOLE_SETUP)
1697 #   define NEED_CONSOLE_SETUP
1698 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1699 # endif /* if defined(CROSS_MINGW32) */
1700 
1701 # if defined(__MINGW64__)
1702 #  undef IS_WINDOWS
1703 #  define IS_WINDOWS 1
1704 #  if !defined(NEED_CONSOLE_SETUP)
1705 #   define NEED_CONSOLE_SETUP
1706 #  endif /* if !defined(NEED_CONSOLE_SETUP) */
1707 # endif /* if defined(__MINGW64__) */
1708 
1709 # if defined(CROSS_MINGW64)
1710 #  undef IS_WINDOWS
1711 #  define IS_WINDOWS 1
1712 #  if !defined(NEED_CONSOLE_SETUP)
1713 #   define NEED_CONSOLE_SETUP
1714 #  endif /* if !defined(NEED_CONSOLE_SETUP */
1715 # endif /* if defined(CROSS_MINGW64) */
1716 
1717 # if defined(__CYGWIN__)
1718 #  if defined(IS_WINDOWS)
1719 #   undef IS_WINDOWS
1720 #  endif /* if defined(IS_WINDOWS) */
1721 # endif /* if defined(__CYGWIN__) */
1722 
1723 # if defined(USE_DUMA)
1724 #  if defined(DUMA_EXPLICIT_INIT)
1725 duma_init();
1726 (void)fflush(stderr);
1727 #  endif /* if defined(DUMA_EXPLICIT_INIT) */
1728 #  if defined(DUMA_MIN_ALIGNMENT)
1729 #   if DUMA_MIN_ALIGNMENT > 0
1730 DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
1731 #   endif /* if DUMA_MIN_ALIGNMENT > 0 */
1732 #  endif /* if defined(DUMA_MIN_ALIGNMENT) */
1733 DUMA_SET_FILL(0x2E);
1734 (void)fflush(stderr);
1735 (void)atexit(CleanDUMA);
1736 # endif /* if defined(USE_DUMA) */
1737 
1738 # if defined(USE_BACKTRACE)
1739 #  if defined(BACKTRACE_SUPPORTED)
1740 #   include "backtrace_main.c"
1741 #  endif /* if defined(BACKTRACE_SUPPORTED) */
1742 # endif /* if defined(USE_BACKTRACE) */
1743 
1744 (void)setlocale(LC_ALL, "");
1745 
1746 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1747 #  if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
1748 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1749 #  endif /* if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) */
1750 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1751 
1752 # if defined(NEED_CONSOLE_SETUP)
1753 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1754 if (handle != INVALID_HANDLE_VALUE)
1755   {
1756     DWORD mode = 0;
1757     if (GetConsoleMode(handle, &mode))
1758       {
1759         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1760         SetConsoleMode(handle, mode);
1761       }
1762   }
1763 puts ("\e[0m");
1764 # endif /* if defined(NEED_CONSOLE_SETUP) */
1765 
1766 # if defined(__HAIKU__)
1767 (void)disable_debugger(1);
1768 # endif /* if defined(__HAIKU__) */
1769 
1770 /* libsir init */
1771 
1772 sirinit si;
1773 if (!sir_makeinit(&si))
1774     return dps8_sir_report_error();
1775 
1776 /* Levels for stdout: send debug, information, warning, and notice there. */
1777 si.d_stdout.levels = SIRL_DEBUG | SIRL_INFO | SIRL_WARN | SIRL_NOTICE;
1778 
1779 /* Options for stdout: don't show the name, level, timestamp, hostname, or PID. */
1780 si.d_stdout.opts = SIRO_NONAME | SIRO_NOLEVEL | SIRO_NOTIME | SIRO_NOHOST | SIRO_NOPID;
1781 
1782 /* Levels for stderr: send error and above there. */
1783 si.d_stderr.levels = SIRL_ERROR | SIRL_CRIT | SIRL_ALERT | SIRL_EMERG;
1784 
1785 /* Options for stderr: don't show the hostname. */
1786 si.d_stderr.opts = SIRO_NOHOST;
1787 
1788 /* Levels for the system logger: don't send any output there. */
1789 si.d_syslog.levels = SIRL_NONE;
1790 
1791 /* Options for the system logger: use the default value. */
1792 si.d_syslog.opts = SIRO_DEFAULT;
1793 
1794 /* Configure a name to associate with our output. */
1795 (void)_sir_strncpy(si.name, SIR_MAXNAME, appname, strnlen(appname, SIR_MAXNAME));
1796 
1797 /* Initialize libsir. */
1798 if (!sir_init(&si))
1799     return dps8_sir_report_error();
1800 
1801 /* Set a friendly name for the current thread. */
1802 char thread_name[SIR_MAXPID] = {0};
1803 _sir_snprintf_trunc(thread_name, SIR_MAXPID, "%s", appname);
1804 (void)_sir_setthreadname(thread_name);
1805 
1806 /* sanity checks */
1807 
1808 # if defined(__clang_analyzer__)
1809 (void)fprintf (stderr, "Error: Attempting to execute a Clang Analyzer build!\n");
1810 return 1;
1811 # endif /* if defined(__clang_analyzer__) */
1812 
1813 if (argc == 0) {
1814     (void)fprintf (stderr, "Error: main() called directly!\n");
1815     return 1;
1816 }
1817 
1818 /* Enable unlimited core dumps */
1819 # if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1820 allowCores();
1821 # endif
1822 
1823 int testEndian = decContextTestEndian();
1824 if (testEndian != 0) {
1825   if (testEndian == 1) {
1826     (void)fprintf (stderr,
1827                    "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1828     return 1;
1829   }
1830   if (testEndian == -1) {
1831     (void)fprintf (stderr,
1832                    "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1833     return 1;
1834   }
1835   (void)fprintf (stderr,
1836                  "Error: Unable to determine system byte order; aborting.\n");
1837   return 1;
1838 }
1839 # if defined(NEED_128)
1840 test_math128();
1841 # endif /* if defined(NEED_128) */
1842 
1843 # define UV_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
1844 
1845 # if defined(LINUX_OS) && UV_VERSION_HEX >= UV_VERSION(1, 44, 0)
1846 // Only use uv_available_parallelism on Linux and only on libuv 1.44.0 or higher.
1847 // This will return a value less than the actual number of CPUs if, for example,
1848 // the CPU affinity mask has been pinned to specific CPUs.  On all other systems,
1849 // prefer the _sir_nprocs routine to query the number of actual CPUs available.
1850 nprocs = (unsigned int)uv_available_parallelism();
1851 # else
1852 nprocs = (unsigned int)_sir_nprocs();
1853 # endif
1854 
1855 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1856 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1857 if (!targv)
1858   {
1859     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1860                    __func__, __FILE__, __LINE__);
1861 # if defined(USE_BACKTRACE)
1862 #  if defined(SIGUSR2)
1863     (void)raise(SIGUSR2);
1864     /*NOTREACHED*/ /* unreachable */
1865 #  endif /* if defined(SIGUSR2) */
1866 # endif /* if defined(USE_BACKTRACE) */
1867     abort();
1868   }
1869 for (i=0; i<argc; i++)
1870     targv[i] = argv[i];
1871 argv = targv;
1872 
1873 /* setup defaults */
1874 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1875 *cbuf = 0;                                              /* init arg buffer */
1876 sim_switches = 0;                                       /* init switches */
1877 lookswitch = TRUE;
1878 stdnul = fopen(NULL_DEVICE,"wb");
1879 
1880 /* process arguments */
1881 for (i = 1; i < argc; i++) {                            /* loop thru args */
1882     if (argv[i] == NULL)                                /* paranoia */
1883         continue;
1884 
1885 # if defined(THREADZ) || defined(LOCKLESS)
1886 /* performance test */
1887     int perftestflag  = strcmp(argv[i], "--perftest");
1888     if (perftestflag == 0) {
1889       char * testName = NULL;
1890       if (i + 1 < argc)
1891         testName = argv[i + 1];
1892       perfTest (testName);
1893       return 0;
1894     }
1895 # endif
1896 
1897 /* requested only version? */
1898     int onlyvers  = strcmp(argv[i], "--version");
1899     if (onlyvers == 0) {
1900 # if defined(VER_H_GIT_VERSION)
1901 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1902 #   if VER_H_GIT_PATCH_INT < 1
1903         (void)fprintf (stdout, "%s simulator %s\n",
1904                        sim_name, VER_H_GIT_VERSION);
1905 #   else
1906         (void)fprintf (stdout, "%s simulator %s+%s\n",
1907                        sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1908 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1909 #  else
1910         (void)fprintf (stdout, "%s simulator %s\n",
1911                        sim_name, VER_H_GIT_VERSION);
1912 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1913 # else
1914         (void)fprintf (stdout, "%s simulator\n", sim_name);
1915 # endif /* if defined(VER_H_GIT_VERSION) */
1916         FREE (targv);
1917         return 0;
1918     }
1919 
1920 /* requested short or long help? */
1921     int longhelp  = strcmp(argv[i], "--help");
1922     int shorthelp = strcmp(argv[i], "-h");
1923     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1924     if (longhelp == 0 || shorthelp == 0) {
1925 # if defined(VER_H_GIT_VERSION)
1926 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1927 #   if VER_H_GIT_PATCH_INT < 1
1928         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1929 #   else
1930         (void)fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1931 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1932 #  else
1933         (void)fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1934 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1935 # else
1936         (void)fprintf (stdout, "%s simulator", sim_name);
1937 # endif /* if defined(VER_H_GIT_VERSION) */
1938         (void)fprintf (stdout, "\n");
1939         (void)fprintf (stdout, "\n USAGE: %s { [ SWITCHES ] ... } { < SCRIPT > }", argv[0]);
1940         (void)fprintf (stdout, "\n");
1941         (void)fprintf (stdout, "\n Invokes the %s simulator, with optional switches and/or script file.", sim_name);
1942         (void)fprintf (stdout, "\n");
1943         (void)fprintf (stdout, "\n Switches:");
1944         (void)fprintf (stdout, "\n  -e, -E            Aborts script processing immediately upon any error");
1945         (void)fprintf (stdout, "\n  -h, -H, --help    Prints only this informational help text and exit");
1946         (void)fprintf (stdout, "\n  -k, -K            Disables all support for exclusive file locking");
1947         (void)fprintf (stdout, "\n  -l, -L            Reports but ignores all exclusive file locking errors");
1948         (void)fprintf (stdout, "\n  -o, -O            Makes scripting ON conditions and actions inheritable");
1949         (void)fprintf (stdout, "\n  -q, -Q            Disables printing of non-fatal informational messages");
1950         (void)fprintf (stdout, "\n  -r, -R            Enables an unlinked ephemeral system state file");
1951         (void)fprintf (stdout, "\n  -s, -S            Enables a randomized persistent system state file");
1952         (void)fprintf (stdout, "\n  -t, -T            Disables fsync and creation/usage of system state file");
1953         (void)fprintf (stdout, "\n  -v, -V            Prints commands read from script file before execution");
1954         (void)fprintf (stdout, "\n  --version         Prints only the simulator identification text and exit");
1955         (void)fprintf (stdout, "\n");
1956 # if defined(USE_DUMA)
1957         nodist++;
1958 # endif /* if defined(USE_DUMA) */
1959 if (!nodist) {
1960         (void)fprintf (stdout, "\n This software is made available under the terms of the ICU License.");
1961         (void)fprintf (stdout, "\n For complete license details, see the LICENSE file included with the");
1962         (void)fprintf (stdout, "\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1963 }
1964 else
1965 {
1966         (void)fprintf (stdout, "\n********** LICENSE RESTRICTED BUILD ****** NOT FOR REDISTRIBUTION **********");
1967 }
1968         (void)fprintf (stdout, "\n");
1969         FREE(argv); //-V726
1970         return 0;
1971     }
1972     /* invalid arguments? */
1973     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
1974         if ((sw = get_switches (argv[i])) < 0) {
1975             (void)fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1976             FREE(argv); //-V726
1977             return 1;
1978             }
1979         sim_switches = sim_switches | sw;
1980         }
1981     /* parse arguments */
1982     else {
1983         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1984             (void)fprintf (stderr, "Argument string too long\n");
1985             FREE(argv); //-V726
1986             return 1;
1987             }
1988         if (*cbuf)                                  /* concat args */
1989             strcat (cbuf, " ");
1990         (void)sprintf(&cbuf[strlen(cbuf)], "%s%s%s", //-V755
1991                       strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : ""); //-V755
1992         lookswitch = FALSE;                         /* no more switches */
1993         }
1994     }                                               /* end for */
1995 sim_nolock = sim_switches & SWMASK ('K');           /* -k means skip locking     */
1996 sim_iglock = sim_switches & SWMASK ('L');           /* -l means ignore locking   */
1997 sim_randompst = sim_switches & SWMASK ('S');        /* -s means persist random   */
1998 sim_quiet = sim_switches & SWMASK ('Q');            /* -q means quiet            */
1999 sim_randstate = sim_switches & SWMASK ('R');        /* -r means random sys_state */
2000 if (sim_randompst) sim_randstate = 1;               /*    and is implied with -s */
2001 sim_nostate = sim_switches & SWMASK ('T');          /* -t means no sys_state     */
2002 if (sim_nostate)                                    /*    and disables -s and -r */
2003   {
2004     sim_randompst = 0;
2005     sim_randstate = 0;
2006   }
2007 sim_on_inherit = sim_switches & SWMASK ('O');       /* -o means inherit on state */
2008 
2009 sim_init_sock ();                                   /* init socket capabilities */
2010 if (sim_dflt_dev == NULL)                           /* if no default */
2011     sim_dflt_dev = sim_devices[0];
2012 if (sim_vm_init != NULL)                            /* call once only */
2013     (*sim_vm_init)();
2014 sim_finit ();                                       /* init fio package */
2015 for (i = 0; cmd_table[i].name; i++) {
2016     size_t alias_len = strlen (cmd_table[i].name);
2017     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
2018     if (!cmd_name)
2019       {
2020         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2021                        __func__, __FILE__, __LINE__);
2022 # if defined(USE_BACKTRACE)
2023 #  if defined(SIGUSR2)
2024         (void)raise(SIGUSR2);
2025         /*NOTREACHED*/ /* unreachable */
2026 #  endif /* if defined(SIGUSR2) */
2027 # endif /* if defined(USE_BACKTRACE) */
2028         abort();
2029       }
2030 
2031     strcpy (cmd_name, cmd_table[i].name);
2032     while (alias_len > 1) {
2033         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
2034         --alias_len;
2035         if (getenv (cmd_name))                      /* Externally defined command alias? */
2036             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
2037         }
2038     FREE (cmd_name);
2039     }
2040 stop_cpu = 0;
2041 sim_interval = 0;
2042 sim_time = sim_rtime = 0;
2043 noqueue_time = 0;
2044 sim_clock_queue = QUEUE_LIST_END;
2045 sim_is_running = 0;
2046 sim_log = NULL;
2047 if (sim_emax <= 0)
2048     sim_emax = 1;
2049 sim_timer_init ();
2050 
2051 if ((stat = sim_ttinit ()) != SCPE_OK) {
2052     (void)fprintf (stderr, "Fatal terminal initialization error\n%s\n",
2053                    sim_error_text (stat));
2054     FREE(argv); //-V726
2055     return 1;
2056     }
2057 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
2058     (void)fprintf (stderr, "Unable to allocate examine buffer\n");
2059     FREE(argv); //-V726
2060     return 1;
2061     };
2062 if ((stat = reset_all_p (0)) != SCPE_OK) {
2063     (void)fprintf (stderr, "Fatal simulator initialization error\n%s\n",
2064                    sim_error_text (stat));
2065     FREE(argv); //-V726
2066     return 1;
2067     }
2068 if ((stat = sim_brk_init ()) != SCPE_OK) {
2069     (void)fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
2070                    sim_error_text (stat));
2071     FREE(argv); //-V726
2072     return 1;
2073     }
2074 if (!sim_quiet) {
2075     (void)printf ("\n");
2076     show_version (stdout, NULL, NULL, 0, NULL);
2077     }
2078 
2079 cptr = getenv("HOME");
2080 if (cptr == NULL) {
2081     cptr = getenv("HOMEPATH");
2082     cptr2 = getenv("HOMEDRIVE");
2083     }
2084 else
2085     cptr2 = NULL;
2086 (void)cptr2;
2087 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
2088     stat = do_cmd (0, cbuf);                            /* proc cmd file */
2089 else if (*argv[0]) {                                    /* sim name arg? */
2090     char *np;                                           /* "path.ini" */
2091     nbuf[0] = '"';                                      /* starting " */
2092     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
2093     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
2094         np = strrchr (nbuf, '/');                       /* strip path and try again in cwd */
2095         if (np == NULL)
2096             np = strrchr (nbuf, '\\');                  /* windows path separator */
2097         if (np != NULL) {
2098             *np = '"';
2099             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
2100             }
2101         }
2102     }
2103 
2104 argv = uv_setup_args(argc, argv);
2105 
2106 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
2107 
2108 if (sim_vm_exit != NULL)                                /* call once only */
2109     (*sim_vm_exit)();
2110 
2111 detach_all (0, TRUE);                                   /* close files */
2112 sim_set_deboff (0, NULL);                               /* close debug */
2113 sim_set_logoff (0, NULL);                               /* close log */
2114 sim_set_notelnet (0, NULL);                             /* close Telnet */
2115 sim_ttclose ();                                         /* close console */
2116 sim_cleanup_sock ();                                    /* cleanup sockets */
2117 fclose (stdnul);                                        /* close bit bucket file handle */
2118 FREE (targv);                                           /* release any argv copy that was made */
2119 FREE (sim_prompt);
2120 FREE (sim_eval);
2121 FREE (sim_internal_devices);
2122 FREE (sim_brk_tab);
2123 FREE (sim_staba.comp);
2124 FREE (sim_staba.mask);
2125 FREE (sim_stabr.comp);
2126 FREE (sim_stabr.mask);
2127 return sir_cleanup() ? EXIT_SUCCESS : dps8_sir_report_error();
2128 }
2129 #endif
2130 
2131 t_stat process_stdin_commands (t_stat stat, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
2132 {
2133 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
2134 CONST char *cptr;
2135 t_stat stat_nomessage;
2136 CTAB *cmdp = NULL;
2137 
2138 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
2139 while (stat != SCPE_EXIT) {                             /* in case exit */
2140     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
2141         (void)printf ("%s%s\n", sim_prompt, cptr);      /* echo */
2142     else if (sim_vm_read != NULL) {                     /* sim routine? */
2143         (void)printf ("%s", sim_prompt);                /* prompt */
2144         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
2145         }
2146     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prompt*/
2147     if (cptr == NULL) {                                 /* EOF? */
2148         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
2149         else break;                                     /* otherwise exit */
2150         }
2151     if (*cptr == 0)                                     /* ignore blank */
2152         continue;
2153     sim_sub_args (cbuf, sizeof(cbuf), argv);
2154     if (sim_log)                                        /* log cmd */
2155         (void)fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
2156     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
2157         (void)fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
2158     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2159     sim_switches = 0;                                   /* init switches */
2160     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
2161         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
2162     else
2163         stat = SCPE_UNK;
2164     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
2165     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2166     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2167     sim_last_cmd_stat = stat;                           /* save command error status */
2168     if (!stat_nomessage) {                              /* displaying message status? */
2169         if (cmdp && (cmdp->message))                    /* special message handler? */
2170             cmdp->message (NULL, stat);                 /* let it deal with display */
2171         else
2172             if (stat >= SCPE_BASE)                      /* error? */
2173                 sim_printf ("%s\n", sim_error_text (stat));
2174         }
2175     if (sim_vm_post != NULL)
2176         (*sim_vm_post) (TRUE);
2177     }                                                   /* end while */
2178 return stat;
2179 }
2180 
2181 /* Set prompt routine */
2182 
2183 t_stat set_prompt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2184 {
2185 char gbuf[CBUFSIZE], *gptr;
2186 
2187 if ((NULL == cptr) || (*cptr == '\0'))
2188     return SCPE_ARG;
2189 
2190 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
2191 if (gbuf[0] == '\0') {                                  /* Token started with quote */
2192     gbuf[sizeof (gbuf)-1] = '\0';
2193     strncpy (gbuf, cptr, sizeof (gbuf)-1);
2194     gptr = strchr (gbuf, '"');
2195     if (NULL != gptr)
2196         *gptr = '\0';
2197     }
2198 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
2199 if (!sim_prompt)
2200   {
2201     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2202                    __func__, __FILE__, __LINE__);
2203 #if defined(USE_BACKTRACE)
2204 # if defined(SIGUSR2)
2205     (void)raise(SIGUSR2);
2206     /*NOTREACHED*/ /* unreachable */
2207 # endif /* if defined(SIGUSR2) */
2208 #endif /* if defined(USE_BACKTRACE) */
2209     abort();
2210   }
2211 (void)sprintf (sim_prompt, "%s ", gbuf);
2212 return SCPE_OK;
2213 }
2214 
2215 /* Find command routine */
2216 
2217 CTAB *find_cmd (const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
2218 {
2219 CTAB *cmdp = NULL;
2220 
2221 if (sim_vm_cmd)                                         /* try ext commands */
2222     cmdp = find_ctab (sim_vm_cmd, gbuf);
2223 if (cmdp == NULL)                                       /* try regular cmds */
2224     cmdp = find_ctab (cmd_table, gbuf);
2225 return cmdp;
2226 }
2227 
2228 /* Exit command */
2229 
2230 t_stat exit_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2231 {
2232 return SCPE_EXIT;
2233 }
2234 
2235 /* Help command */
2236 
2237 /* Used when sorting a list of command names */
2238 static int _cmd_name_compare (const void *pa, const void *pb)
     /* [previous][next][first][last][top][bottom][index][help] */
2239 {
2240 CTAB * const *a = (CTAB * const *)pa;
2241 CTAB * const *b = (CTAB * const *)pb;
2242 
2243 return strcmp((*a)->name, (*b)->name);
2244 }
2245 
2246 void fprint_help (FILE *st)
     /* [previous][next][first][last][top][bottom][index][help] */
2247 {
2248 CTAB *cmdp;
2249 CTAB **hlp_cmdp = NULL;
2250 size_t cmd_cnt = 0;
2251 size_t cmd_size = 0;
2252 size_t max_cmdname_size = 0;
2253 size_t i, line_offset;
2254 
2255 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
2256     if (cmdp->help) {
2257         if (cmd_cnt >= cmd_size) {
2258             cmd_size += 20;
2259             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2260             if (!hlp_cmdp)
2261               {
2262                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2263                                __func__, __FILE__, __LINE__);
2264 #if defined(USE_BACKTRACE)
2265 # if defined(SIGUSR2)
2266                 (void)raise(SIGUSR2);
2267                 /*NOTREACHED*/ /* unreachable */
2268 # endif /* if defined(SIGUSR2) */
2269 #endif /* if defined(USE_BACKTRACE) */
2270                 abort();
2271               }
2272             }
2273         hlp_cmdp[cmd_cnt] = cmdp;
2274         ++cmd_cnt;
2275         if (strlen(cmdp->name) > max_cmdname_size)
2276             max_cmdname_size = strlen(cmdp->name);
2277         }
2278     }
2279 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
2280     if (cmdp->help && (NULL == sim_vm_cmd || NULL == find_ctab (sim_vm_cmd, cmdp->name))) {
2281         if (cmd_cnt >= cmd_size) {
2282             cmd_size += 20;
2283             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2284             if (!hlp_cmdp)
2285               {
2286                 (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2287                                __func__, __FILE__, __LINE__);
2288 #if defined(USE_BACKTRACE)
2289 # if defined(SIGUSR2)
2290                 (void)raise(SIGUSR2);
2291                 /*NOTREACHED*/ /* unreachable */
2292 # endif /* if defined(SIGUSR2) */
2293 #endif /* if defined(USE_BACKTRACE) */
2294                 abort();
2295               }
2296             }
2297         hlp_cmdp[cmd_cnt] = cmdp;
2298         ++cmd_cnt;
2299         if (strlen (cmdp->name) > max_cmdname_size)
2300             max_cmdname_size = strlen(cmdp->name);
2301         }
2302     }
2303 (void)fprintf (st, "HELP is available for the following commands:\n\n    ");
2304 if (hlp_cmdp)
2305   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
2306 line_offset = 4;
2307 for ( i = 0 ; i < cmd_cnt ; ++i ) {
2308     fputs (hlp_cmdp[i]->name, st);
2309     line_offset += 5 + max_cmdname_size;
2310     if (line_offset + max_cmdname_size > 79) {
2311         line_offset = 4;
2312         (void)fprintf (st, "\n    ");
2313         }
2314     else
2315         (void)fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
2316     }
2317 FREE (hlp_cmdp);
2318 (void)fprintf (st, "\n");
2319 return;
2320 }
2321 
2322 static void fprint_header (FILE *st, t_bool *pdone, char *context)
     /* [previous][next][first][last][top][bottom][index][help] */
2323 {
2324 if (!*pdone)
2325     (void)fprintf (st, "%s", context);
2326 *pdone = TRUE;
2327 }
2328 
2329 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2330 {
2331 REG *rptr, *trptr;
2332 t_bool found = FALSE;
2333 t_bool all_unique = TRUE;
2334 size_t max_namelen = 0;
2335 DEVICE *tdptr;
2336 CONST char *tptr;
2337 char *namebuf;
2338 char rangebuf[32];
2339 
2340 if (dptr->registers)
2341     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2342         if (rptr->flags & REG_HIDDEN)
2343             continue;
2344         if (rptr->depth > 1)
2345             (void)sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2346         else
2347             strcpy (rangebuf, "");
2348         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2349             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2350         found = TRUE;
2351         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2352         if ((trptr == NULL) || (tdptr != dptr))
2353             all_unique = FALSE;
2354         }
2355 if (!found) {
2356     if (!silent)
2357         (void)fprintf (st, "No register HELP available for the %s device\n",
2358                        dptr->name);
2359     }
2360 else {
2361     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2362     if (!namebuf)
2363       {
2364         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2365                        __func__, __FILE__, __LINE__);
2366 #if defined(USE_BACKTRACE)
2367 # if defined(SIGUSR2)
2368         (void)raise(SIGUSR2);
2369         /*NOTREACHED*/ /* unreachable */
2370 # endif /* if defined(SIGUSR2) */
2371 #endif /* if defined(USE_BACKTRACE) */
2372         abort();
2373       }
2374     (void)fprintf (st, "\nThe %s device implements these registers:\n\n",
2375                    dptr->name);
2376     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2377         if (rptr->flags & REG_HIDDEN)
2378             continue;
2379         if (rptr->depth <= 1)
2380             (void)sprintf (namebuf, "%*s",
2381                            -((int)max_namelen),
2382                            rptr->name);
2383         else {
2384             (void)sprintf (rangebuf, "[%d:%d]",
2385                            0,
2386                            rptr->depth-1);
2387             (void)sprintf (namebuf, "%s%*s",
2388                            rptr->name,
2389                            (int)(strlen(rptr->name))-((int)max_namelen),
2390                            rangebuf);
2391             }
2392         if (all_unique) {
2393             (void)fprintf (st, "  %s %4d  %s\n",
2394                            namebuf,
2395                            rptr->width,
2396                            rptr->desc ? rptr->desc : "");
2397             continue;
2398             }
2399         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2400         if ((trptr == NULL) || (tdptr != dptr))
2401             (void)fprintf (st, "  %s %s %4d  %s\n",
2402                            dptr->name,
2403                            namebuf,
2404                            rptr->width,
2405                            rptr->desc ? rptr->desc : "");
2406         else
2407             (void)fprintf (st, "  %*s %s %4d  %s\n",
2408                            (int)strlen(dptr->name), "",
2409                            namebuf,
2410                            rptr->width,
2411                            rptr->desc ? rptr->desc : "");
2412         }
2413     FREE (namebuf);
2414     }
2415 }
2416 
2417 void fprint_reg_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2418 {
2419 fprint_reg_help_ex (st, dptr, TRUE);
2420 }
2421 
2422 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2423 {
2424 if (dptr->attach_help) {
2425     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2426     dptr->attach_help (st, dptr, NULL, 0, NULL);
2427     return;
2428     }
2429 if (DEV_TYPE(dptr) == DEV_DISK) {
2430     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2431     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2432     return;
2433     }
2434 if (DEV_TYPE(dptr) == DEV_TAPE) {
2435     (void)fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2436     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2437     return;
2438     }
2439 if (!silent) {
2440     (void)fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2441     if (dptr->help)
2442         dptr->help (st, dptr, NULL, 0, NULL);
2443     }
2444 }
2445 
2446 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2447 {
2448 MTAB *mptr;
2449 DEBTAB *dep;
2450 t_bool found = FALSE;
2451 char buf[CBUFSIZE], header[CBUFSIZE];
2452 uint32 enabled_units = dptr->numunits;
2453 uint32 unit;
2454 
2455 (void)sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2456 for (unit=0; unit < dptr->numunits; unit++)
2457     if (dptr->units[unit].flags & UNIT_DIS)
2458         --enabled_units;
2459 if (dptr->modifiers) {
2460     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2461         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2462             continue;                                       /* skip unit only extended modifiers */
2463         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2464             continue;                                       /* skip unit only simple modifiers */
2465         if (mptr->mstring) {
2466             fprint_header (st, &found, header);
2467             (void)sprintf (buf, "SET %s %s%s", sim_dname (dptr),
2468                            mptr->mstring,
2469                            (strchr(mptr->mstring, '=')) \
2470                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2471                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2472                                ? "{=val}" : "")));
2473             if ((strlen (buf) < 30) || (NULL == mptr->help))
2474                 (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2475             else
2476                 (void)fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2477             }
2478         }
2479     }
2480 if (dptr->flags & DEV_DISABLE) {
2481     fprint_header (st, &found, header);
2482     (void)sprintf (buf, "SET %s ENABLE", sim_dname (dptr));
2483     (void)fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2484     (void)sprintf (buf, "SET %s DISABLE", sim_dname (dptr));
2485     (void)fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2486     }
2487 if (dptr->flags & DEV_DEBUG) {
2488     fprint_header (st, &found, header);
2489     (void)sprintf (buf, "SET %s DEBUG", sim_dname (dptr));
2490     (void)fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2491     (void)sprintf (buf, "SET %s NODEBUG", sim_dname (dptr));
2492     (void)fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2493     if (dptr->debflags) {
2494         t_bool desc_available = FALSE;
2495         strcpy (buf, "");
2496         (void)fprintf (st, "SET %s DEBUG=", sim_dname (dptr));
2497         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2498             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2499             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2500             }
2501         (void)fprintf (st, "\n");
2502         (void)fprintf (st,  "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2503         (void)fprintf (st, "SET %s NODEBUG=", sim_dname (dptr));
2504         for (dep = dptr->debflags; dep->name != NULL; dep++)
2505             (void)fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2506         (void)fprintf (st, "\n");
2507         (void)fprintf (st,  "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2508         if (desc_available) {
2509             (void)fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2510             for (dep = dptr->debflags; dep->name != NULL; dep++)
2511                 (void)fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2512             }
2513         }
2514     }
2515 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2516     if (dptr->units->flags & UNIT_DISABLE) {
2517         fprint_header (st, &found, header);
2518         (void)sprintf (buf, "SET %sn ENABLE", sim_dname (dptr));
2519         (void)fprintf (st,  "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2520         (void)sprintf (buf, "SET %sn DISABLE", sim_dname (dptr));
2521         (void)fprintf (st,  "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2522         }
2523     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2524         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2525             continue;                                           /* skip device only modifiers */
2526         if ((NULL == mptr->valid) && MODMASK(mptr,MTAB_XTD))
2527             continue;                                           /* skip show only modifiers */
2528         if (mptr->mstring) {
2529             fprint_header (st, &found, header);
2530             (void)sprintf (buf, "SET %s%s %s%s", sim_dname (dptr),
2531                            (dptr->numunits > 1) ? "n" : "0", mptr->mstring,
2532                            (strchr(mptr->mstring, '=')) \
2533                                ? ""       : (MODMASK(mptr,MTAB_VALR) \
2534                                ? "=val"   : (MODMASK(mptr,MTAB_VALO) \
2535                                ? "{=val}" : "")));
2536             (void)fprintf (st, "%-30s\t%s\n", buf,
2537                            (strchr(mptr->mstring, '=')) \
2538                                ? "" : (mptr->help ? mptr->help : ""));
2539             }
2540         }
2541     }
2542 if (!found && !silent)
2543     (void)fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2544 }
2545 
2546 void fprint_set_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2547 {
2548   fprint_set_help_ex (st, dptr, TRUE);
2549 }
2550 
2551 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2552 {
2553 MTAB *mptr;
2554 t_bool found = FALSE;
2555 char buf[CBUFSIZE], header[CBUFSIZE];
2556 uint32 enabled_units = dptr->numunits;
2557 uint32 unit;
2558 
2559 (void)sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2560 for (unit=0; unit < dptr->numunits; unit++)
2561     if (dptr->units[unit].flags & UNIT_DIS)
2562         --enabled_units;
2563 if (dptr->modifiers) {
2564     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2565         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2566             continue;                                       /* skip unit only extended modifiers */
2567         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2568             continue;                                       /* skip unit only simple modifiers */
2569         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2570             continue;
2571         fprint_header (st, &found, header);
2572         (void)sprintf (buf, "SHOW %s %s%s", sim_dname (dptr),
2573                        mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2574         (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2575         }
2576     }
2577 if (dptr->flags & DEV_DEBUG) {
2578     fprint_header (st, &found, header);
2579     (void)sprintf (buf, "SHOW %s DEBUG", sim_dname (dptr));
2580     (void)fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2581     }
2582 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2583     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2584         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2585             continue;                                           /* skip device only modifiers */
2586         if ((!mptr->disp) || (!mptr->pstring))
2587             continue;
2588         fprint_header (st, &found, header);
2589         (void)sprintf (buf, "SHOW %s%s %s%s", sim_dname (dptr),
2590                        (dptr->numunits > 1) ? "n" : "0", mptr->pstring,
2591                        MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2592         (void)fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2593         }
2594     }
2595 if (!found && !silent)
2596     (void)fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2597 }
2598 
2599 void fprint_show_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2600     {
2601     fprint_show_help_ex (st, dptr, TRUE);
2602     }
2603 
2604 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2605 {
2606 BRKTYPTAB *brkt = dptr->brk_types;
2607 char gbuf[CBUFSIZE];
2608 
2609 if (sim_brk_types == 0) {
2610     if ((dptr != sim_dflt_dev) && (!silent)) {
2611         (void)fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2612         if (dptr->help)
2613             dptr->help (st, dptr, NULL, 0, NULL);
2614         }
2615     return;
2616     }
2617 if (brkt == NULL) {
2618     int i;
2619 
2620     if (dptr == sim_dflt_dev) {
2621         if (sim_brk_types & ~sim_brk_dflt) {
2622             (void)fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2623             for (i=0; i<26; i++) {
2624                 if (sim_brk_types & (1<<i))
2625                     (void)fprintf (st, "  -%c\n", 'A'+i);
2626                 }
2627             }
2628         (void)fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2629         }
2630     return;
2631     }
2632 (void)fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2633 while (brkt->btyp) {
2634     (void)fprintf (st, "  %s     %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2635     ++brkt;
2636     }
2637 (void)fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2638 }
2639 
2640 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2641 {
2642 char gbuf[CBUFSIZE];
2643 CTAB *cmdp;
2644 
2645 if (*cptr) {
2646     (void)get_glyph (cptr, gbuf, 0);
2647     if ((cmdp = find_cmd (gbuf))) {
2648         if (cmdp->action == &exdep_cmd) {
2649             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2650                 return dptr->help (st, dptr, uptr, flag, cptr);
2651             else
2652                 (void)fprintf (st, "No HELP available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2653             return SCPE_OK;
2654             }
2655         if (cmdp->action == &set_cmd) {
2656             fprint_set_help_ex (st, dptr, FALSE);
2657             return SCPE_OK;
2658             }
2659         if (cmdp->action == &show_cmd) {
2660             fprint_show_help_ex (st, dptr, FALSE);
2661             return SCPE_OK;
2662             }
2663         if (cmdp->action == &attach_cmd) {
2664             fprint_attach_help_ex (st, dptr, FALSE);
2665             return SCPE_OK;
2666             }
2667         if (cmdp->action == &brk_cmd) {
2668             fprint_brk_help_ex (st, dptr, FALSE);
2669             return SCPE_OK;
2670             }
2671         if (dptr->help)
2672             return dptr->help (st, dptr, uptr, flag, cptr);
2673         (void)fprintf (st, "No %s HELP is available for the %s device\n", cmdp->name, dptr->name);
2674         return SCPE_OK;
2675         }
2676     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2677         fprint_reg_help_ex (st, dptr, FALSE);
2678         return SCPE_OK;
2679         }
2680     if (dptr->help)
2681         return dptr->help (st, dptr, uptr, flag, cptr);
2682     (void)fprintf (st, "No %s HELP is available for the %s device\n", gbuf, dptr->name);
2683     return SCPE_OK;
2684     }
2685 if (dptr->help) {
2686     return dptr->help (st, dptr, uptr, flag, cptr);
2687     }
2688 if (dptr->description)
2689     (void)fprintf (st, "%s %s HELP\n", dptr->description (dptr), dptr->name);
2690 else
2691     (void)fprintf (st, "%s HELP\n", dptr->name);
2692 fprint_set_help_ex    (st, dptr, TRUE);
2693 fprint_show_help_ex   (st, dptr, TRUE);
2694 fprint_attach_help_ex (st, dptr, TRUE);
2695 fprint_reg_help_ex    (st, dptr, TRUE);
2696 fprint_brk_help_ex    (st, dptr, TRUE);
2697 return SCPE_OK;
2698 }
2699 
2700 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
     /* [previous][next][first][last][top][bottom][index][help] */
2701 {
2702 switch (help[0]) {
2703     case '*':
2704         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2705         if (sim_log)
2706             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2707         break;
2708     default:
2709         fputs (help, stdout);
2710         if (sim_log)
2711             fputs (help, sim_log);
2712         break;
2713     }
2714 return SCPE_OK;
2715 }
2716 
2717 t_stat help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2718 {
2719 char gbuf[CBUFSIZE];
2720 CTAB *cmdp;
2721 
2722 GET_SWITCHES (cptr);
2723 if (sim_switches & SWMASK ('F'))
2724     flag = flag | SCP_HELP_FLAT;
2725 if (*cptr) {
2726     cptr = get_glyph (cptr, gbuf, 0);
2727     if ((cmdp = find_cmd (gbuf))) {
2728         if (*cptr) {
2729             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2730                 DEVICE *dptr;
2731                 UNIT *uptr;
2732                 t_stat r;
2733                 cptr = get_glyph (cptr, gbuf, 0);
2734                 dptr = find_unit (gbuf, &uptr);
2735                 if (dptr == NULL)
2736                     dptr = find_dev (gbuf);
2737                 if (dptr != NULL) {
2738                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2739                     if (sim_log)
2740                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2741                     return r;
2742                     }
2743                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2744                     /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2745                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2746                          (cmdp->help))
2747                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2748                     }
2749                 else { /* HELP SHOW xxx (not device or unit) */
2750                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2751                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2752                         return SCPE_ARG;
2753                     return help_cmd_output (flag, shptr->help, NULL);
2754                     }
2755                 return SCPE_ARG;
2756                 }
2757             else
2758                 return SCPE_2MARG;
2759             }
2760         if (cmdp->help) {
2761             if (strcmp (cmdp->name, "HELP") == 0) {
2762 
2763 
2764 
2765 
2766 
2767 
2768 
2769 
2770 
2771 
2772 
2773 
2774 
2775 
2776 
2777 
2778 
2779 
2780 
2781 
2782 
2783 
2784 
2785 
2786 
2787 
2788 
2789 
2790 
2791 
2792 
2793 
2794 
2795 
2796                 }
2797             else {
2798                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2799                     sim_dflt_dev && sim_dflt_dev->help) {
2800                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2801                         if (sim_log)
2802                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2803                     }
2804                 }
2805             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2806             }
2807         else { /* no help so it is likely a command alias */
2808             CTAB *cmdpa;
2809             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2810                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2811                     sim_printf ("%s is an alias for the %s command:\n%s",
2812                                 cmdp->name, cmdpa->name, cmdpa->help);
2813                     break;
2814                     }
2815             if (cmdpa->name == NULL)                /* not found? */
2816                 sim_printf ("No help available for the %s command\n", cmdp->name);
2817             }
2818         }
2819     else {
2820         DEVICE *dptr;
2821         UNIT *uptr;
2822         t_stat r;
2823         dptr = find_unit (gbuf, &uptr);
2824         if (dptr == NULL) {
2825             dptr = find_dev (gbuf);
2826             if (dptr == NULL)
2827                 return SCPE_ARG;
2828             if (dptr->flags & DEV_DIS)
2829                 sim_printf ("Device %s is currently disabled\n", dptr->name);
2830             }
2831         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2832         if (sim_log)
2833             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2834         return r;
2835         }
2836     }
2837 else {
2838     fprint_help (stdout);
2839     if (sim_log)
2840         fprint_help (sim_log);
2841     }
2842 return SCPE_OK;
2843 }
2844 
2845 /* Spawn command */
2846 
2847 t_stat spawn_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2848 {
2849 t_stat status;
2850 if ((cptr == NULL) || (strlen (cptr) == 0))
2851     cptr = getenv("SHELL");
2852 if ((cptr == NULL) || (strlen (cptr) == 0))
2853     cptr = getenv("ComSpec");
2854 (void)fflush(stdout);                                   /* flush stdout */
2855 if (sim_log)                                            /* flush log if enabled */
2856     (void)fflush (sim_log);
2857 if (sim_deb)                                            /* flush debug if enabled */
2858     (void)fflush (sim_deb);
2859 status = system (cptr);
2860 
2861 return status;
2862 }
2863 
2864 /* Echo command */
2865 
2866 t_stat echo_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2867 {
2868 sim_printf ("%s\n", cptr);
2869 return SCPE_OK;
2870 }
2871 
2872 /*
2873  * DO command
2874  *
2875  * Note that SCPE_STEP ("Step expired") is considered a note and
2876  * not an error; it does not abort command execution when using -E.
2877  *
2878  * Inputs:
2879  *      flag    =   caller and nesting level indicator
2880  *      fcptr   =   filename and optional arguments, space-separated
2881  * Outputs:
2882  *      status  =   error status
2883  *
2884  * The "flag" input value indicates the source of the call, as follows:
2885  *
2886  *      -1      =   initialization file (no error if not found)
2887  *       0      =   command line file
2888  *       1      =   "DO" command
2889  *      >1      =   nested "DO" command
2890  */
2891 
2892 t_stat do_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2893 {
2894 return do_cmd_label (flag, fcptr, NULL);
2895 }
2896 
2897 static char *do_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2898 {
2899 static char cbuf[4*CBUFSIZE];
2900 
2901 (void)snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth],
2902                 sim_do_label[sim_do_depth] ? "::" : "",
2903                 sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "",
2904                 sim_goto_line[sim_do_depth]);
2905 return cbuf;
2906 }
2907 
2908 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
2909 {
2910 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2911 CONST char *cptr;
2912 FILE *fpin;
2913 CTAB *cmdp = NULL;
2914 int32 echo, nargs, errabort, i;
2915 int32 saved_sim_do_echo = sim_do_echo,
2916       saved_sim_show_message = sim_show_message,
2917       saved_sim_on_inherit = sim_on_inherit,
2918       saved_sim_quiet = sim_quiet;
2919 t_bool staying;
2920 t_stat stat, stat_nomessage;
2921 
2922 stat = SCPE_OK;
2923 staying = TRUE;
2924 if (flag > 0)                                           /* need switches? */
2925     GET_SWITCHES (fcptr);                               /* get switches */
2926 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
2927 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2928 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2929 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
2930 
2931 abuf[sizeof(abuf)-1] = '\0';
2932 strncpy (abuf, fcptr, sizeof(abuf)-1);
2933 c = abuf;
2934 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
2935 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
2936     while (sim_isspace (*c))                            /* skip blanks */
2937         c++;
2938     if (*c == 0)                                        /* all done? */
2939         do_arg [nargs++] = NULL;                        /* null argument */
2940     else {
2941         if (*c == '\'' || *c == '"')                    /* quoted string? */
2942             quote = *c++;
2943         else quote = 0;
2944         do_arg[nargs++] = c;                            /* save start */
2945         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2946             c++;
2947         if (*c)                                         /* term at quote/spc */
2948             *c++ = 0;
2949         }
2950     }                                                   /* end for */
2951 
2952 if (do_arg [0] == NULL)                                 /* need at least 1 */
2953     return SCPE_2FARG;
2954 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
2955     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
2956     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
2957         if (flag == 0)                                  /* cmd line file? */
2958              (void)fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2959         return SCPE_OPENERR;                            /* return failure */
2960         }
2961     }
2962 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2963     ++sim_do_depth;
2964     if (sim_on_inherit) {                               /* inherit ON condition actions? */
2965         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2966         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
2967             if (sim_on_actions[sim_do_depth-1][i]) {
2968                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2969                 if (NULL == sim_on_actions[sim_do_depth][i]) {
2970                     while (--i >= 0) {
2971                         FREE(sim_on_actions[sim_do_depth][i]);
2972                         sim_on_actions[sim_do_depth][i] = NULL;
2973                         }
2974                     sim_on_check[sim_do_depth] = 0;
2975                     sim_brk_clract ();                  /* defang breakpoint actions */
2976                     --sim_do_depth;                     /* unwind nesting */
2977                     fclose(fpin);
2978                     return SCPE_MEM;
2979                     }
2980                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2981                 }
2982             }
2983         }
2984     }
2985 
2986 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
2987 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
2988 sim_goto_line[sim_do_depth] = 0;
2989 if (label) {
2990     sim_gotofile = fpin;
2991     sim_do_echo = echo;
2992     stat = goto_cmd (0, label);
2993     if (stat != SCPE_OK) {
2994         strcpy(cbuf, "RETURN SCPE_ARG");
2995         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
2996         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
2997         goto Cleanup_Return;
2998         }
2999     }
3000 if (errabort)                                           /* -e flag? */
3001     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
3002 
3003 do {
3004     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
3005     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
3006         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
3007         sim_goto_line[sim_do_depth] += 1;
3008         }
3009     if (cptr != NULL && strlen(cptr) < sizeof(cbuf)) {  /* validate */
3010         sim_sub_args(cbuf, sizeof(cbuf), do_arg);       /* substitute args */
3011         }
3012     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
3013     if (cptr == NULL) {                                 /* EOF? */
3014         stat = SCPE_OK;                                 /* set good return */
3015         break;
3016         }
3017     if (*cptr == 0)                                     /* ignore blank */
3018         continue;
3019     if (echo)                                           /* echo if -v */
3020         sim_printf("%s> %s\n", do_position(), cptr);
3021     if (*cptr == ':')                                   /* ignore label */
3022         continue;
3023     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
3024     sim_switches = 0;                                   /* init switches */
3025     sim_gotofile = fpin;
3026     sim_do_echo = echo;
3027     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
3028         if (cmdp->action == &return_cmd)                /* RETURN command? */
3029             break;                                      /*    done! */
3030         if (cmdp->action == &do_cmd) {                  /* DO command? */
3031             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
3032                 stat = SCPE_NEST;
3033             else
3034                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
3035             }
3036         else
3037             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
3038         }
3039     else stat = SCPE_UNK;                               /* bad cmd given */
3040     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
3041     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
3042     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
3043     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
3044     if (cmdp)
3045       if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
3046           ((cmdp->action != &return_cmd) &&
3047            (cmdp->action != &goto_cmd) &&
3048            (cmdp->action != &on_cmd) &&
3049            (cmdp->action != &echo_cmd)))
3050         sim_last_cmd_stat = stat;                       /* save command error status */
3051     switch (stat) {
3052         case SCPE_AFAIL:
3053             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
3054                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
3055             break;
3056         case SCPE_EXIT:
3057             staying = FALSE;
3058             break;
3059         case SCPE_OK:
3060         case SCPE_STEP:
3061             break;
3062         default:
3063             break;
3064         }
3065     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
3066         (stat != SCPE_STEP)) {
3067         if (!echo && !sim_quiet &&                      /* report if not echoing */
3068             !stat_nomessage &&                          /* and not suppressing messages */
3069             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
3070             sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
3071             }
3072         }
3073     if (!stat_nomessage) {                              /* report error if not suppressed */
3074         if (cmdp && cmdp->message)                      /* special message handler */
3075             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
3076         else
3077             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
3078                 sim_printf ("%s\n", sim_error_text (stat));
3079         }
3080     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
3081         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
3082     if (staying &&
3083         (sim_on_check[sim_do_depth]) &&
3084         (stat != SCPE_OK) &&
3085         (stat != SCPE_STEP)) {
3086         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
3087             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
3088         else
3089             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
3090         }
3091     if (sim_vm_post != NULL)
3092         (*sim_vm_post) (TRUE);
3093     } while (staying);
3094 Cleanup_Return:
3095 if (fpin) //-V547
3096     fclose (fpin);                                      /* close file */
3097 sim_gotofile = NULL;
3098 if (flag >= 0) {
3099     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
3100     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
3101     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
3102     }
3103 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
3104 if ((flag >= 0) || (!sim_on_inherit)) {
3105     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
3106         FREE (sim_on_actions[sim_do_depth][i]);
3107         sim_on_actions[sim_do_depth][i] = NULL;
3108         }
3109     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
3110     }
3111 if (flag >= 0)
3112     --sim_do_depth;                                     /* unwind nesting */
3113 sim_brk_clract ();                                      /* defang breakpoint actions */
3114 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
3115     sim_string_to_stat (cptr, &stat);
3116     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
3117     if (sim_switches & SWMASK ('Q'))
3118         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
3119     return stat;                                        /* return with explicit return status */
3120     }
3121 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
3122 }
3123 
3124 /*
3125  * Substitute_args - replace %n tokens in 'instr' with the do command's arguments
3126  *                   and other environment variables
3127  *
3128  * Calling sequence
3129  * instr        =       input string
3130  * instr_size   =       sizeof input string buffer
3131  * do_arg[10]   =       arguments
3132  *
3133  * Token "%0" expands to the command file name.
3134  * Token %n (n being a single digit) expands to the n'th argument
3135  * Tonen %* expands to the whole set of arguments (%1 ... %9)
3136  *
3137  * The input sequence "\%" represents a literal "%", and "\\" represents a
3138  * literal "\".  All other character combinations are rendered literally.
3139  *
3140  * Omitted parameters result in null-string substitutions.
3141  *
3142  * A Tokens preceded and followed by % characters are expanded as environment
3143  * variables, and if one isn't found then can be one of several special
3144  * variables:
3145  *   %DATE%              yyyy-mm-dd
3146  *   %TIME%              hh:mm:ss
3147  *   %STIME%             hh_mm_ss
3148  *   %CTIME%             Www Mmm dd hh:mm:ss yyyy
3149  *   %STATUS%            Status value from the last command executed
3150  *   %TSTATUS%           The text form of the last status value
3151  *   %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
3152  *   %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
3153  *   %SIM_QUIET%         The Quiet mode of the current Do command file
3154  *   %SIM_MESSAGE%       The message display status of the current Do command file
3155  * Environment variable lookups are done first with the precise name between
3156  * the % characters and if that fails, then the name between the % characters
3157  * is upcased and a lookup of that value is attempted.
3158 
3159  * The first Space delimited token on the line is extracted in uppercase and
3160  * then looked up as an environment variable.  If found it the value is
3161  * substituted for the original string before expanding everything else.  If
3162  * it is not found, then the original beginning token on the line is left
3163  * untouched.
3164  */
3165 
3166 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
     /* [previous][next][first][last][top][bottom][index][help] */
3167 {
3168 char gbuf[CBUFSIZE];
3169 char *ip = instr, *op, *oend, *tmpbuf;
3170 const char *ap;
3171 char rbuf[CBUFSIZE];
3172 int i;
3173 time_t now;
3174 struct tm *tmnow;
3175 
3176 time(&now);
3177 tmnow = localtime(&now);
3178 tmpbuf = (char *)malloc(instr_size);
3179 if (!tmpbuf)
3180   {
3181      (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3182                    __func__, __FILE__, __LINE__);
3183 #if defined(USE_BACKTRACE)
3184 # if defined(SIGUSR2)
3185      (void)raise(SIGUSR2);
3186      /*NOTREACHED*/ /* unreachable */
3187 # endif /* if defined(SIGUSR2) */
3188 #endif /* if defined(USE_BACKTRACE) */
3189      abort();
3190   }
3191 op = tmpbuf;
3192 oend = tmpbuf + instr_size - 2;
3193 while (sim_isspace (*ip))                               /* skip leading spaces */
3194     *op++ = *ip++;
3195 for (; *ip && (op < oend); ) {
3196     if ((ip [0] == '\\') &&                             /* literal escape? */
3197         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
3198         ip++;                                           /* skip '\' */
3199         *op++ = *ip++;                                  /* copy escaped char */
3200         }
3201     else
3202         if ((*ip == '%') &&
3203             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
3204             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
3205                 ap = do_arg[ip[1] - '0'];
3206                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
3207                     if (do_arg[i] == NULL) {
3208                         ap = NULL;
3209                         break;
3210                         }
3211                 ip = ip + 2;
3212                 }
3213             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
3214                 (void)memset (rbuf, '\0', sizeof(rbuf));
3215                 ap = rbuf;
3216                 for (i=1; i<=9; ++i)
3217                     if (do_arg[i] == NULL)
3218                         break;
3219                     else
3220                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
3221                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
3222                                 char quote = '"';
3223                                 if (strchr(do_arg[i], quote))
3224                                     quote = '\'';
3225                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"",
3226                                               (i != 1) ? " " : "", quote,
3227                                               do_arg[i], quote);
3228                                 }
3229                             else
3230                                 (void)sprintf(&rbuf[strlen(rbuf)], "%s%s",
3231                                               (i != 1) ? " " : "", do_arg[i]);
3232                             }
3233                         else
3234                             break;
3235                 ip = ip + 2;
3236                 }
3237             else {                                      /* environment variable */
3238                 ap = NULL;
3239                 (void)get_glyph_nc (ip+1, gbuf, '%');   /* first try using the literal name */
3240                 ap = getenv(gbuf);
3241                 if (!ap) {
3242                     (void)get_glyph (ip+1, gbuf, '%');  /* now try using the upcased name */
3243                     ap = getenv(gbuf);
3244                     }
3245                 ip += 1 + strlen (gbuf);
3246                 if (*ip == '%') ++ip;
3247                 if (!ap) {
3248                     /* ISO 8601 format date/time info */
3249                     if (!strcmp ("DATE", gbuf)) {
3250                         (void)sprintf (rbuf, "%4d-%02d-%02d",
3251                                        tmnow->tm_year + 1900,
3252                                        tmnow->tm_mon  + 1,
3253                                        tmnow->tm_mday);
3254                         ap = rbuf;
3255                         }
3256                     else if (!strcmp ("TIME", gbuf)) {
3257                         (void)sprintf (rbuf, "%02d:%02d:%02d",
3258                                        tmnow->tm_hour,
3259                                        tmnow->tm_min,
3260                                        tmnow->tm_sec);
3261                         ap = rbuf;
3262                         }
3263                     else if (!strcmp ("DATETIME", gbuf)) {
3264                         (void)sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d",
3265                                        tmnow->tm_year + 1900,
3266                                        tmnow->tm_mon  + 1,
3267                                        tmnow->tm_mday,
3268                                        tmnow->tm_hour,
3269                                        tmnow->tm_min,
3270                                        tmnow->tm_sec);
3271                         ap = rbuf;
3272                         }
3273                     /* Locale oriented formatted date/time info */
3274                     if (!strcmp ("LDATE", gbuf)) {
3275                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
3276                         ap = rbuf;
3277                         }
3278                     else if (!strcmp ("LTIME", gbuf)) {
3279                         strftime (rbuf, sizeof(rbuf), "%I:%M:%S %p", tmnow);
3280                         ap = rbuf;
3281                         }
3282                     else if (!strcmp ("CTIME", gbuf)) {
3283                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
3284                         ap = rbuf;
3285                         }
3286                     /* Separate Date/Time info */
3287                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
3288                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
3289                         ap = rbuf;
3290                         }
3291                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3292                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
3293                         ap = rbuf;
3294                         }
3295                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
3296                         (void)sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
3297                         ap = rbuf;
3298                         }
3299                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
3300                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
3301                         int year = tmnow->tm_year + 1900;
3302                         int days = year - 2001;
3303                         int leaps = days/4 - days/100 + days/400;
3304                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
3305                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
3306                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
3307                                               96, 80, 92, 76, 88, 72, 84};
3308                         int cal_year = years[selector];
3309 
3310                         if (!strcmp ("DATE_19XX_YY", gbuf))
3311                             (void)sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
3312                         else
3313                             (void)sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
3314                         ap = rbuf;
3315                         }
3316                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
3317                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
3318                         ap = rbuf;
3319                         }
3320                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
3321                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
3322                         ap = rbuf;
3323                         }
3324                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
3325                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
3326                         ap = rbuf;
3327                         }
3328                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
3329                         (void)sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
3330                         ap = rbuf;
3331                         }
3332                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
3333                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
3334                         int iso_yr = tmnow->tm_year + 1900;
3335                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
3336 
3337                         if (iso_wk == 0) {
3338                             iso_yr = iso_yr - 1;
3339                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
3340                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
3341                             }
3342                         else
3343                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
3344                                 ++iso_yr;
3345                                 iso_wk = 1;
3346                                 }
3347                         if (!strcmp ("DATE_WW", gbuf))
3348                             (void)sprintf (rbuf, "%02d", iso_wk);
3349                         else
3350                             (void)sprintf (rbuf, "%04d", iso_yr);
3351                         ap = rbuf;
3352                         }
3353                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
3354                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
3355                         ap = rbuf;
3356                         }
3357                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
3358                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
3359                         ap = rbuf;
3360                         }
3361                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
3362                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
3363                         ap = rbuf;
3364                         }
3365                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
3366                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
3367                         ap = rbuf;
3368                         }
3369                     else if (!strcmp ("STATUS", gbuf)) {
3370                         (void)sprintf (rbuf, "%08X", sim_last_cmd_stat);
3371                         ap = rbuf;
3372                         }
3373                     else if (!strcmp ("TSTATUS", gbuf)) {
3374                         (void)sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
3375                         ap = rbuf;
3376                         }
3377                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
3378                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3379                         ap = rbuf;
3380                         }
3381                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
3382                         (void)sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3383                         ap = rbuf;
3384                         }
3385                     else if (!strcmp ("SIM_LOCALOPC", gbuf)) {
3386                         (void)sprintf (rbuf, "%s", sim_localopc ? "1" : "");
3387                         ap = rbuf;
3388                         }
3389                     else if (!strcmp ("SIM_QUIET", gbuf)) {
3390                         (void)sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
3391                         ap = rbuf;
3392                         }
3393                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
3394                         (void)sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
3395                         ap = rbuf;
3396                         }
3397                     else if (!strcmp ("HOSTID", gbuf)) {
3398 #if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__)
3399                         (void)sprintf (rbuf, "%ld", (long)gethostid());
3400 #else
3401                         (void)sprintf (rbuf, "00000000");
3402 #endif /* if defined(HAVE_UNISTD) && !defined(__HAIKU__) && !defined(__ANDROID__) && !defined(__serenity__) */
3403                         ap = rbuf;
3404                         }
3405                     else if (!strcmp ("UID", gbuf)) {
3406 #if defined(HAVE_UNISTD)
3407                         (void)sprintf (rbuf, "%ld", (long)getuid());
3408 #else
3409                         (void)sprintf (rbuf, "0");
3410 #endif /* if defined(HAVE_UNISTD) */
3411                         ap = rbuf;
3412                         }
3413                     else if (!strcmp ("GID", gbuf)) {
3414 #if defined(HAVE_UNISTD)
3415                         (void)sprintf (rbuf, "%ld", (long)getgid());
3416 #else
3417                         (void)sprintf (rbuf, "0");
3418 #endif /* if defined(HAVE_UNISTD) */
3419                         ap = rbuf;
3420                         }
3421                     else if (!strcmp ("EUID", gbuf)) {
3422 #if defined(HAVE_UNISTD)
3423                         (void)sprintf (rbuf, "%ld", (long)geteuid());
3424 #else
3425                         (void)sprintf (rbuf, "0");
3426 #endif /* if defined(HAVE_UNISTD) */
3427                         ap = rbuf;
3428                         }
3429                     else if (!strcmp ("EGID", gbuf)) {
3430 #if defined(HAVE_UNISTD)
3431                         (void)sprintf (rbuf, "%ld", (long)getegid());
3432 #else
3433                         (void)sprintf (rbuf, "0");
3434 #endif /* if defined(HAVE_UNISTD) */
3435                         ap = rbuf;
3436                         }
3437                     else if (!strcmp ("PID", gbuf)) {
3438 #if defined(HAVE_UNISTD)
3439                         (void)sprintf (rbuf, "%ld", (long)getpid());
3440 #else
3441                         (void)sprintf (rbuf, "0");
3442 #endif /* if defined(HAVE_UNISTD) */
3443                         ap = rbuf;
3444                         }
3445                     else if (!strcmp ("PPID", gbuf)) {
3446 #if defined(HAVE_UNISTD)
3447                         (void)sprintf (rbuf, "%ld", (long)getppid());
3448 #else
3449                         (void)sprintf (rbuf, "0");
3450 #endif /* if defined(HAVE_UNISTD) */
3451                         ap = rbuf;
3452                         }
3453                     else if (!strcmp ("PGID", gbuf)) {
3454 #if defined(HAVE_UNISTD)
3455                         (void)sprintf (rbuf, "%ld", (long)getpgid(getpid()));
3456 #else
3457                         (void)sprintf (rbuf, "0");
3458 #endif /* if defined(HAVE_UNISTD) */
3459                         ap = rbuf;
3460                         }
3461                     else if (!strcmp ("SID", gbuf)) {
3462 #if defined(HAVE_UNISTD)
3463                         (void)sprintf (rbuf, "%ld", (long)getsid(getpid()));
3464 #else
3465                         (void)sprintf (rbuf, "0");
3466 #endif /* if defined(HAVE_UNISTD) */
3467                         ap = rbuf;
3468                         }
3469                     else if (!strcmp ("ENDIAN", gbuf)) {
3470 #if ( defined(DECLITEND) && DECLITEND == 1 )
3471                         (void)sprintf (rbuf, "LITTLE");
3472 #elif ( defined(DECLITEND) && DECLITEND == 0 )
3473                         (void)sprintf (rbuf, "BIG");
3474 #else
3475                         (void)sprintf (rbuf, "UNKNOWN");
3476 #endif /* if ( defined(DECLITEND) && DECLITEND == 1 ) */
3477                         ap = rbuf;
3478                         }
3479                     else if (!strcmp("SIM_NAME", gbuf)) {
3480                         (void)sprintf (rbuf, "%s", sim_name);
3481                         ap = rbuf;
3482                         }
3483                     else if (!strcmp("SIM_VERSION", gbuf)) {
3484 #if defined(VER_H_GIT_VERSION)
3485                         (void)sprintf (rbuf, "%s", VER_H_GIT_VERSION);
3486 #else
3487                         (void)sprintf (rbuf, "UNKNOWN");
3488 #endif /* if defined(VER_H_GIT_VERSION) */
3489                         ap = rbuf;
3490                         }
3491                     else if (!strcmp("SIM_HASH", gbuf)) {
3492 #if defined(VER_H_GIT_HASH)
3493                         (void)sprintf (rbuf, "%s", VER_H_GIT_HASH);
3494 #else
3495                         (void)sprintf (rbuf, "0000000000000000000000000000000000000000");
3496 #endif /* if defined(VER_H_GIT_HASH) */
3497                         ap = rbuf;
3498                         }
3499                     else if (!strcmp("SIM_RELT", gbuf)) {
3500 #if defined(VER_H_GIT_RELT)
3501                         (void)sprintf (rbuf, "%s", VER_H_GIT_RELT);
3502 #else
3503                         (void)sprintf (rbuf, "X");
3504 #endif /* if defined(VER_H_GIT_RELT) */
3505                         ap = rbuf;
3506                         }
3507                     else if (!strcmp("SIM_DATE", gbuf)) {
3508 #if defined(VER_H_GIT_DATE)
3509                         (void)sprintf (rbuf, "%s", VER_H_GIT_DATE);
3510 #else
3511                         (void)sprintf (rbuf, "UNKNOWN");
3512 #endif /* if defined(VER_H_GIT_DATE) */
3513                         ap = rbuf;
3514                         }
3515                     else if ( (!strcmp("CPUS", gbuf)) \
3516                       || (!strcmp("SIM_PROCESSORS", gbuf) ) ) {
3517                         (void)sprintf(rbuf, "%u", nprocs);
3518                         ap = rbuf;
3519                         }
3520                     }
3521                 }
3522             if (ap) {                                   /* non-null arg? */
3523                 while (*ap && (op < oend))              /* copy the argument */
3524                     *op++ = *ap++;
3525                 }
3526             }
3527         else
3528             *op++ = *ip++;
3529     }
3530 *op = 0;                                                /* term buffer */
3531 strcpy (instr, tmpbuf);
3532 FREE (tmpbuf);
3533 return;
3534 }
3535 
3536 static
3537 int sim_cmp_string (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
3538 {
3539 long int v1, v2;
3540 char *ep1, *ep2;
3541 
3542 v1 = strtol(s1+1, &ep1, 0);
3543 v2 = strtol(s2+1, &ep2, 0);
3544 if ((ep1 != s1 + strlen (s1) - 1) ||
3545     (ep2 != s2 + strlen (s2) - 1))
3546     return strcmp (s1, s2);
3547 if (v1 == v2)
3548     return 0;
3549 if (v1 < v2)
3550     return -1;
3551 return 1;
3552 }
3553 
3554 /* Assert command */
3555 
3556 t_stat assert_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3557 {
3558 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3559 CONST char *tptr, *gptr;
3560 REG *rptr;
3561 uint32 idx = 0;
3562 t_value val;
3563 t_stat r;
3564 t_bool Not = FALSE;
3565 t_bool result;
3566 t_addr addr = 0;
3567 t_stat reason = SCPE_AFAIL; /* default fail reason */
3568 
3569 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3570                                                         /* get sw, default */
3571 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3572 # pragma diagnostic push
3573 # pragma diag_suppress = integer_sign_change
3574 #endif
3575 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3576 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3577 # pragma diagnostic pop
3578 #endif
3579 if (*cptr == 0)                                         /* must be more */
3580     return SCPE_2FARG;
3581 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3582 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3583     Not = TRUE;                                         /* remember that, and */
3584     cptr = (CONST char *)tptr;
3585     }
3586 if (*cptr == '"') {                                     /* quoted string comparison? */
3587     char op[CBUFSIZE];
3588     static struct {
3589         const char *op;
3590         int aval;
3591         int bval;
3592         t_bool invert;
3593         } *optr, compare_ops[] =
3594         {
3595             { "==",   0,  0, FALSE },
3596             { "EQU",  0,  0, FALSE },
3597             { "!=",   0,  0, TRUE  },
3598             { "NEQ",  0,  0, TRUE  },
3599             { "<",   -1, -1, FALSE },
3600             { "LSS", -1, -1, FALSE },
3601             { "<=",   0, -1, FALSE },
3602             { "LEQ",  0, -1, FALSE },
3603             { ">",    1,  1, FALSE },
3604             { "GTR",  1,  1, FALSE },
3605             { ">=",   0,  1, FALSE },
3606             { "GEQ",  0,  1, FALSE },
3607             { NULL }
3608         };
3609 
3610     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3611                                                     /* get first string */
3612     if (!*tptr)
3613         return SCPE_2FARG;
3614     cptr += strlen (gbuf);
3615     while (sim_isspace (*cptr))                     /* skip spaces */
3616         ++cptr;
3617     (void)get_glyph (cptr, op, '"');
3618     for (optr = compare_ops; optr->op; optr++)
3619         if (0 == strcmp (op, optr->op))
3620             break;
3621     if (!optr->op)
3622         return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3623     cptr += strlen (op);
3624     while (sim_isspace (*cptr))                         /* skip spaces */
3625         ++cptr;
3626     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3627                                                         /* get second string */
3628     if (*cptr) {                                        /* more? */
3629         if (flag)                                       /* ASSERT has no more args */
3630             return SCPE_2MARG;
3631         }
3632     else {
3633         if (!flag)
3634             return SCPE_2FARG;                          /* IF needs actions! */
3635         }
3636     result = sim_cmp_string (gbuf, gbuf2);
3637     result = ((result == optr->aval) || (result == optr->bval));
3638     if (optr->invert)
3639         result = !result;
3640     }
3641 else {
3642     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3643     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3644     if (rptr) {                                         /* got register? */
3645         if (*gptr == '[') {                             /* subscript? */
3646             if (rptr->depth <= 1)                       /* array register? */
3647                 return SCPE_ARG;
3648             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3649             if ((gptr == tptr) || (*tptr++ != ']'))
3650                 return SCPE_ARG;
3651             gptr = tptr;                                /* update */
3652             }
3653         else idx = 0;                                   /* not array */
3654         if (idx >= rptr->depth)                         /* validate subscript */
3655             return SCPE_SUB;
3656         }
3657     else {                                              /* not reg, check for memory */
3658         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3659             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3660         else if (sim_dfdev) //-V547
3661             addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix); //-V547
3662         if (gbuf == gptr)                               /* error? */
3663             return SCPE_NXREG;
3664         }
3665     if (*gptr != 0)                                     /* more? must be search */
3666         (void)get_glyph (gptr, gbuf, 0);
3667     else {
3668         if (*cptr == 0)                                 /* must be more */
3669             return SCPE_2FARG;
3670         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3671         }
3672     if (*cptr) {                                        /* more? */
3673         if (flag)                                       /* ASSERT has no more args */
3674             return SCPE_2MARG;
3675         }
3676     else {
3677         if (!flag)
3678             return SCPE_2FARG;                          /* IF needs actions! */
3679         }
3680     if (rptr) {                                         /* Handle register case */
3681 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3682 # pragma diagnostic push
3683 # pragma diag_suppress = integer_sign_change
3684 #endif
3685         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3686             (sim_stabr.boolop == -1))                   /* relational op reqd */
3687             return SCPE_MISVAL;
3688 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3689 # pragma diagnostic pop
3690 #endif
3691         val = get_rval (rptr, idx);                     /* get register value */
3692         result = test_search (&val, &sim_stabr);        /* test condition */
3693         }
3694     else {                                              /* Handle memory case */
3695 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3696 # pragma diagnostic push
3697 # pragma diag_suppress = integer_sign_change
3698 #endif
3699         if (sim_dfdev)
3700             if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3701                 (sim_staba.boolop == -1))                    /* relational op reqd */
3702                 return SCPE_MISVAL;
3703 #if defined(__NVCOMPILER) || defined(__NVCOMPILER_LLVM__) || defined(__PGI) || defined(__PGLLVM__)
3704 # pragma diagnostic pop
3705 #endif
3706         if (sim_dfdev)
3707             reason = get_aval (addr, sim_dfdev, sim_dfunit); /* get data */
3708         if (reason != SCPE_OK)                          /* return if error */
3709             return reason;
3710         result = test_search (sim_eval, &sim_staba);    /* test condition */
3711         }
3712     }
3713 if (Not ^ result) {
3714     if (!flag)
3715         sim_brk_setact (cptr);                          /* set up IF actions */
3716     }
3717 else
3718     if (flag)
3719         return SCPE_AFAIL;                              /* return assert status */
3720 return SCPE_OK;
3721 }
3722 
3723 /* Send command */
3724 
3725 t_stat send_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3726 {
3727 char gbuf[CBUFSIZE];
3728 CONST char *tptr;
3729 uint8 dbuf[CBUFSIZE];
3730 uint32 dsize = 0;
3731 uint32 delay = 0;
3732 uint32 after = 0;
3733 t_stat r;
3734 SEND *snd = NULL;
3735 
3736 GET_SWITCHES (cptr);                                    /* get switches */
3737 tptr = get_glyph (cptr, gbuf, ',');
3738 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3739     r = tmxr_locate_line_send (gbuf, &snd);
3740     if (r != SCPE_OK)
3741       return r;
3742     cptr = tptr;
3743     tptr = get_glyph (tptr, gbuf, ',');
3744     }
3745 else
3746     snd = sim_cons_get_send ();
3747 
3748 while (*cptr) {
3749     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3750         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3751         if (r != SCPE_OK)
3752             return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3753         cptr = tptr;
3754         tptr = get_glyph (cptr, gbuf, ',');
3755         continue;
3756         }
3757     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3758         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3759         if (r != SCPE_OK)
3760             return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3761         cptr = tptr;
3762         tptr = get_glyph (cptr, gbuf, ',');
3763         continue;
3764         }
3765     if ((*cptr == '"') || (*cptr == '\''))
3766         break;
3767     return SCPE_ARG;
3768     }
3769 if (*cptr) {
3770     if ((*cptr != '"') && (*cptr != '\'')) //-V560
3771         return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3772     cptr = get_glyph_quoted (cptr, gbuf, 0);
3773     if (*cptr != '\0')
3774         return SCPE_2MARG;                  /* No more arguments */
3775 
3776     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3777         return sim_messagef (SCPE_ARG, "Invalid String\n");
3778     }
3779 if ((dsize == 0) && (delay == 0) && (after == 0))
3780     return SCPE_2FARG;
3781 return sim_send_input (snd, dbuf, dsize, after, delay);
3782 }
3783 
3784 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3785 {
3786 char gbuf[CBUFSIZE];
3787 CONST char *tptr;
3788 t_stat r;
3789 SEND *snd = NULL;
3790 
3791 tptr = get_glyph (cptr, gbuf, ',');
3792 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3793     r = tmxr_locate_line_send (gbuf, &snd);
3794     if (r != SCPE_OK)
3795       return r;
3796     cptr = tptr;
3797     }
3798 else
3799     snd = sim_cons_get_send ();
3800 if (*cptr)
3801     return SCPE_2MARG;
3802 return sim_show_send_input (st, snd);
3803 }
3804 
3805 t_stat expect_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3806 {
3807 char gbuf[CBUFSIZE];
3808 CONST char *tptr;
3809 EXPECT *exp = NULL;
3810 
3811 GET_SWITCHES (cptr);                                    /* get switches */
3812 tptr = get_glyph (cptr, gbuf, ',');
3813 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3814     cptr = tptr;
3815 } else {
3816     exp = sim_cons_get_expect ();
3817 }
3818 
3819 if (flag) {
3820     return sim_set_expect (exp, cptr);
3821 } else {
3822     if (exp == NULL) {
3823         exp = sim_cons_get_expect();
3824     }
3825     return sim_set_noexpect (exp, cptr);
3826 }
3827 }
3828 
3829 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3830 {
3831 char gbuf[CBUFSIZE];
3832 CONST char *tptr;
3833 EXPECT *exp = NULL;
3834 t_stat r;
3835 
3836 tptr = get_glyph (cptr, gbuf, ',');
3837 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3838     r = tmxr_locate_line_expect (gbuf, &exp);
3839     if (r != SCPE_OK)
3840         return r;
3841     cptr = tptr;
3842     }
3843 else
3844     exp = sim_cons_get_expect ();
3845 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3846     return SCPE_ARG;            /* String must be quote delimited */
3847 tptr = get_glyph_quoted (cptr, gbuf, 0);
3848 if (*tptr != '\0')
3849     return SCPE_2MARG;          /* No more arguments */
3850 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3851     return SCPE_ARG;            /* String must be quote delimited */
3852 return sim_exp_show (st, exp, gbuf);
3853 }
3854 
3855 /* Goto command */
3856 
3857 t_stat goto_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3858 {
3859 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3860 const char *cptr;
3861 long fpos;
3862 int32 saved_do_echo = sim_do_echo;
3863 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3864 
3865 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3866 (void)get_glyph (fcptr, gbuf1, 0);
3867 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
3868 fpos = ftell(sim_gotofile);                             /* Save start position */
3869 rewind(sim_gotofile);                                   /* start search for label */
3870 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
3871 sim_do_echo = 0;                                        /* Don't echo while searching for label */
3872 while (1) {
3873     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3874     if (cptr == NULL) break;                            /* exit on eof */
3875     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
3876     if (*cptr == 0) continue;                           /* ignore blank */
3877     if (*cptr != ':') continue;                         /* ignore non-labels */
3878     ++cptr;                                             /* skip : */
3879     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
3880     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
3881     if (0 == strcmp(gbuf, gbuf1)) {
3882         sim_brk_clract ();                              /* goto defangs current actions */
3883         sim_do_echo = saved_do_echo;                    /* restore echo mode */
3884         if (sim_do_echo)                                /* echo if -v */
3885             sim_printf("%s> %s\n", do_position(), cbuf);
3886         return SCPE_OK;
3887         }
3888     }
3889 sim_do_echo = saved_do_echo;                       /* restore echo mode         */
3890 fseek(sim_gotofile, fpos, SEEK_SET);               /* restore start position    */
3891 sim_goto_line[sim_do_depth] = saved_goto_line;     /* restore start line number */
3892 return SCPE_ARG;
3893 }
3894 
3895 /* Return command */
3896 
3897 /* The return command is invalid unless encountered in a do_cmd context,    */
3898 /* and in that context, it is handled as a special case inside of do_cmd()  */
3899 /* and not dispatched here, so if we get here a return has been issued from */
3900 /* interactive input */
3901 
3902 t_stat return_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3903 {
3904 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3905 }
3906 
3907 /* Shift command */
3908 
3909 /* The shift command is invalid unless encountered in a do_cmd context,    */
3910 /* and in that context, it is handled as a special case inside of do_cmd() */
3911 /* and not dispatched here, so if we get here a shift has been issued from */
3912 /* interactive input (it is not valid interactively since it would have to */
3913 /* mess with the program's argv which is owned by the C runtime library    */
3914 
3915 t_stat shift_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3916 {
3917 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3918 }
3919 
3920 /* Call command */
3921 
3922 /* The call command is invalid unless encountered in a do_cmd context,     */
3923 /* and in that context, it is handled as a special case inside of do_cmd() */
3924 /* and not dispatched here, so if we get here a call has been issued from  */
3925 /* interactive input                                                       */
3926 
3927 t_stat call_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3928 {
3929 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3930 const char *cptr;
3931 
3932 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3933 cptr = get_glyph (fcptr, gbuf, 0);
3934 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
3935 (void)snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3936 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
3937 return do_cmd_label (flag, cbuf, gbuf);
3938 }
3939 
3940 /* On command */
3941 
3942 t_stat on_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3943 {
3944 char gbuf[CBUFSIZE];
3945 t_stat cond;
3946 
3947 cptr = get_glyph (cptr, gbuf, 0);
3948 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
3949 if (0 == strcmp("ERROR", gbuf))
3950     cond = 0;
3951 else
3952     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3953         return SCPE_ARG;
3954 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
3955     FREE(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
3956     sim_on_actions[sim_do_depth][cond] = NULL; }
3957 else {
3958     sim_on_actions[sim_do_depth][cond] =
3959         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3960     if (!sim_on_actions[sim_do_depth][cond])
3961       {
3962         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3963                        __func__, __FILE__, __LINE__);
3964 #if defined(USE_BACKTRACE)
3965 # if defined(SIGUSR2)
3966         (void)raise(SIGUSR2);
3967         /*NOTREACHED*/ /* unreachable */
3968 # endif /* if defined(SIGUSR2) */
3969 #endif /* if defined(USE_BACKTRACE) */
3970         abort();
3971       }
3972     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3973     }
3974 return SCPE_OK;
3975 }
3976 
3977 /* noop command */
3978 
3979 t_stat noop_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3980 {
3981 if (cptr && (*cptr != 0))                               /* now eol? */
3982     return SCPE_2MARG;
3983 return SCPE_OK;                                         /* we're happy doing nothing */
3984 }
3985 
3986 /* Set on/noon routine */
3987 
3988 t_stat set_on (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3989 {
3990 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
3991     char gbuf[CBUFSIZE];
3992 
3993     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
3994     if (((MATCH_CMD(gbuf,"INHERIT")) &&
3995          (MATCH_CMD(gbuf,"NOINHERIT"))) || //-V600
3996         (*cptr))
3997         return SCPE_2MARG;
3998     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT"))) //-V560
3999         sim_on_inherit = 1;
4000     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT"))) //-V560
4001         sim_on_inherit = 0;
4002     return SCPE_OK;
4003     }
4004 if (cptr && (*cptr != 0))                               /* now eol? */
4005     return SCPE_2MARG;
4006 sim_on_check[sim_do_depth] = flag;
4007 if ((sim_do_depth != 0) &&
4008     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
4009     sim_on_actions[sim_do_depth][0] =                   /* No, so make "RETURN" */
4010         (char *)malloc(1+strlen("RETURN"));             /* be the default action */
4011     strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
4012     }
4013 if ((sim_do_depth != 0) &&
4014     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
4015     sim_on_actions[sim_do_depth][SCPE_AFAIL] =          /* No, so make "RETURN" */
4016         (char *)malloc(1+strlen("RETURN"));             /* be the action */
4017     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
4018     }
4019 return SCPE_OK;
4020 }
4021 
4022 /* Set verify/noverify routine */
4023 
4024 t_stat set_verify (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4025 {
4026 if (cptr && (*cptr != 0))                               /* now eol? */
4027     return SCPE_2MARG;
4028 if (flag == sim_do_echo)                                /* already set correctly? */
4029     return SCPE_OK;
4030 sim_do_echo = flag;
4031 return SCPE_OK;
4032 }
4033 
4034 /* Set message/nomessage routine */
4035 
4036 t_stat set_message (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4037 {
4038 if (cptr && (*cptr != 0))                               /* now eol? */
4039     return SCPE_2MARG;
4040 if (flag == sim_show_message)                           /* already set correctly? */
4041     return SCPE_OK;
4042 sim_show_message = flag;
4043 return SCPE_OK;
4044 }
4045 
4046 /* Set localopc/nolocalopc routine */
4047 
4048 t_stat set_localopc (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4049 {
4050 if (cptr && (*cptr != 0))                               /* now eol? */
4051     return SCPE_2MARG;
4052 if (flag == sim_localopc)                               /* already set correctly? */
4053     return SCPE_OK;
4054 sim_localopc = flag;
4055 return SCPE_OK;
4056 }
4057 /* Set quiet/noquiet routine */
4058 
4059 t_stat set_quiet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4060 {
4061 if (cptr && (*cptr != 0))                               /* now eol? */
4062     return SCPE_2MARG;
4063 if (flag == sim_quiet)                                  /* already set correctly? */
4064     return SCPE_OK;
4065 sim_quiet = flag;
4066 return SCPE_OK;
4067 }
4068 
4069 /* Set environment routine */
4070 
4071 t_stat sim_set_environment (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4072 {
4073 char varname[CBUFSIZE];
4074 
4075 if ((NULL == cptr) || (*cptr == 0))                            /* now eol? */
4076     return SCPE_2FARG;
4077 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
4078 setenv(varname, cptr, 1);
4079 return SCPE_OK;
4080 }
4081 
4082 /* Set command */
4083 
4084 t_stat set_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4085 {
4086 uint32 lvl = 0;
4087 t_stat r;
4088 char gbuf[CBUFSIZE], *cvptr;
4089 CONST char *svptr;
4090 DEVICE *dptr;
4091 UNIT *uptr;
4092 MTAB *mptr;
4093 CTAB *gcmdp;
4094 C1TAB *ctbr = NULL, *glbr;
4095 
4096 GET_SWITCHES (cptr);                                    /* get switches */
4097 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
4098     return SCPE_2FARG;
4099 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
4100 
4101 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4102     uptr = dptr->units;                                 /* first unit */
4103     ctbr = set_dev_tab;                                 /* global table */
4104     lvl = MTAB_VDV;                                     /* device match */
4105     GET_SWITCHES (cptr);                                /* get more switches */
4106     }
4107 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4108     if (uptr == NULL)                                   /* invalid unit */
4109         return SCPE_NXUN;
4110     ctbr = set_unit_tab;                                /* global table */
4111     lvl = MTAB_VUN;                                     /* unit match */
4112     GET_SWITCHES (cptr);                                /* get more switches */
4113     }
4114 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
4115     GET_SWITCHES (cptr);                                /* get more switches */
4116     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
4117     }
4118 else {
4119     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4120         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4121             *cvptr++ = 0;
4122         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
4123             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
4124                 dptr = sim_dflt_dev;
4125                 cptr = svptr;
4126                 while (sim_isspace(*cptr))
4127                     ++cptr;
4128                 break;
4129                 }
4130             }
4131         }
4132     if (!dptr)
4133         return SCPE_NXDEV;                              /* no match */
4134     lvl = MTAB_VDV;                                     /* device match */
4135     uptr = dptr->units;                                 /* first unit */
4136     }
4137 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4138     return SCPE_2FARG;
4139 GET_SWITCHES (cptr);                                    /* get more switches */
4140 
4141 while (*cptr != 0) {                                    /* do all mods */
4142     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
4143     if (0 == strcmp (gbuf, ";"))
4144         break;
4145     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4146         *cvptr++ = 0;
4147     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4148         if ((mptr->mstring) &&                          /* match string */
4149             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
4150             if (mptr->mask & MTAB_XTD) {                /* extended? */
4151                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
4152                     return SCPE_ARG;
4153                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
4154                     return SCPE_UDIS;                   /* unit disabled? */
4155                 if (mptr->valid) {                      /* validation rtn? */
4156                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
4157                         svptr = get_glyph_quoted (svptr, gbuf, ',');
4158                         if ((cvptr = strchr (gbuf, '='))) {
4159                             *cvptr++ = 0;
4160                             cptr = svptr;
4161                             }
4162                         }
4163                     else {
4164                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
4165                             (void)get_glyph_nc (svptr, gbuf, ',');
4166                             if ((cvptr = strchr (gbuf, '=')))
4167                                 *cvptr++ = 0;
4168                             }
4169                         }
4170                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
4171                     if (r != SCPE_OK)
4172                         return r;
4173                     }
4174                 else if (!mptr->desc)                   /* value desc? */
4175                     break;
4176                 else if (cvptr)                         /* = value? */
4177                     return SCPE_ARG;
4178                 else *((int32 *) mptr->desc) = mptr->match;
4179                 }                                       /* end if xtd */
4180             else {                                      /* old style */
4181                 if (cvptr)                              /* = value? */
4182                     return SCPE_ARG;
4183                 if (uptr->flags & UNIT_DIS)             /* disabled? */
4184                      return SCPE_UDIS;
4185                 if ((mptr->valid) &&                    /* invalid? */
4186                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
4187                     return r;
4188                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
4189                     (mptr->match & mptr->mask);         /* set new value */
4190                 }                                       /* end else xtd */
4191             break;                                      /* terminate for */
4192             }                                           /* end if match */
4193         }                                               /* end for */
4194     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4195         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
4196             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
4197             if (r != SCPE_OK)
4198                 return r;
4199             }
4200         else if (!dptr->modifiers)                      /* no modifiers? */
4201             return SCPE_NOPARAM;
4202         else return SCPE_NXPAR;
4203         }                                               /* end if no mat */
4204     }                                                   /* end while */
4205 return SCPE_OK;                                         /* done all */
4206 }
4207 
4208 /* Match CTAB/CTAB1 name */
4209 
4210 CTAB *find_ctab (CTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4211 {
4212 if (!tab)
4213     return NULL;
4214 for (; tab->name != NULL; tab++) {
4215     if (MATCH_CMD (gbuf, tab->name) == 0)
4216         return tab;
4217     }
4218 return NULL;
4219 }
4220 
4221 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4222 {
4223 if (!tab)
4224     return NULL;
4225 for (; tab->name != NULL; tab++) {
4226     if (MATCH_CMD (gbuf, tab->name) == 0)
4227         return tab;
4228     }
4229 return NULL;
4230 }
4231 
4232 /* Set device data radix routine */
4233 
4234 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4235 {
4236 if (cptr)
4237     return SCPE_ARG;
4238 dptr->dradix = flag & 037;
4239 return SCPE_OK;
4240 }
4241 
4242 /* Set device enabled/disabled routine */
4243 
4244 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4245 {
4246 UNIT *up;
4247 uint32 i;
4248 
4249 if (cptr)
4250     return SCPE_ARG;
4251 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
4252     return SCPE_NOFNC;
4253 if (flag) {                                             /* enable? */
4254     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
4255         return SCPE_OK;
4256     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
4257     }
4258 else {
4259     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
4260         return SCPE_OK;
4261     for (i = 0; i < dptr->numunits; i++) {              /* check units */
4262         up = (dptr->units) + i;                         /* att or active? */
4263         if ((up->flags & UNIT_ATT) || sim_is_active (up))
4264             return SCPE_NOFNC;                          /* can't do it */
4265         }
4266     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
4267     }
4268 if (dptr->reset)                                        /* reset device */
4269     return dptr->reset (dptr);
4270 else return SCPE_OK;
4271 }
4272 
4273 /* Set unit enabled/disabled routine */
4274 
4275 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4276 {
4277 if (cptr)
4278     return SCPE_ARG;
4279 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
4280     return SCPE_NOFNC;
4281 if (flag)                                               /* enb? enable */
4282     uptr->flags = uptr->flags & ~UNIT_DIS;
4283 else {
4284     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
4285         sim_is_active (uptr))                           /* more tests */
4286         return SCPE_NOFNC;
4287     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
4288     }
4289 return SCPE_OK;
4290 }
4291 
4292 /* Set device debug enabled/disabled routine */
4293 
4294 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4295 {
4296 char gbuf[CBUFSIZE];
4297 DEBTAB *dep;
4298 
4299 if ((dptr->flags & DEV_DEBUG) == 0)
4300     return SCPE_NOFNC;
4301 if (cptr == NULL) {                                     /* no arguments? */
4302     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
4303     if (flag && dptr->debflags) {                       /* enable with table? */
4304         for (dep = dptr->debflags; dep->name != NULL; dep++)
4305             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
4306         }
4307     return SCPE_OK;
4308     }
4309 if (dptr->debflags == NULL)                             /* must have table */
4310     return SCPE_ARG;
4311 while (*cptr) {
4312     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
4313     for (dep = dptr->debflags; dep->name != NULL; dep++) {
4314         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
4315             if (flag)
4316                 dptr->dctrl = dptr->dctrl | dep->mask;
4317             else dptr->dctrl = dptr->dctrl & ~dep->mask;
4318             break;
4319             }
4320         }                                               /* end for */
4321     if (dep->mask == 0)                                 /* no match? */
4322         return SCPE_ARG;
4323     }                                                   /* end while */
4324 return SCPE_OK;
4325 }
4326 
4327 /* Show command */
4328 
4329 t_stat show_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4330 {
4331 t_stat r = SCPE_IERR;
4332 
4333 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
4334                                                         /* get sw, ofile */
4335 if (NULL == cptr)                                              /* error? */
4336     return r;
4337 if (sim_ofile) {                                        /* output file? */
4338     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
4339     fclose (sim_ofile);
4340     }
4341 else {
4342     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
4343     if (sim_log && (sim_log != stdout))
4344         show_cmd_fi (sim_log, flag, cptr);
4345     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
4346         show_cmd_fi (sim_deb, flag, cptr);
4347     }
4348 return r;
4349 }
4350 
4351 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4352 {
4353 uint32 lvl = 0xFFFFFFFF;
4354 char gbuf[CBUFSIZE], *cvptr;
4355 CONST char *svptr;
4356 DEVICE *dptr;
4357 UNIT *uptr;
4358 MTAB *mptr;
4359 SHTAB *shtb = NULL, *shptr;
4360 
4361 GET_SWITCHES (cptr);                                    /* get switches */
4362 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4363     return SCPE_2FARG;
4364 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
4365 
4366 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4367     uptr = dptr->units;                                 /* first unit */
4368     shtb = show_dev_tab;                                /* global table */
4369     lvl = MTAB_VDV;                                     /* device match */
4370     GET_SWITCHES (cptr);                                /* get more switches */
4371     }
4372 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4373     if (uptr == NULL)                                   /* invalid unit */
4374         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
4375     if (uptr->flags & UNIT_DIS)                         /* disabled? */
4376         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
4377     shtb = show_unit_tab;                               /* global table */
4378     lvl = MTAB_VUN;                                     /* unit match */
4379     GET_SWITCHES (cptr);                                /* get more switches */
4380     }
4381 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
4382     GET_SWITCHES (cptr);                                /* get more switches */
4383     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
4384     }
4385 else {
4386     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4387         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4388             *cvptr++ = 0;
4389         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
4390             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
4391                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) || //-V600
4392                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
4393                 dptr = sim_dflt_dev;
4394                 lvl = MTAB_VDV;                         /* device match */
4395                 cptr = svptr;
4396                 while (sim_isspace(*cptr))
4397                     ++cptr;
4398                 break;
4399                 }
4400             }
4401         }
4402     if (!dptr) {
4403         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
4404             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
4405         else
4406             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
4407         }
4408     }
4409 
4410 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
4411     return (lvl == MTAB_VDV)?
4412         show_device (ofile, dptr, 0):
4413         show_unit (ofile, dptr, uptr, -1);
4414     }
4415 GET_SWITCHES (cptr);                                    /* get more switches */
4416 
4417 while (*cptr != 0) {                                    /* do all mods */
4418     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
4419     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4420         *cvptr++ = 0;
4421     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4422         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
4423             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
4424             ((mptr->disp && mptr->pstring &&            /* named disp? */
4425             (MATCH_CMD (gbuf, mptr->pstring) == 0))
4426             )) {
4427             if (cvptr && !MODMASK(mptr,MTAB_SHP))
4428                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
4429             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
4430             break;
4431             }                                           /* end if */
4432         }                                               /* end for */
4433     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4434         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
4435             t_stat r;
4436 
4437             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
4438             if (r != SCPE_OK)
4439                 return r;
4440             }
4441         else {
4442             if (!dptr->modifiers)                       /* no modifiers? */
4443                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
4444             else
4445                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
4446             }
4447         }                                               /* end if */
4448     }                                                   /* end while */
4449 return SCPE_OK;
4450 }
4451 
4452 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4453 {
4454 if (!tab)
4455     return NULL;
4456 for (; tab->name != NULL; tab++) {
4457     if (MATCH_CMD (gbuf, tab->name) == 0)
4458         return tab;
4459     }
4460 return NULL;
4461 }
4462 
4463 /* Show device and unit */
4464 
4465 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4466 {
4467 uint32 j, udbl, ucnt;
4468 UNIT *uptr;
4469 int32 toks = 0;
4470 
4471 if (strcmp(sim_dname (dptr),"SYS") != 0) {
4472 (void)fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
4473 if ((flag == 2) && dptr->description) {
4474     (void)fprintf (st, "\t%s\n", dptr->description(dptr));
4475     }
4476 else {
4477     if ((sim_switches & SWMASK ('D')) && dptr->description)
4478         (void)fprintf (st, "\t%s\n", dptr->description(dptr));
4479     }
4480 if (qdisable (dptr)) {                                  /* disabled? */
4481     (void)fprintf (st, "\tdisabled\n");
4482     return SCPE_OK;
4483     }
4484 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
4485     uptr = dptr->units + j;
4486     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
4487         ucnt++;
4488     else if (uptr->flags & UNIT_DISABLE)
4489         udbl++;                                         /* count user-disabled */
4490     }
4491 //show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
4492 if (dptr->numunits == 0) {
4493     // if (toks) //-V547
4494     //     (void)fprintf (st, "\n");
4495 }
4496  else
4497 {
4498     if (ucnt == 0) {
4499         fprint_sep (st, &toks);
4500         (void)fprintf (st, "all units disabled\n");
4501         }
4502     else if ((ucnt + udbl) == 1) {
4503         fprint_sep (st, &toks);
4504         (void)fprintf (st, " 1 unit\n");
4505         }
4506     else if ((ucnt > 1) || (udbl > 0)) {
4507         fprint_sep (st, &toks);
4508         (void)fprintf (st, "%2.d units\n", ucnt + udbl);
4509         }
4510     else
4511         if ((flag != 2) || !dptr->description || toks)
4512             (void)fprintf (st, "\n");
4513     toks = 0;
4514 }
4515 if (flag)                                               /* dev only? */
4516     return SCPE_OK;
4517 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
4518     uptr = dptr->units + j;
4519     if ((uptr->flags & UNIT_DIS) == 0)
4520         show_unit (st, dptr, uptr, ucnt + udbl);
4521     }
4522 }
4523 return SCPE_OK;
4524 }
4525 
4526 void fprint_sep (FILE *st, int32 *tokens)
     /* [previous][next][first][last][top][bottom][index][help] */
4527 {
4528 (void)fprintf (st, "%s", (*tokens > 0) ? "" : "\t");
4529 *tokens += 1;
4530 }
4531 
4532 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4533 {
4534 int32 u = (int32)(uptr - dptr->units);
4535 int32 toks = 0;
4536 
4537 if (flag > 1)
4538     (void)fprintf (st, "   %s%d \n", sim_dname (dptr), u);
4539 else if (flag < 0)
4540     (void)fprintf (st, " %s%d ", sim_dname (dptr), u);
4541 if (uptr->flags & UNIT_ATT) {
4542     fprint_sep (st, &toks);
4543     (void)fprintf (st, "status   : attached to %s", uptr->filename);
4544     if (uptr->flags & UNIT_RO)
4545         (void)fprintf (st, ", read only");
4546     }
4547 else {
4548     if (uptr->flags & UNIT_ATTABLE) {
4549         fprint_sep (st, &toks);
4550         (void)fprintf (st, "status   : not attached");
4551         }
4552     }
4553 if ((uptr->capac > 0) && (uptr->flags & UNIT_FIX)) {
4554     fprint_sep (st, &toks);
4555     fprint_capac (st, dptr, uptr);
4556     }
4557 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
4558 if (toks || (flag < 0) || (flag > 1))
4559     (void)fprintf (st, "\n");
4560 return SCPE_OK;
4561 }
4562 
4563 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4564 {
4565 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4566 t_offset kval = (t_offset)((uptr->flags & UNIT_BINK) ? 1024: 1000);
4567 t_offset mval;
4568 t_offset psize = (t_offset)uptr->capac;
4569 char *scale, *width;
4570 
4571 if (sim_switches & SWMASK ('B'))
4572     kval = 1024;
4573 mval = kval * kval;
4574 if (dptr->flags & DEV_SECTORS) {
4575     kval = kval / 512;
4576     mval = mval / 512;
4577     }
4578 if ((dptr->dwidth / dptr->aincr) > 8)
4579     width = "W";
4580 else
4581     width = "B";
4582 if ((psize < (kval * 10)) &&
4583     (0 != (psize % kval))) {
4584     scale = "";
4585     }
4586 else if ((psize < (mval * 10)) &&
4587          (0 != (psize % mval))){
4588     scale = "K";
4589     psize = psize / kval;
4590     }
4591 else {
4592     scale = "M";
4593     psize = psize / mval;
4594     }
4595 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4596 (void)sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4597 return capac_buf;
4598 }
4599 
4600 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4601 {
4602 (void)fprintf (st, " %s", sprint_capac (dptr, uptr));
4603 }
4604 
4605 /* Show <global name> processors  */
4606 
4607 extern void print_default_base_system_script (void);
4608 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] */
4609 {
4610 #if !defined(PERF_STRIP)
4611   print_default_base_system_script();
4612 #endif /* if !defined(PERF_STRIP) */
4613   return 0;
4614 }
4615 
4616 static void printp (unsigned char * PROM, char * label, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4617   sim_printf (" %s ", label);
4618   sim_printf ("   %2d     %3o(8)     '", length, offset);
4619   for (int l = 0; l < length; l ++)
4620     {
4621       unsigned int byte = PROM[offset + l];
4622       if (byte == 255)
4623         {
4624           byte = ' ';
4625         }
4626       sim_printf (isprint (byte) ? "%c" : "\\%03o", byte);
4627     }
4628   sim_printf ("'\r\n");
4629 }
4630 
4631 static void strip_spaces(char* str) {
     /* [previous][next][first][last][top][bottom][index][help] */
4632   int i, x;
4633   for (i=x=0; str[i]; ++i)
4634     {
4635       if (!isspace((int)str[i]) || (i > 0 && !isspace((int)str[(int)(i-1)]))) //-V781
4636         {
4637           str[x++] = str[i];
4638         }
4639     }
4640   str[x] = '\0';
4641   i = -1;
4642   x = 0;
4643   while (str[x] != '\0')
4644     {
4645       if (str[x] != ' ' && str[x] != '\t' && str[x] != '\n')
4646         {
4647           i=x;
4648         }
4649       x++;
4650     }
4651   str[i+1] = '\0';
4652 }
4653 
4654 static void printpq (unsigned char * PROM, FILE * st, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4655   char sx[1024];
4656   sx[1023] = '\0';
4657   unsigned int lastbyte = 0;
4658   for (int l = 0; l < length; l ++)
4659     {
4660       unsigned int byte = PROM[offset + l];
4661       if (byte == 255)
4662         {
4663           byte = 20;
4664         }
4665       if ((lastbyte != 20) && (byte != 20))
4666         {
4667           (void)sprintf(&sx[l], isprint (byte) ? "%c" : " ", byte);
4668         }
4669       lastbyte = byte;
4670     }
4671   strip_spaces(sx);
4672   (void)fprintf (st, "%s", sx);
4673 }
4674 
4675 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4676 {
4677   unsigned char PROM[1024];
4678   setupPROM (0, PROM);
4679 
4680   sim_printf (" PROM size: %llu bytes\r\n",
4681               (long long unsigned)sizeof(PROM));
4682   sim_printf (" PROM initialization data:\r\n\r\n");
4683 
4684   sim_printf ("     Field Description      Length   Offset              Contents\r\n");
4685   sim_printf (" ========================= ======== ======== ==================================\r\n");
4686   sim_printf ("\r\n");
4687 
4688   //                      Field                 Offset       Length
4689   //             -------------------------    ----------   ----------
4690   printp (PROM, "CPU Model                ",       0,          11);
4691   printp (PROM, "CPU Serial               ",      11,          11);
4692   printp (PROM, "Ship Date                ",      22,           6);
4693   printp (PROM, "PROM Layout Version      ",      60,           1);
4694   printp (PROM, "Release Git Commit Date  ",      70,          10);
4695   printp (PROM, "Release Major            ",      80,           3);
4696   printp (PROM, "Release Minor            ",      83,           3);
4697   printp (PROM, "Release Patch            ",      86,           3);
4698   printp (PROM, "Release Iteration        ",      89,           3);
4699   printp (PROM, "Release Build Number     ",      92,           8);  /* Reserved */
4700   printp (PROM, "Release Type             ",     100,           1);
4701   printp (PROM, "Release Version Text     ",     101,          29);
4702   printp (PROM, "Build Architecture       ",     130,          20);
4703   printp (PROM, "Build Operating System   ",     150,          20);
4704   printp (PROM, "Target Architecture      ",     170,          20);
4705   printp (PROM, "Target Operating System  ",     190,          20);
4706 
4707   sim_printf("\r\n");
4708   return 0;
4709 }
4710 
4711 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4712 {
4713     (void)fprintf (st, "\r Build Information:\n");
4714 #if defined(BUILDINFO_scp)
4715     (void)fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4716 #else
4717     (void)fprintf (st, "\r\n      Compilation info: Not available\n" );
4718 #endif
4719 #if !defined(__CYGWIN__)
4720 # if !defined(__APPLE__)
4721 #  if !defined(_AIX)
4722 #   if !defined(__MINGW32__)
4723 #    if !defined(__MINGW64__)
4724 #     if !defined(CROSS_MINGW32)
4725 #      if !defined(CROSS_MINGW64)
4726 #       if !defined(_WIN32)
4727 #        if !defined(__HAIKU__)
4728     (void)dl_iterate_phdr (dl_iterate_phdr_callback, NULL);
4729     if (dl_iterate_phdr_callback_called)
4730         (void)fprintf (st, "\n");
4731     dl_iterate_phdr_callback_called = 0;
4732 #        endif
4733 #       endif
4734 #      endif
4735 #     endif
4736 #    endif
4737 #   endif
4738 #  endif
4739 # endif
4740 #endif
4741 #if defined(UV_VERSION_MAJOR) && \
4742     defined(UV_VERSION_MINOR) && \
4743     defined(UV_VERSION_PATCH)
4744 # if defined(UV_VERSION_MAJOR)
4745 #  if !defined(UV_VERSION_MINOR) && \
4746       !defined(UV_VERSION_PATCH) && \
4747       !defined(UV_VERSION_SUFFIX)
4748     (void)fprintf (st, "\r\n    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4749 #  endif /* if !defined(UV_VERSION_MINOR) && !defined(UV_VERSION_PATCH) && defined(UV_VERSION_SUFFIX) */
4750 #  if defined(UV_VERSION_MINOR)
4751 #   if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX)
4752     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4753                    UV_VERSION_MINOR);
4754 #   endif /* if !defined(UV_VERSION_PATCH) && !defined(UV_VERSION_SUFFIX) */
4755 #   if defined(UV_VERSION_PATCH)
4756 #    if !defined(UV_VERSION_SUFFIX)
4757     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4758                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4759 #    endif /* if !defined(UV_VERSION_SUFFIX) */
4760 #    if defined(UV_VERSION_SUFFIX)
4761     (void)fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4762                    UV_VERSION_MINOR, UV_VERSION_PATCH);
4763 #     if defined(UV_VERSION_IS_RELEASE)
4764 #      if UV_VERSION_IS_RELEASE == 1
4765 #       define UV_RELEASE_TYPE " (release)"
4766 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4767 #      if UV_VERSION_IS_RELEASE == 0
4768 #       define UV_RELEASE_TYPE "-dev"
4769 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4770 #      if !defined(UV_RELEASE_TYPE)
4771 #       define UV_RELEASE_TYPE ""
4772 #      endif /* if !defined(UV_RELEASE_TYPE) */
4773 #      if defined(UV_RELEASE_TYPE)
4774     (void)fprintf (st, "%s", UV_RELEASE_TYPE);
4775 #      endif /* if defined(UV_RELEASE_TYPE) */
4776 #     endif /* if defined(UV_VERSION_IS_RELEASE) */
4777 #    endif /* if defined(UV_VERSION_SUFFIX) */
4778 #   endif /* if defined(UV_VERSION_PATCH) */
4779 #  endif /* if defined(UV_VERSION_MINOR) */
4780     unsigned int CurrentUvVersion = uv_version();
4781     if (CurrentUvVersion > 0)
4782         if (uv_version_string() != NULL)
4783             (void)fprintf (st, "; %s in use", uv_version_string());
4784 # endif /* if defined(UV_VERSION_MAJOR) */
4785 #else
4786     (void)fprintf (st, "\r\n    Event loop library: Using libuv (or compatible) library, unknown version");
4787 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4788         *    defined(UV_VERSION_MINOR) &&  \
4789         *    defined(UV_VERSION_PATCH)     \
4790         */
4791     (void)fprintf (st, "\r\n   Log support library: Built with libsir %d.%d.%d%s%s; %s%s in use",
4792                    SIR_VERSION_MAJOR, SIR_VERSION_MINOR, SIR_VERSION_PATCH,
4793                    SIR_VERSION_SUFFIX, SIR_VERSION_IS_RELEASE ? " (release)" : "",
4794                    sir_getversionstring(), SIR_VERSION_IS_RELEASE ? "" : sir_isprerelease() ? "" : " (release)");
4795 #if defined(DECNUMBERLOC)
4796 # if defined(DECVERSION)
4797 #  if defined(DECVERSEXT)
4798     (void)fprintf (st, "\r\n          Math library: %s-%s", DECVERSION, DECVERSEXT);
4799 #  else
4800 #   if defined(DECNLAUTHOR)
4801     (void)fprintf (st, "\r\n          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4802 #   else
4803     (void)fprintf (st, "\r\n          Math library: %s", DECVERSION);
4804 #   endif /* if defined(DECNLAUTHOR) */
4805 #  endif /* if defined(DECVERSEXT) */
4806 # else
4807     (void)fprintf (st, "\r\n          Math library: decNumber, unknown version");
4808 # endif /* if defined(DECVERSION) */
4809 #endif /* if defined(DECNUMBERLOC) */
4810 #if defined(LOCKLESS)
4811     (void)fprintf (st, "\r\n     Atomic operations: ");
4812 # if defined(AIX_ATOMICS)
4813     (void)fprintf (st, "C11 and IBM AIX-style");
4814 # elif defined(BSD_ATOMICS)
4815     (void)fprintf (st, "C11 and FreeBSD-style");
4816 # elif defined(GNU_ATOMICS)
4817     (void)fprintf (st, "C11 and GNU-style");
4818 # elif defined(SYNC_ATOMICS)
4819     (void)fprintf (st, "C11 and GNU sync-style");
4820 # elif defined(ISO_ATOMICS)
4821     (void)fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4822 # elif defined(NT_ATOMICS)
4823     (void)fprintf (st, "C11 and Windows NT interlocked operations");
4824 # endif
4825 #endif /* if defined(LOCKLESS) */
4826     (void)fprintf (st, "\r\n          File locking: ");
4827 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4828     (void)fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4829 #endif
4830 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4831     (void)fprintf (st, "POSIX-style fcntl() locking");
4832 #endif
4833 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4834     (void)fprintf (st, "BSD-style flock() locking");
4835 #endif
4836 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4837     (void)fprintf (st, "No file locking available");
4838 #endif
4839     (void)fprintf (st, "\r\n       Windows support: ");
4840 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
4841 # if defined(__MINGW64_VERSION_STR)
4842     (void)fprintf (st, "Built with MinGW-w64 %s", __MINGW64_VERSION_STR);
4843 # elif defined(__MINGW32_MAJOR_VERSION) && defined(__MINGW32_MINOR_VERSION)
4844     (void)fprintf (st, "Built with MinGW %d.%d", __MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);
4845 # else
4846     (void)fprintf (st, "Built with MinGW");
4847 # endif
4848 
4849 # if defined(MINGW_CRT)
4850     (void)fprintf (st, "; %s", MINGW_CRT);
4851 #  if !defined(_UCRT)
4852 #   if defined(__MSVCRT_VERSION__)
4853 #    if __MSVCRT_VERSION__ > 0x00
4854     (void)fprintf (st, " %d.%d", (__MSVCRT_VERSION__ >> CHAR_BIT) & UCHAR_MAX, __MSVCRT_VERSION__ & UCHAR_MAX);
4855 #    endif
4856 #   endif
4857 #  else
4858 
4859     struct UCRTVersion ucrtversion;
4860     int result = GetUCRTVersion (&ucrtversion);
4861 
4862     if (result == 0)
4863       (void)fprintf (st, " %u.%u.%u.%u",
4864                      ucrtversion.ProductVersion[1], ucrtversion.ProductVersion[0],
4865                      ucrtversion.ProductVersion[3], ucrtversion.ProductVersion[2]);
4866 #  endif
4867     (void)fprintf (st, " in use");
4868 # endif
4869 #elif defined(__CYGWIN__)
4870     struct utsname utsname;
4871     (void)fprintf (st, "Built with Cygwin %d.%d.%d",
4872                    CYGWIN_VERSION_DLL_MAJOR / 1000,
4873                    CYGWIN_VERSION_DLL_MAJOR % 1000,
4874                    CYGWIN_VERSION_DLL_MINOR);
4875     if (uname(&utsname) == 0)
4876       fprintf (st, "; %s in use", utsname.release);
4877 #else
4878     (void)fprintf (st, "Disabled");
4879 #endif
4880 
4881     (void)fprintf (st, "\r\n");
4882     return 0;
4883 }
4884 
4885 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4886 {
4887 const char *arch = "";
4888 char *whydirty = " ";
4889 int dirty = 0;
4890 
4891 if (cptr && (*cptr != 0))
4892     return SCPE_2MARG;
4893 if (flag) {
4894         (void)fprintf (st, " %s Simulator:", sim_name);
4895 #if defined(USE_DUMA)
4896 # undef NO_SUPPORT_VERSION
4897 # define NO_SUPPORT_VERSION 1
4898         nodist++;
4899 #endif /* if defined(USE_DUMA) */
4900 #if defined(NO_SUPPORT_VERSION) ||  \
4901     defined(WITH_SOCKET_DEV)    ||  \
4902     defined(WITH_ABSI_DEV)      ||  \
4903     defined(WITH_MGP_DEV)       ||  \
4904     defined(TESTING)            ||  \
4905     defined(ISOLTS)             ||  \
4906     defined(USE_DUMA)
4907 # if !defined(NO_SUPPORT_VERSION)
4908 #  define NO_SUPPORT_VERSION 1
4909 # endif /* if !defined(NO_SUPPORT_VERSION) */
4910 #endif
4911 #if defined(NO_SUPPORT_VERSION)
4912         dirty++;
4913 #endif
4914 #if defined(GENERATED_MAKE_VER_H)
4915 # if defined(VER_H_GIT_VERSION)
4916 
4917         /* Dirty if git source is dirty */
4918         if (strstr(VER_H_GIT_VERSION, "*"))
4919           {
4920                 dirty++;
4921           }
4922 
4923         /* Dirty if version contains "X", "D", "A", or "B" */
4924         if ((strstr(VER_H_GIT_VERSION, "X")) ||  \
4925             (strstr(VER_H_GIT_VERSION, "D")) ||  \
4926             (strstr(VER_H_GIT_VERSION, "A")) ||  \
4927             (strstr(VER_H_GIT_VERSION, "B")))
4928           {
4929                 dirty++;
4930           }
4931 
4932         /* Why? */
4933         if (dirty) //-V547
4934           {
4935             if ((strstr(VER_H_GIT_VERSION, "X")))
4936               {
4937                     whydirty = " ";
4938               }
4939             else if ((strstr(VER_H_GIT_VERSION, "D")))
4940               {
4941                     whydirty = " DEV ";
4942               }
4943             else if ((strstr(VER_H_GIT_VERSION, "A")))
4944               {
4945                     whydirty = " ALPHA ";
4946               }
4947             else if ((strstr(VER_H_GIT_VERSION, "B")))
4948               {
4949                     whydirty = " BETA ";
4950               }
4951           }
4952 
4953 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4954 #   if defined(VER_H_GIT_HASH)
4955 #    if VER_H_GIT_PATCH_INT < 1
4956     (void)fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4957                    VER_H_GIT_VERSION,
4958                    (long)(CHAR_BIT*sizeof(void *)),
4959                    VER_H_GIT_HASH);
4960 #    else
4961 #     define NO_SUPPORT_VERSION 1
4962     (void)fprintf (st, "\n   Version: %s+%s (%ld-bit)\n    Commit: %s",
4963                    VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4964                    (long)(CHAR_BIT*sizeof(void *)),
4965                    VER_H_GIT_HASH);
4966 #    endif
4967 #   else
4968 #    if VER_H_GIT_PATCH_INT < 1
4969         (void)fprintf (st, "\n   Version: %s (%ld-bit)",
4970                        VER_H_GIT_VERSION,
4971                        (long)(CHAR_BIT*sizeof(void *)));
4972 #    else
4973 #     define NO_SUPPORT_VERSION 1
4974         (void)fprintf (st, "\n   Version: %s+%s (%ld-bit)",
4975                        VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4976                        (long)(CHAR_BIT*sizeof(void *)));
4977 #    endif
4978 #   endif
4979 #  else
4980 #   if defined(VER_H_GIT_HASH)
4981         (void)fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4982                        VER_H_GIT_VERSION,
4983                        (long)(CHAR_BIT*sizeof(void *)),
4984                        VER_H_GIT_HASH);
4985 #   else
4986         (void)fprintf (st, "\n   Version: %s (%ld-bit)",
4987                        VER_H_GIT_VERSION,
4988                        (long)(CHAR_BIT*sizeof(void *)));
4989 #   endif
4990 #  endif
4991 # endif
4992 #endif
4993 
4994 /* TESTING */
4995 #if defined(TESTING)
4996     (void)fprintf (st, "\n   Options: ");
4997 # if !defined(HAVE_DPSOPT)
4998 #  define HAVE_DPSOPT 1
4999 # endif
5000     (void)fprintf (st, "TESTING");
5001 #endif /* if defined(TESTING) */
5002 
5003 /* ISOLTS */
5004 #if defined(ISOLTS)
5005 # if defined(HAVE_DPSOPT)
5006     (void)fprintf (st, ", ");
5007 # else
5008     (void)fprintf (st, "\n   Options: ");
5009 # endif
5010 # if !defined(HAVE_DPSOPT)
5011 #  define HAVE_DPSOPT 1
5012 # endif
5013     (void)fprintf (st, "ISOLTS");
5014 #endif /* if defined(ISOLTS) */
5015 
5016 /* NO_UCACHE */
5017 #if defined(NO_UCACHE)
5018 # if defined(HAVE_DPSOPT)
5019     (void)fprintf (st, ", ");
5020 # else
5021     (void)fprintf (st, "\n   Options: ");
5022 # endif
5023 # if !defined(HAVE_DPSOPT)
5024 #  define HAVE_DPSOPT 1
5025 # endif
5026     (void)fprintf (st, "NO_UCACHE");
5027 #endif /* if defined(NO_UCACHE) */
5028 
5029 /* NEED_128 */
5030 #if defined(NEED_128)
5031 # if defined(HAVE_DPSOPT)
5032     (void)fprintf (st, ", ");
5033 # else
5034     (void)fprintf (st, "\n   Options: ");
5035 # endif
5036 # if !defined(HAVE_DPSOPT)
5037 #  define HAVE_DPSOPT 1
5038 # endif
5039     (void)fprintf (st, "NEED_128");
5040 #endif /* if defined(NEED_128) */
5041 
5042 /* WAM */
5043 #if defined(WAM)
5044 # if defined(HAVE_DPSOPT)
5045     (void)fprintf (st, ", ");
5046 # else
5047     (void)fprintf (st, "\n   Options: ");
5048 # endif
5049 # if !defined(HAVE_DPSOPT)
5050 #  define HAVE_DPSOPT 1
5051 # endif
5052     (void)fprintf (st, "WAM");
5053 #endif /* if defined(WAM) */
5054 
5055 /* ROUND_ROBIN */
5056 #if defined(ROUND_ROBIN)
5057 # if defined(HAVE_DPSOPT)
5058     (void)fprintf (st, ", ");
5059 # else
5060     (void)fprintf (st, "\n   Options: ");
5061 # endif
5062 # if !defined(HAVE_DPSOPT)
5063 #  define HAVE_DPSOPT 1
5064 # endif
5065     (void)fprintf (st, "ROUND_ROBIN");
5066 #endif /* if defined(ROUND_ROBIN) */
5067 
5068 /* NO_LOCKLESS */
5069 #if !defined(LOCKLESS)
5070 # if defined(HAVE_DPSOPT)
5071     (void)fprintf (st, ", ");
5072 # else
5073     (void)fprintf (st, "\n   Options: ");
5074 # endif
5075 # if !defined(HAVE_DPSOPT)
5076 #  define HAVE_DPSOPT 1
5077 # endif
5078     (void)fprintf (st, "NO_LOCKLESS");
5079 #endif /* if !defined(LOCKLESS) */
5080 
5081 /* ABSI */  /* XXX: Change to NO_ABSI once code is non-experimental */
5082 #if defined(WITH_ABSI_DEV)
5083 # if defined(HAVE_DPSOPT)
5084     (void)fprintf (st, ", ");
5085 # else
5086     (void)fprintf (st, "\n   Options: ");
5087 # endif
5088 # if !defined(HAVE_DPSOPT)
5089 #  define HAVE_DPSOPT 1
5090 # endif
5091     (void)fprintf (st, "ABSI");
5092 #endif /* if defined(WITH_ABSI_DEV) */
5093 
5094 /* SOCKET */  /* XXX: Change to NO_SOCKET once code is non-experimental */
5095 #if defined(WITH_SOCKET_DEV)
5096 # if defined(HAVE_DPSOPT)
5097     (void)fprintf (st, ", ");
5098 # else
5099     (void)fprintf (st, "\n   Options: ");
5100 # endif
5101 # if !defined(HAVE_DPSOPT)
5102 #  define HAVE_DPSOPT 1
5103 # endif
5104     (void)fprintf (st, "SOCKET");
5105 #endif /* if defined(WITH_SOCKET_DEV) */
5106 
5107 /* CHAOSNET */  /* XXX: Change to NO_CHAOSNET once code is non-experimental */
5108 #if defined(WITH_MGP_DEV)
5109 # if defined(HAVE_DPSOPT)
5110     (void)fprintf (st, ", ");
5111 # else
5112     (void)fprintf (st, "\n   Options: ");
5113 # endif
5114 # if !defined(HAVE_DPSOPT)
5115 #  define HAVE_DPSOPT 1
5116 # endif
5117     (void)fprintf (st, "CHAOSNET");
5118 # if USE_SOCKET_DEV_APPROACH
5119     (void)fprintf (st, "-S");
5120 # endif /* if USE_SOCKET_DEV_APPROACH */
5121 #endif /* if defined(WITH_MGP_DEV) */
5122 
5123 /* DUMA */
5124 #if defined(USE_DUMA)
5125 # if defined(HAVE_DPSOPT)
5126     (void)fprintf (st, ", ");
5127 # else
5128     (void)fprintf (st, "\n   Options: ");
5129 # endif
5130 # if !defined(HAVE_DPSOPT)
5131 #  define HAVE_DPSOPT 1
5132 # endif
5133     (void)fprintf (st, "DUMA");
5134 #endif /* if defined(USE_DUMA) */
5135 
5136 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
5137 # if defined(NO_SUPPORT_VERSION)
5138     (void)fprintf (st, "\n  Modified: %s", VER_H_GIT_DATE);
5139 # else
5140     (void)fprintf (st, "\n  Released: %s", VER_H_GIT_DATE);
5141 # endif
5142 #endif
5143 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
5144     (void)fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
5145 #endif
5146 #if defined(VER_CURRENT_TIME)
5147     (void)fprintf (st, "\n  Compiled: %s", VER_CURRENT_TIME);
5148 #endif
5149     if (dirty) //-V547
5150       {
5151         (void)fprintf (st, "\r\n\r\n ****** THIS%sBUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******", whydirty);
5152       }
5153     (void)fprintf (st, "\r\n\r\n Build Information:");
5154 #if defined(BUILD_PROM_OSV_TEXT) && defined(BUILD_PROM_OSA_TEXT)
5155     char build_os_version_raw[255];
5156     char build_os_arch_raw[255];
5157     (void)sprintf(build_os_version_raw, "%.254s", BUILD_PROM_OSV_TEXT);
5158     (void)sprintf(build_os_arch_raw, "%.254s", BUILD_PROM_OSA_TEXT);
5159     char *build_os_version = strdup(build_os_version_raw);
5160     if (!build_os_version)
5161       {
5162         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5163                        __func__, __FILE__, __LINE__);
5164 # if defined(USE_BACKTRACE)
5165 #  if defined(SIGUSR2)
5166         (void)raise(SIGUSR2);
5167         /*NOTREACHED*/ /* unreachable */
5168 #  endif /* if defined(SIGUSR2) */
5169 # endif /* if defined(USE_BACKTRACE) */
5170         abort();
5171       }
5172     char *build_os_arch = strdup(build_os_arch_raw);
5173     if (!build_os_arch)
5174       {
5175         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
5176                        __func__, __FILE__, __LINE__);
5177 # if defined(USE_BACKTRACE)
5178 #  if defined(SIGUSR2)
5179         (void)raise(SIGUSR2);
5180         /*NOTREACHED*/ /* unreachable */
5181 #  endif /* if defined(SIGUSR2) */
5182 # endif /* if defined(USE_BACKTRACE) */
5183         abort();
5184       }
5185     unsigned char SPROM[1024];
5186     setupPROM (0, SPROM);
5187     (void)fprintf (st, "\n    Target: ");
5188     printpq (SPROM, st, 190, 20);
5189     if (SPROM[170] != 20)
5190       {
5191         if (SPROM[170] != 255)
5192           {
5193             (void)fprintf (st, " on ");
5194             printpq (SPROM, st, 170, 20);
5195           }
5196       }
5197     strtrimspace(build_os_version, build_os_version_raw);
5198     strtrimspace(build_os_arch, build_os_arch_raw);
5199     (void)fprintf (st, "\n  Build OS: %s %s", build_os_version, build_os_arch);
5200     FREE(build_os_version);
5201     FREE(build_os_arch);
5202 #endif
5203 #if defined(__VERSION__)
5204     char gnumver[2];
5205     char postver[1024];
5206     (void)sprintf(gnumver, "%.1s", __VERSION__);
5207     (void)sprintf(postver, "%.1023s", __VERSION__);
5208     strremove(postver, "(TM)");
5209     strremove(postver, "(R)");
5210     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
5211     strremove(postver, "https://github.com/OpenIndiana/oi-userland.git ");
5212     strremove(postver, " gcc 4.9 mode");
5213     strremove(postver, "4.2.1 Compatible ");
5214     strremove(postver, "git@github.com:llvm/llvm-project.git ");
5215     strremove(postver, "https://github.com/llvm/llvm-project.git ");
5216     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
5217     strremove(postver, "https://github.com/yrnkrn/zapcc ");
5218     strremove(postver, "(experimental) ");
5219     strremove(postver, ".module+el8.7.0+20823+214a699d");
5220     strremove(postver, "17.1.1 (5725-C72, 5765-J20), version ");
5221     strremove(postver, "17.1.1 (5725-C72, 5765-J18), version ");
5222     strremove(postver, "17.1.2 (5725-C72, 5765-J20), version ");
5223     strremove(postver, "17.1.2 (5725-C72, 5765-J18), version ");
5224     strremove(postver, "llvmorg-16.0.6-0-");
5225     strremove(postver, " Clang 15.0.0 (build 760095e)");
5226     strremove(postver, " Clang 15.0.0 (build 6af5742)");
5227     strremove(postver, " Clang 15.0.0 (build ca7115e)");
5228     strremove(postver, " Clang 15.0.0 (build 232543c)");
5229     strremove(postver, " Clang 17.0.6 (build 19a779f)");
5230     strremove(postver, "CLANG: ");
5231 #endif
5232 #if ( defined(__GNUC__) && defined(__VERSION__) ) && !defined(__EDG__)
5233 # if !defined(__clang_version__)
5234     if (isdigit((unsigned char)gnumver[0])) {
5235         (void)fprintf (st, "\n  Compiler: GCC %s", postver);
5236     } else {
5237         (void)fprintf (st, "\n  Compiler: %s", postver);
5238     }
5239 # endif
5240 # if defined(__clang_analyzer__ )
5241     (void)fprintf (st, "\n  Compiler: Clang C/C++ Static Analyzer");
5242 # elif defined(__clang_version__) && defined(__VERSION__)
5243     char clangllvmver[1024];
5244     (void)sprintf(clangllvmver, "%.1023s", __clang_version__);
5245     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
5246     strremove(clangllvmver, "https://github.com/OpenIndiana/oi-userland.git ");
5247     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git ");
5248     strremove(clangllvmver, "c13b7485b87909fcf739f62cfa382b55407433c0");
5249     strremove(clangllvmver, "e6c3289804a67ea0bb6a86fadbe454dd93b8d855");
5250     strremove(clangllvmver, "https://github.com/llvm/llvm-project.git");
5251     strremove(clangllvmver, " ( )");
5252     strremove(clangllvmver, " ()");
5253     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
5254         (void)fprintf (st, "\n  Compiler: Clang %s", clangllvmver);
5255     } else {
5256         (void)fprintf (st, "\n  Compiler: %s", postver);
5257     }
5258 # elif defined(__clang_version__)
5259     (void)fprintf (st, "\n  Compiler: %s", postver);
5260 # endif
5261 #elif defined(__PGI) && !defined(__NVCOMPILER)
5262     (void)fprintf (st, "\n  Compiler: Portland Group, Inc. (PGI) C Compiler ");
5263 # if defined(__PGIC__)
5264     (void)fprintf (st, "%d", __PGIC__);
5265 #  if defined(__PGIC_MINOR__)
5266     (void)fprintf (st, ".%d", __PGIC_MINOR__);
5267 #   if defined(__PGIC_PATCHLEVEL__)
5268     (void)fprintf (st, ".%d", __PGIC_PATCHLEVEL__);
5269 #   endif
5270 #  endif
5271 # endif
5272 #elif defined(__NVCOMPILER)
5273     (void)fprintf (st, "\n  Compiler: NVIDIA HPC SDK C Compiler ");
5274 # if defined(__NVCOMPILER_MAJOR__)
5275     (void)fprintf (st, "%d", __NVCOMPILER_MAJOR__);
5276 #  if defined(__NVCOMPILER_MINOR__)
5277     (void)fprintf (st, ".%d", __NVCOMPILER_MINOR__);
5278 #   if defined(__NVCOMPILER_PATCHLEVEL__)
5279     (void)fprintf (st, ".%d", __NVCOMPILER_PATCHLEVEL__);
5280 #   endif
5281 #  endif
5282 # endif
5283 #elif defined(_MSC_FULL_VER) && defined(_MSC_BUILD)
5284     (void)fprintf (st, "\n  Compiler: Microsoft C %d.%02d.%05d.%02d",
5285                    _MSC_FULL_VER/10000000,
5286                    (_MSC_FULL_VER/100000)%100,
5287                    _MSC_FULL_VER%100000,
5288                    _MSC_BUILD);
5289 #elif ( defined(__xlc__) && !defined(__clang_version__) )
5290 # if defined(_AIX) && defined(__PASE__)
5291     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
5292 # endif
5293 # if defined(_AIX) && !defined(__PASE__)
5294     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
5295 # endif
5296 # if defined(__linux__) && ( !defined(_AIX) || !defined(__PASE__) )
5297     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
5298 # endif
5299 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(__PASE__) && !defined(__linux__) && defined(__xlc__) )
5300 #  if defined(__PPC__) && defined(__APPLE__)
5301     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s for Mac OS X", __xlc__);
5302 #  else
5303     (void)fprintf (st, "\n  Compiler: IBM XL C/C++ V%s", __xlc__);
5304 #  endif
5305 # endif
5306 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__SUNPRO_CC_COMPAT)
5307 # define VER_ENC(maj, min, rev) \
5308   (((maj) * 1000000) + ((min) * 1000) + (rev))
5309 # define VER_DEC_MAJ(ver) \
5310   ((ver) / 1000000)
5311 # define VER_DEC_MIN(ver) \
5312   (((ver) % 1000000) / 1000)
5313 # define VER_DEC_REV(ver) \
5314   ((ver) % 1000)
5315 # if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
5316 #  define COMP_VER VER_ENC(                                        \
5317    (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
5318    (((__SUNPRO_C >>  8) & 0xf) * 10) + ((__SUNPRO_C >>  4) & 0xf), \
5319      (__SUNPRO_C & 0xf) * 10)
5320 # elif defined(__SUNPRO_C)
5321 #  define COMP_VER VER_ENC(    \
5322      (__SUNPRO_C >>  8) & 0xf, \
5323      (__SUNPRO_C >>  4) & 0xf, \
5324      (__SUNPRO_C) & 0xf)
5325 # elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
5326 #  define COMP_VER VER_ENC(                                          \
5327    (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
5328    (((__SUNPRO_CC >>  8) & 0xf) * 10) + ((__SUNPRO_CC >>  4) & 0xf), \
5329      (__SUNPRO_CC & 0xf) * 10)
5330 # elif defined(__SUNPRO_CC)
5331 #  define COMP_VER VER_ENC(     \
5332      (__SUNPRO_CC >>  8) & 0xf, \
5333      (__SUNPRO_CC >>  4) & 0xf, \
5334      (__SUNPRO_CC) & 0xf)
5335 # endif
5336 # if !defined(COMP_VER)
5337 #  define COMP_VER 0
5338 # endif
5339     (void)fprintf (st, "\n  Compiler: Oracle Developer Studio C/C++ %d.%d.%d",
5340                    VER_DEC_MAJ(COMP_VER),
5341                    VER_DEC_MIN(COMP_VER),
5342                    VER_DEC_REV(COMP_VER));
5343 #elif defined(__DMC__)
5344     (void)fprintf (st, "\n  Compiler: Digital Mars C/C++");
5345 #elif defined(__PCC__)
5346     (void)fprintf (st, "\n  Compiler: Portable C Compiler");
5347 #elif defined(KENC) || defined(KENCC) || defined(__KENC__) || defined(__KENCC__)
5348     (void)fprintf (st, "\n  Compiler: Plan 9 Compiler Suite");
5349 #elif defined(__ACK__)
5350     (void)fprintf (st, "\n  Compiler: Amsterdam Compiler Kit");
5351 #elif defined(__COMO__)
5352     (void)fprintf (st, "\n  Compiler: Comeau C++");
5353 #elif defined(__COMPCERT__)
5354     (void)fprintf (st, "\n  Compiler: CompCert C");
5355 #elif defined(__COVERITY__)
5356     (void)fprintf (st, "\n  Compiler: Coverity C/C++ Static Analyzer");
5357 #elif defined(__LCC__)
5358     (void)fprintf (st, "\n  Compiler: Local C Compiler (lcc)");
5359 #elif defined(sgi) || defined(__sgi) || defined(_sgi) || defined(_SGI_COMPILER_VERSION)
5360     (void)fprintf (st, "\n  Compiler: SGI MIPSpro");
5361 #elif defined(__OPEN64__)
5362     (void)fprintf (st, "\n  Compiler: Open64 %s", __OPEN64__);
5363 #elif defined(__PGI) || defined(__PGIC__)
5364     (void)fprintf (st, "\n  Compiler: Portland Group/PGI C/C++");
5365 #elif defined(__VBCC__)
5366     (void)fprintf (st, "\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
5367 #elif defined(__WATCOMC__)
5368     (void)fprintf (st, "\n  Compiler: Watcom C/C++ %d.%d",
5369                    __WATCOMC__ / 100,
5370                    __WATCOMC__ % 100);
5371 #elif defined(__xlC__)
5372     (void)fprintf (st, "\n  Compiler: IBM XL C/C++");
5373 #elif defined(__INTEL_COMPILER) || defined(__ICC)
5374 # if defined(__INTEL_COMPILER_UPDATE)
5375 #  if defined(__INTEL_COMPILER_BUILD_DATE)
5376     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d (%d)",
5377                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE,
5378                    __INTEL_COMPILER_BUILD_DATE);
5379 #  else
5380     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d",
5381                    __INTEL_COMPILER, __INTEL_COMPILER_UPDATE);
5382 #  endif
5383 # else
5384     (void)fprintf (st, "\n  Compiler: Intel C++ Compiler %d",
5385                    __INTEL_COMPILER);
5386 # endif
5387 #elif defined(SIM_COMPILER)
5388 # define S_xstr(a) S_str(a)
5389 # define S_str(a) #a
5390     (void)fprintf (st, "\n  Compiler: %s", S_xstr(SIM_COMPILER));
5391 # undef S_str
5392 # undef S_xstr
5393 #else
5394     (void)fprintf (st, "\n  Compiler: Unknown");
5395 #endif
5396 
5397 #if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || \
5398     defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
5399 # define SC_IS_PPC64 1
5400 #else
5401 # define SC_IS_PPC64 0
5402 #endif
5403 
5404 #if defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__PPC) || \
5405     defined(__ppc32__) || defined(__PPC32__) || defined(__powerpc32__) || defined(__POWERPC32__) || defined(_M_PPC32) || \
5406     defined(__PPC32)
5407 # define SC_IS_PPC32 1
5408 #else
5409 # define SC_IS_PPC32 0
5410 #endif
5411 
5412 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
5413     arch = " x86_64";
5414 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
5415     arch = " x86";
5416 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
5417     arch = " arm64";
5418 #elif defined(_M_ARM) || defined(__arm__)
5419     arch = " arm";
5420 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
5421     arch = " ia64";
5422 #elif SC_IS_PPC64
5423     arch = " powerpc64";
5424 #elif SC_IS_PPC32
5425     arch = " powerpc";
5426 #elif defined(__s390x__)
5427     arch = " s390x";
5428 #elif defined(__s390__)
5429     arch = " s390";
5430 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
5431     arch = " j2";
5432 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
5433     arch = " sh4";
5434 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
5435     arch = " sh2";
5436 #elif defined(__alpha__)
5437     arch = " alpha";
5438 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
5439     arch = " hppa";
5440 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
5441     arch = " ice9";
5442 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
5443     arch = " mips64";
5444 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
5445     arch = " mips";
5446 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || \
5447       defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
5448     arch = " openrisc";
5449 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
5450     arch = " sparc64";
5451 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
5452     arch = " sparc";
5453 #elif defined(__riscv) || defined(__riscv__)
5454     arch = " riscv";
5455 #elif defined(__myriad2__)
5456     arch = " myriad2";
5457 #elif defined(__loongarch64) || defined(__loongarch__)
5458     arch = " loongarch";
5459 #elif defined(_m68851) || defined(__m68k__) || defined(__m68000__) || defined(__M68K)
5460     arch = " m68k";
5461 #elif defined(__m88k__) || defined(__m88000__) || defined(__M88K)
5462     arch = " m88k";
5463 #elif defined(__VAX__) || defined(__vax__)
5464     arch = " vax";
5465 #elif defined(__NIOS2__) || defined(__nios2__)
5466     arch = " nios2";
5467 #elif defined(__MICROBLAZE__) || defined(__microblaze__)
5468     arch = " microblaze";
5469 #else
5470     arch = " ";
5471 #endif
5472     (void)fprintf (st, "%s", arch);
5473 #if defined(BUILD_BY_USER)
5474         (void)fprintf (st, "\n  Built by: %s", BUILD_BY_USER);
5475 #else
5476 # if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
5477         (void)fprintf (st, "\n  Built by: %s", VER_H_PREP_USER);
5478 # endif
5479 #endif
5480                 (void)fprintf (st, "\n\n Host System Information:");
5481 #if defined(_WIN32)
5482     if (1) {
5483         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
5484         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
5485         char osversion[PATH_MAX+1] = "";
5486         FILE *f;
5487 
5488         if ((f = _popen ("ver", "r"))) {
5489             (void)memset (osversion, 0, sizeof(osversion));
5490             do {
5491                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
5492                     break;
5493                 sim_trim_endspc (osversion);
5494                 } while (osversion[0] == '\0');
5495             _pclose (f);
5496             }
5497         (void)fprintf (st, "\n   Host OS: %s", osversion);
5498         (void)fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
5499         }
5500 #else
5501     if (1) {
5502         char osversion[2*PATH_MAX+1] = "";
5503         FILE *f;
5504 # if !defined(_AIX)
5505         if ((f = popen \
5506              ("uname -mrs 2> /dev/null", "r"))) {
5507 # else
5508         if ((f = popen \
5509              ("sh -c 'echo \"$(command -p env uname -v \
5510                2> /dev/null).$(command -p env uname -r \
5511                2> /dev/null) $(command -p env uname -p \
5512                2> /dev/null)\"' 2> /dev/null", "r"))) {
5513 # endif /* if !defined(_AIX) */
5514             (void)memset (osversion, 0, sizeof(osversion));
5515             do {
5516               if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
5517                     break;
5518               }
5519             sim_trim_endspc (osversion);
5520             } while (osversion[0] == '\0');
5521             pclose (f);
5522             strremove(osversion, "0000000000000000 ");
5523             strremove(osversion, " 0000000000000000");
5524             strremove(osversion, "000000000000 ");
5525             strremove(osversion, " 000000000000");
5526             strremove(osversion, "IBM ");
5527             strremove(osversion, " (emulated by qemu)");
5528             strremove(osversion, " (emulated by QEMU)");
5529         }
5530 # if !defined(_AIX)
5531             (void)fprintf (st, "\n   Host OS: %s", osversion);
5532 # else
5533             strremove(osversion, "AIX ");
5534 #  if !defined(__PASE__)
5535             (void)fprintf (st, "\n   Host OS: IBM AIX %s", osversion);
5536 #  else
5537             (void)fprintf (st, "\n   Host OS: IBM OS/400 (PASE) %s", osversion);
5538 #  endif /* if !defined(__PASE__) */
5539 # endif /* if !defined(_AIX) */
5540     } else {
5541 # if !defined(_AIX)
5542         (void)fprintf (st, "\n   Host OS: Unknown");
5543 # else
5544 #  if !defined(__PASE__)
5545         (void)fprintf (st, "\n   Host OS: IBM AIX");
5546 #  else
5547         (void)fprintf (st, "\n   Host OS: IBM OS/400 (PASE)");
5548 #  endif /* if !defined(__PASE__) */
5549 # endif /* if !defined(_AIX) */
5550     }
5551 #endif
5552 #if defined(__APPLE__)
5553     int isRosetta = processIsTranslated();
5554     if (isRosetta == 1) {
5555         sim_printf ("\n\n  ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
5556     }
5557 #endif
5558     if (nodist)
5559       {
5560         sim_printf ("\n\n ********* LICENSE RESTRICTED BUILD *** NOT FOR REDISTRIBUTION *********\n");
5561       }
5562     else
5563       {
5564         (void)fprintf (st, "\n");
5565         (void)fprintf (st, "\n This software is made available under the terms of the ICU License.");
5566         (void)fprintf (st, "\n For complete license details, see the LICENSE file included with the");
5567         (void)fprintf (st, "\n software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
5568       }
5569         (void)fprintf (st, "\n");
5570     }
5571 return SCPE_OK;
5572 }
5573 
5574 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5575 {
5576 size_t i;
5577 DEVICE *dptr;
5578 t_bool only_enabled = (sim_switches & SWMASK ('E'));
5579 
5580 if (cptr && (*cptr != 0))
5581     return SCPE_2MARG;
5582 (void)fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
5583 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5584     if (!only_enabled || !qdisable (dptr))
5585         show_device (st, dptr, flag);
5586 return SCPE_OK;
5587 }
5588 
5589 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5590 {
5591 int32 i;
5592 DEVICE *dptr;
5593 
5594 if (cptr && (*cptr != 0))
5595     return SCPE_2MARG;
5596 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5597     show_dev_logicals (st, dptr, NULL, 1, cptr);
5598 return SCPE_OK;
5599 }
5600 
5601 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5602 {
5603 if (dptr->lname)
5604     (void)fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
5605 else if (!flag)
5606     fputs ("no logical name assigned\n", st);
5607 return SCPE_OK;
5608 }
5609 
5610 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5611 {
5612 DEVICE *dptr;
5613 UNIT *uptr;
5614 int32 accum;
5615 
5616 if (cptr && (*cptr != 0))
5617     return SCPE_2MARG;
5618 if (sim_clock_queue == QUEUE_LIST_END)
5619     (void)fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructions/sec\n",
5620                    sim_name, sim_time, sim_timer_inst_per_sec ());
5621 else {
5622     const char *tim;
5623 
5624     (void)fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
5625                    sim_name, sim_time, sim_timer_inst_per_sec ());
5626     accum = 0;
5627     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
5628         if (uptr == &sim_step_unit)
5629             (void)fprintf (st, "  Step timer");
5630         else
5631             if (uptr == &sim_expect_unit)
5632                 (void)fprintf (st, "  Expect fired");
5633             else
5634                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
5635                     (void)fprintf (st, "  %s", sim_dname (dptr));
5636                     if (dptr->numunits > 1)
5637                         (void)fprintf (st, " unit %d", (int32) (uptr - dptr->units));
5638                     }
5639                 else
5640                     (void)fprintf (st, "  Unknown");
5641         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
5642         (void)fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
5643                        (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
5644                        (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
5645         accum = accum + uptr->time;
5646         }
5647     }
5648 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
5649 return SCPE_OK;
5650 }
5651 
5652 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5653 {
5654 if (cptr && (*cptr != 0))
5655     return SCPE_2MARG;
5656 (void)fprintf (st, "Time:\t%.0f\n", sim_gtime());
5657 return SCPE_OK;
5658 }
5659 
5660 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5661 {
5662 t_stat r;
5663 
5664 if (cptr && (*cptr != 0))
5665     r = ssh_break (st, cptr, 1);  /* more? */
5666 else
5667     r = sim_brk_showall (st, (uint32)sim_switches);
5668 return r;
5669 }
5670 
5671 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5672 {
5673 (void)fprintf (st, "Radix=%d\n", dptr->dradix);
5674 return SCPE_OK;
5675 }
5676 
5677 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5678 {
5679 int32 any = 0;
5680 DEBTAB *dep;
5681 
5682 if (dptr->flags & DEV_DEBUG) {
5683     if (dptr->dctrl == 0)
5684         fputs ("Debugging disabled", st);
5685     else if (dptr->debflags == NULL)
5686         fputs ("Debugging enabled", st);
5687     else {
5688         uint32 dctrl = dptr->dctrl;
5689 
5690         fputs ("Debug=", st);
5691         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
5692             if ((dctrl & dep->mask) == dep->mask) {
5693                 dctrl &= ~dep->mask;
5694                 if (any)
5695                     fputc (';', st);
5696                 fputs (dep->name, st);
5697                 any = 1;
5698                 }
5699             }
5700         }
5701     fputc ('\n', st);
5702     return SCPE_OK;
5703     }
5704 else return SCPE_NOFNC;
5705 }
5706 
5707 /* Show On actions */
5708 
5709 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5710 {
5711 int32 lvl, i;
5712 
5713 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
5714 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
5715     if (lvl > 0)
5716         (void)fprintf(st, "On Processing at Do Nest Level: %d", lvl);
5717     else
5718         (void)fprintf(st, "On Processing for input commands");
5719     (void)fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
5720     for (i=1; i<SCPE_BASE; ++i) {
5721         if (sim_on_actions[lvl][i])
5722             (void)fprintf(st, "    on %5d    %s\n", i, sim_on_actions[lvl][i]); }
5723     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
5724         if (sim_on_actions[lvl][i])
5725             (void)fprintf(st, "    on %-5s    %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
5726     if (sim_on_actions[lvl][0])
5727         (void)fprintf(st, "    on ERROR    %s\n", sim_on_actions[lvl][0]);
5728     (void)fprintf(st, "\n");
5729     }
5730 if (sim_on_inherit)
5731     (void)fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
5732 return SCPE_OK;
5733 }
5734 
5735 /* Show modifiers */
5736 
5737 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5738 {
5739 int32 i;
5740 DEVICE *dptr;
5741 
5742 if (cptr && (*cptr != 0))                               /* now eol? */
5743     return SCPE_2MARG;
5744 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5745     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5746 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5747     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5748 return SCPE_OK;
5749 }
5750 
5751 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5752 {
5753 fprint_set_help (st, dptr);
5754 return SCPE_OK;
5755 }
5756 
5757 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
     /* [previous][next][first][last][top][bottom][index][help] */
5758 {
5759 MTAB *mptr;
5760 t_stat r = SCPE_OK;
5761 
5762 if (dptr->modifiers == NULL)
5763     return SCPE_OK;
5764 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
5765     if (mptr->pstring &&
5766         ((mptr->mask & MTAB_XTD)?
5767             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
5768             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
5769         if (*toks > 0) {
5770             (void)fprintf (st, "\n");
5771             *toks = 0;
5772             }
5773         if (r == SCPE_OK)
5774             fprint_sep (st, toks);
5775         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
5776         }
5777     }
5778 return SCPE_OK;
5779 }
5780 
5781 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
     /* [previous][next][first][last][top][bottom][index][help] */
5782     CONST char *cptr, int32 flag)
5783 {
5784 t_stat r = SCPE_OK;
5785 
5786 if (mptr->disp)
5787     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
5788 else
5789     fputs (mptr->pstring, st);
5790 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
5791     fputc ('\n', st);
5792 return r;
5793 }
5794 
5795 /* Show show commands */
5796 
5797 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5798 {
5799 int32 i;
5800 DEVICE *dptr;
5801 
5802 if (cptr && (*cptr != 0))                               /* now eol? */
5803     return SCPE_2MARG;
5804 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5805     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5806 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5807     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5808 return SCPE_OK;
5809 }
5810 
5811 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] */
5812 {
5813 fprint_show_help (st, dptr);
5814 return SCPE_OK;
5815 }
5816 
5817 /* Breakpoint commands */
5818 
5819 t_stat brk_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5820 {
5821 GET_SWITCHES (cptr);                                    /* get switches */
5822 return ssh_break (NULL, cptr, flg);                     /* call common code */
5823 }
5824 
5825 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
     /* [previous][next][first][last][top][bottom][index][help] */
5826 {
5827 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
5828 CONST char *tptr, *t1ptr;
5829 DEVICE *dptr = sim_dflt_dev;
5830 UNIT *uptr;
5831 t_stat r;
5832 t_addr lo, hi, max;
5833 int32 cnt;
5834 
5835 if (sim_brk_types == 0)
5836     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5837 if (dptr == NULL)
5838     return SCPE_IERR;
5839 uptr = dptr->units;
5840 if (uptr == NULL)
5841     return SCPE_IERR;
5842 max = uptr->capac - 1;
5843 abuf[sizeof(abuf)-1] = '\0';
5844 strncpy (abuf, cptr, sizeof(abuf)-1);
5845 cptr = abuf;
5846 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
5847     if (flg != SSH_ST)                                  /* only on SET */
5848         return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
5849     *aptr++ = 0;                                        /* separate strings */
5850     }
5851 if (*cptr == 0) {                                       /* no argument? */
5852     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
5853     return ssh_break_one (st, flg, lo, 0, aptr);
5854     }
5855 while (*cptr) {
5856     cptr = get_glyph (cptr, gbuf, ',');
5857     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
5858     if (tptr == NULL)
5859         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
5860     if (*tptr == '[') {
5861         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
5862         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
5863             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
5864         tptr = t1ptr + 1;
5865         }
5866     else cnt = 0;
5867     if (*tptr != 0)
5868         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
5869     if ((lo == 0) && (hi == max)) {
5870         if (flg == SSH_CL)
5871             sim_brk_clrall (sim_switches);
5872         else
5873             if (flg == SSH_SH)
5874                 sim_brk_showall (st, (uint32)sim_switches);
5875             else
5876                 return SCPE_ARG;
5877         }
5878     else {
5879         for ( ; lo <= hi; lo = lo + 1) {
5880             r = ssh_break_one (st, flg, lo, cnt, aptr);
5881             if (r != SCPE_OK)
5882                 return r;
5883             }
5884         }
5885     }
5886 return SCPE_OK;
5887 }
5888 
5889 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] */
5890 {
5891 if (!sim_brk_types)
5892     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5893 switch (flg) {
5894     case SSH_ST:
5895         return sim_brk_set (lo, sim_switches, cnt, aptr);
5896         /*NOTREACHED*/ /* unreachable */
5897         break;
5898 
5899     case SSH_CL:
5900         return sim_brk_clr (lo, sim_switches);
5901         /*NOTREACHED*/ /* unreachable */
5902         break;
5903 
5904     case SSH_SH:
5905         return sim_brk_show (st, lo, sim_switches);
5906         /*NOTREACHED*/ /* unreachable */
5907         break;
5908 
5909     default:
5910         return SCPE_ARG;
5911     }
5912 }
5913 
5914 /* Reset command and routines */
5915 
5916 static t_bool run_cmd_did_reset = FALSE;
5917 
5918 t_stat reset_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5919 {
5920 char gbuf[CBUFSIZE];
5921 DEVICE *dptr;
5922 
5923 GET_SWITCHES (cptr);                                    /* get switches */
5924 run_cmd_did_reset = FALSE;
5925 if (*cptr == 0)                                         /* reset(cr) */
5926     return (reset_all (0));
5927 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5928 if (*cptr != 0)                                         /* now eol? */
5929     return SCPE_2MARG;
5930 if (strcmp (gbuf, "ALL") == 0)
5931     return (reset_all (0));
5932 dptr = find_dev (gbuf);                                 /* locate device */
5933 if (dptr == NULL)                                       /* found it? */
5934     return SCPE_NXDEV;
5935 if (dptr->reset != NULL)
5936     return dptr->reset (dptr);
5937 else return SCPE_OK;
5938 }
5939 
5940 /* Reset devices start..end
5941 
5942    Inputs:
5943         start   =       number of starting device
5944    Outputs:
5945         status  =       error status
5946 */
5947 
5948 t_stat reset_all (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5949 {
5950 DEVICE *dptr;
5951 uint32 i;
5952 t_stat reason;
5953 
5954 for (i = 0; i < start; i++) {
5955     if (sim_devices[i] == NULL)
5956         return SCPE_IERR;
5957     }
5958 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
5959     if (dptr->reset != NULL) {
5960         reason = dptr->reset (dptr);
5961         if (reason != SCPE_OK)
5962             return reason;
5963         }
5964     }
5965 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
5966     if (dptr->reset != NULL) {
5967         reason = dptr->reset (dptr);
5968         if (reason != SCPE_OK)
5969             return reason;
5970         }
5971     }
5972 return SCPE_OK;
5973 }
5974 
5975 /* Reset to powerup state
5976 
5977    Inputs:
5978         start   =       number of starting device
5979    Outputs:
5980         status  =       error status
5981 */
5982 
5983 t_stat reset_all_p (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5984 {
5985 t_stat r;
5986 int32 old_sw = sim_switches;
5987 
5988 sim_switches = SWMASK ('P');
5989 r = reset_all (start);
5990 sim_switches = old_sw;
5991 return r;
5992 }
5993 
5994 /* Attach command */
5995 
5996 t_stat attach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5997 {
5998 char gbuf[4*CBUFSIZE];
5999 DEVICE *dptr;
6000 UNIT *uptr;
6001 t_stat r;
6002 
6003 GET_SWITCHES (cptr);                                    /* get switches */
6004 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6005     return SCPE_2FARG;
6006 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6007 GET_SWITCHES (cptr);                                    /* get switches */
6008 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* now eol? */
6009     return SCPE_2FARG;
6010 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6011 if (dptr == NULL)                                       /* found dev? */
6012     return SCPE_NXDEV;
6013 if (uptr == NULL)                                       /* valid unit? */
6014     return SCPE_NXUN;
6015 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
6016     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
6017         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
6018         r = scp_detach_unit (dptr, uptr);               /* detach it */
6019         if (r != SCPE_OK)                               /* error? */
6020             return r; }
6021     else {
6022         if (!(uptr->dynflags & UNIT_ATTMULT))
6023             return SCPE_ALATT;                          /* Already attached */
6024         }
6025     }
6026 gbuf[sizeof(gbuf)-1] = '\0';
6027 strncpy (gbuf, cptr, sizeof(gbuf)-1);
6028 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
6029 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
6030 }
6031 
6032 /* Call device-specific or file-oriented attach unit routine */
6033 
6034 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6035 {
6036 if (dptr->attach != NULL)                               /* device routine? */
6037     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
6038 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
6039 }
6040 
6041 /* Attach unit to file */
6042 
6043 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6044 {
6045 DEVICE *dptr;
6046 
6047 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6048     return SCPE_UDIS;
6049 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
6050     return SCPE_NOATT;
6051 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6052     return SCPE_NOATT;
6053 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
6054 if (uptr->filename == NULL)
6055     return SCPE_MEM;
6056 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
6057 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
6058     ((uptr->flags & UNIT_RO) != 0)) {
6059     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
6060         ((uptr->flags & UNIT_RO) == 0))
6061         return attach_err (uptr, SCPE_NORO);            /* no, error */
6062     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
6063     if (uptr->fileref == NULL)                          /* open fail? */
6064         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
6065     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
6066     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6067         sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
6068         }
6069     }
6070 else {
6071     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
6072         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
6073         if (uptr->fileref == NULL)                      /* open fail? */
6074             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
6075         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
6076             sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
6077             }
6078         }
6079     else {                                              /* normal */
6080         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
6081         if (uptr->fileref == NULL) {                    /* open fail? */
6082 #if defined(EWOULDBLOCK)
6083             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
6084 #else
6085             if ((errno == EAGAIN))
6086 #endif
6087                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6088 
6089 #if defined(EPERM)
6090             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
6091 #else
6092             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
6093 #endif
6094                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
6095                     return attach_err (uptr, SCPE_NORO);/* no error */
6096                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
6097                 if (uptr->fileref == NULL)              /* open fail? */
6098                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6099                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
6100                 if (!sim_quiet) {
6101                     sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
6102                     }
6103                 }
6104             else {                                      /* doesn't exist */
6105                 if (sim_switches & SWMASK ('E'))        /* must exist? */
6106                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6107                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
6108                 if (uptr->fileref == NULL)              /* open fail? */
6109                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
6110                 if (!sim_quiet) {
6111                     sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
6112                     }
6113                 }
6114             }                                           /* end if null */
6115         }                                               /* end else */
6116     }
6117 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
6118     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
6119     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
6120         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
6121     if (uptr->filebuf == NULL)                          /* no buffer? */
6122         return attach_err (uptr, SCPE_MEM);             /* error */
6123     if (!sim_quiet) {
6124         sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
6125         }
6126     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
6127         SZ_D (dptr), cap, uptr->fileref);
6128     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
6129     }
6130 uptr->flags = uptr->flags | UNIT_ATT;
6131 uptr->pos = 0;
6132 return SCPE_OK;
6133 }
6134 
6135 t_stat attach_err (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
6136 {
6137 FREE (uptr->filename);
6138 uptr->filename = NULL;
6139 return stat;
6140 }
6141 
6142 /* Detach command */
6143 
6144 t_stat detach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6145 {
6146 char gbuf[CBUFSIZE];
6147 DEVICE *dptr;
6148 UNIT *uptr;
6149 
6150 GET_SWITCHES (cptr);                                    /* get switches */
6151 if ((NULL == cptr) || (*cptr == 0)) //-V560             /* must be more */
6152     return SCPE_2FARG;
6153 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
6154 if (*cptr != 0)                                         /* now eol? */
6155     return SCPE_2MARG;
6156 if (strcmp (gbuf, "ALL") == 0)
6157     return (detach_all (0, FALSE));
6158 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
6159 if (dptr == NULL)                                       /* found dev? */
6160     return SCPE_NXDEV;
6161 if (uptr == NULL)                                       /* valid unit? */
6162     return SCPE_NXUN;
6163 return scp_detach_unit (dptr, uptr);                    /* detach */
6164 }
6165 
6166 /* Detach devices start..end
6167 
6168    Inputs:
6169         start   =       number of starting device
6170         shutdown =      TRUE if simulator shutting down
6171    Outputs:
6172         status  =       error status
6173 
6174    Note that during shutdown, detach routines for non-attachable devices
6175    will be called.  These routines can implement simulator shutdown.  Error
6176    returns during shutdown are ignored.
6177 */
6178 
6179 t_stat detach_all (int32 start, t_bool shutdown)
     /* [previous][next][first][last][top][bottom][index][help] */
6180 {
6181 uint32 i, j;
6182 DEVICE *dptr;
6183 UNIT *uptr;
6184 t_stat r;
6185 
6186 if ((start < 0) || (start > 1))
6187     return SCPE_IERR;
6188 if (shutdown)
6189     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
6190 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
6191     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
6192         uptr = (dptr->units) + j;
6193         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
6194             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
6195             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
6196             r = scp_detach_unit (dptr, uptr);           /* detach unit */
6197 
6198             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
6199                 return r;                               /* bail out now with error status */
6200             }
6201         }
6202     }
6203 return SCPE_OK;
6204 }
6205 
6206 /* Call device-specific or file-oriented detach unit routine */
6207 
6208 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6209 {
6210 if (dptr->detach != NULL)                               /* device routine? */
6211     return dptr->detach (uptr);
6212 return detach_unit (uptr);                              /* no, standard */
6213 }
6214 
6215 /* Detach unit from file */
6216 
6217 t_stat detach_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6218 {
6219 DEVICE *dptr;
6220 
6221 if (uptr == NULL)
6222     return SCPE_IERR;
6223 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
6224     return SCPE_NOATT;
6225 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
6226     if (sim_switches & SIM_SW_REST)                     /* restoring? */
6227         return SCPE_OK;                                 /* allow detach */
6228     else
6229         return SCPE_NOTATT;                             /* complain */
6230     }
6231 if ((dptr = find_dev_from_unit (uptr)) == NULL)
6232     return SCPE_OK;
6233 if (uptr->flags & UNIT_BUF) {
6234     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
6235     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
6236         if (!sim_quiet) {
6237             sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
6238             }
6239         rewind (uptr->fileref);
6240         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
6241         if (ferror (uptr->fileref))
6242             sim_printf ("%s: I/O error - %s (Error %d)",
6243                         sim_dname (dptr), xstrerror_l(errno), errno);
6244         }
6245     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
6246         FREE (uptr->filebuf);                           /* free buf */
6247         uptr->filebuf = NULL;
6248         }
6249     uptr->flags = uptr->flags & ~UNIT_BUF;
6250     }
6251 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
6252 FREE (uptr->filename);
6253 uptr->filename = NULL;
6254 if (fclose (uptr->fileref) == EOF)
6255     return SCPE_IOERR;
6256 return SCPE_OK;
6257 }
6258 
6259 /* Get device display name */
6260 
6261 const char *sim_dname (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6262 {
6263 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
6264 }
6265 
6266 /* Get unit display name */
6267 
6268 const char *sim_uname (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6269 {
6270 DEVICE *d = find_dev_from_unit(uptr);
6271 static char uname[CBUFSIZE];
6272 
6273 if (!d)
6274     return "";
6275 if (d->numunits == 1)
6276     return sim_dname (d);
6277 (void)sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
6278 return uname;
6279 }
6280 
6281 /* Run, go, boot, cont, step, next commands */
6282 
6283 t_stat run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6284 {
6285 char gbuf[CBUFSIZE] = "";
6286 CONST char *tptr;
6287 uint32 i, j;
6288 int32 sim_next = 0;
6289 int32 unitno;
6290 t_value pcv, orig_pcv;
6291 t_stat r;
6292 DEVICE *dptr;
6293 UNIT *uptr;
6294 
6295 GET_SWITCHES (cptr);                                    /* get switches */
6296 sim_step = 0;
6297 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
6298     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
6299     if (*cptr != 0) {                                   /* argument? */
6300         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6301         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
6302             if (sim_dflt_dev && sim_vm_parse_addr)      /* address parser? */
6303                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
6304             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
6305             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
6306                 (pcv > width_mask[sim_PC->width]))
6307                 return SCPE_ARG;
6308             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
6309             }
6310         }
6311     if ((flag == RU_RUN) &&                             /* run? */
6312         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
6313         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
6314         return r;
6315         }
6316     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { //-V600 /* should be end */
6317         int32 saved_switches = sim_switches;
6318 
6319         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6320             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
6321         if (MATCH_CMD (gbuf, "UNTIL") != 0)
6322             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
6323                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
6324         sim_switches = 0;
6325         GET_SWITCHES (cptr);
6326         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
6327             r = expect_cmd (1, cptr);
6328             if (r != SCPE_OK)
6329                 return r;
6330             }
6331         else {                                          /* BREAK UNTIL condition */
6332             if (sim_switches == 0)
6333                 sim_switches = sim_brk_dflt;
6334             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
6335             sim_brk_types |= BRK_TYP_TEMP;
6336             r = ssh_break (NULL, cptr, SSH_ST);
6337             if (r != SCPE_OK)
6338                 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
6339             }
6340         sim_switches = saved_switches;
6341         }
6342     }
6343 
6344 else if ((flag == RU_STEP) ||
6345          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
6346     static t_bool not_implemented_message = FALSE;
6347 
6348     if ((!not_implemented_message) && (flag == RU_NEXT)) {
6349         not_implemented_message = TRUE;
6350         flag = RU_STEP;
6351         }
6352     if (*cptr != 0) {                                   /* argument? */
6353         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6354         if (*cptr != 0)                                 /* should be end */
6355             return SCPE_2MARG;
6356         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6357         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
6358             return SCPE_ARG;
6359         }
6360     else sim_step = 1;
6361     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
6362         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
6363     }
6364 else if (flag == RU_NEXT) {                             /* next */
6365     t_addr *addrs;
6366 
6367     if (*cptr != 0) {                                   /* argument? */
6368         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
6369         if (*cptr != 0)                                 /* should be end */
6370             return SCPE_2MARG;
6371         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
6372         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
6373             return SCPE_ARG;
6374         }
6375     else sim_next = 1;
6376     if (sim_vm_is_subroutine_call(&addrs)) {
6377         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6378         for (i=0; addrs[i]; i++)
6379             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6380         }
6381     else
6382         sim_step = 1;
6383     }
6384 else if (flag == RU_BOOT) {                             /* boot */
6385     if (*cptr == 0)                                     /* must be more */
6386         return SCPE_2FARG;
6387     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
6388     if (*cptr != 0)                                     /* should be end */
6389         return SCPE_2MARG;
6390     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
6391     if (dptr == NULL)                                   /* found dev? */
6392         return SCPE_NXDEV;
6393     if (uptr == NULL)                                   /* valid unit? */
6394         return SCPE_NXUN;
6395     if (dptr->boot == NULL)                             /* can it boot? */
6396         return SCPE_NOFNC;
6397     if (uptr->flags & UNIT_DIS)                         /* disabled? */
6398         return SCPE_UDIS;
6399     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
6400         !(uptr->flags & UNIT_ATT))
6401         return SCPE_UNATT;
6402     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
6403     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
6404         return r;
6405     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
6406         return r;
6407     }
6408 
6409 else
6410     if (flag != RU_CONT)                                /* must be cont */
6411         return SCPE_IERR;
6412     else                                                /* CONTINUE command */
6413         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
6414             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
6415 
6416 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
6417     return SCPE_OK;
6418 
6419 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
6420     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
6421         uptr = dptr->units + j;
6422         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
6423             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
6424         }
6425     }
6426 stop_cpu = 0;
6427 sim_is_running = 1;                                     /* flag running */
6428 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
6429     sim_is_running = 0;                                 /* flag idle */
6430     sim_ttcmd ();
6431     return SCPE_TTYERR;
6432     }
6433 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
6434     sim_is_running = 0;                                 /* flag idle */
6435     sim_ttcmd ();
6436     return r;
6437     }
6438 #if !defined(IS_WINDOWS)
6439 # if defined(SIGINT)
6440 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
6441     sim_is_running = 0;                                 /* flag idle */
6442     sim_ttcmd ();
6443     return SCPE_SIGERR;
6444     }
6445 # endif
6446 #endif
6447 #if !defined(IS_WINDOWS)
6448 # if defined(SIGHUP)
6449 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
6450     sim_is_running = 0;                                 /* flag idle */
6451     sim_ttcmd ();
6452     return SCPE_SIGERR;
6453     }
6454 # endif
6455 #endif
6456 #if !defined(IS_WINDOWS)
6457 # if defined(SIGTERM)
6458 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
6459     sim_is_running = 0;                                 /* flag idle */
6460     sim_ttcmd ();
6461     return SCPE_SIGERR;
6462     }
6463 # endif
6464 #endif
6465 if (sim_step)                                           /* set step timer */
6466     sim_activate (&sim_step_unit, sim_step);
6467 (void)fflush(stdout);                                   /* flush stdout */
6468 if (sim_log)                                            /* flush log if enabled */
6469     (void)fflush (sim_log);
6470 sim_rtcn_init_all ();                                   /* re-init clocks */
6471 sim_start_timer_services ();                            /* enable wall clock timing */
6472 
6473 do {
6474     t_addr *addrs;
6475 
6476     while (1) {
6477         r = sim_instr();
6478         if (r != SCPE_REMOTE)
6479             break;
6480         sim_remote_process_command ();                  /* Process the command and resume processing */
6481         }
6482     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
6483         (--sim_next <=0))
6484         break;
6485     if (sim_step == 0) {                                /* doing a NEXT? */
6486         t_addr val;
6487         BRKTAB *bp;
6488 
6489         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
6490             break;
6491         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
6492             val = (t_addr)(*sim_vm_pc_value)();
6493         else
6494             val = (t_addr)get_rval (sim_PC, 0);
6495         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6496             break;
6497         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
6498         }
6499     else {
6500         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
6501             break;
6502         }
6503     /* setup another next/step */
6504     sim_step = 0;
6505     if (sim_vm_is_subroutine_call(&addrs)) {
6506         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6507         for (i=0; addrs[i]; i++)
6508             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6509         }
6510     else
6511         sim_step = 1;
6512     if (sim_step)                                       /* set step timer */
6513         sim_activate (&sim_step_unit, sim_step);
6514     } while (1);
6515 
6516 sim_is_running = 0;                                     /* flag idle */
6517 sim_stop_timer_services ();                             /* disable wall clock timing */
6518 sim_ttcmd ();                                           /* restore console */
6519 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
6520 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
6521 #if defined(SIGHUP)
6522 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
6523 #endif
6524 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
6525 if (sim_log)                                            /* flush console log */
6526     (void)fflush (sim_log);
6527 if (sim_deb)                                            /* flush debug log */
6528     sim_debug_flush ();
6529 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
6530     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
6531         uptr = dptr->units + j;
6532         if (uptr->flags & UNIT_ATT) {                   /* attached, */
6533             if (uptr->io_flush)                         /* unit specific flush routine */
6534                 uptr->io_flush (uptr);                  /* call it */
6535             else {
6536                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
6537                     (uptr->fileref) &&                  /* real file, */
6538                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
6539                     !(uptr->flags & UNIT_RO))           /* not read only? */
6540                     (void)fflush (uptr->fileref);
6541                 }
6542             }
6543         }
6544     }
6545 sim_cancel (&sim_step_unit);                            /* cancel step timer */
6546 UPDATE_SIM_TIME;                                        /* update sim time */
6547 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6548 }
6549 
6550 /* run command message handler */
6551 
6552 void
6553 run_cmd_message (const char *unechoed_cmdline, t_stat r)
     /* [previous][next][first][last][top][bottom][index][help] */
6554 {
6555 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6556     sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
6557 #if defined(WIN_STDIO)
6558 (void)fflush(stderr);
6559 (void)fflush(stdout);
6560 #endif /* if defined(WIN_STDIO) */
6561 fprint_stopped (stdout, r);                         /* print msg */
6562 if (sim_log && (sim_log != stdout))                 /* log if enabled */
6563     fprint_stopped (sim_log, r);
6564 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6565     fprint_stopped (sim_deb, r);
6566 #if defined(WIN_STDIO)
6567 (void)fflush(stderr);
6568 (void)fflush(stdout);
6569 #endif /* if defined(WIN_STDIO) */
6570 }
6571 
6572 /* Common setup for RUN or BOOT */
6573 
6574 t_stat sim_run_boot_prep (int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
6575 {
6576 UNIT *uptr;
6577 t_stat r;
6578 
6579 sim_interval = 0;                                       /* reset queue */
6580 sim_time = sim_rtime = 0;
6581 noqueue_time = 0;
6582 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6583     sim_clock_queue = uptr->next;
6584     uptr->next = NULL;
6585     }
6586 r = reset_all (0);
6587 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6588     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6589         sim_printf ("Resetting all devices...  This may not have been your intention.\n");
6590         sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
6591         }
6592     run_cmd_did_reset = TRUE;
6593     }
6594 return r;
6595 }
6596 
6597 /* Print stopped message
6598  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6599  * call the indicated routine to print additional information after the message
6600  * and before the PC value is printed.  If the routine returns FALSE, skip
6601  * printing the PC and its related instruction.
6602  */
6603 
6604 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6605 {
6606 int32 i;
6607 t_stat r = 0;
6608 t_addr k;
6609 t_value pcval;
6610 
6611 fputc ('\n', st);                                       /* start on a new line */
6612 
6613 if (v >= SCPE_BASE)                                     /* SCP error? */
6614     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
6615 else {                                                  /* VM error */
6616     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6617 
6618     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6619         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6620         return;                                         /*     we're done */
6621     }
6622 
6623 (void)fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6624 
6625 pcval = get_rval (pc, 0);
6626 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6627     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6628 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6629     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6630 if ((dptr != NULL) && (dptr->examine != NULL)) {
6631     for (i = 0; i < sim_emax; i++)
6632         sim_eval[i] = 0;
6633     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6634         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6635             break;
6636         }
6637     if ((r == SCPE_OK) || (i > 0)) {
6638         (void)fprintf (st, " (");
6639         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6640             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6641         (void)fprintf (st, ")");
6642         }
6643     }
6644 (void)fprintf (st, "\n");
6645 return;
6646 }
6647 
6648 void fprint_stopped (FILE *st, t_stat v)
     /* [previous][next][first][last][top][bottom][index][help] */
6649 {
6650 #if defined(WIN_STDIO)
6651 (void)fflush(stderr);
6652 (void)fflush(stdout);
6653 #endif /* if defined(WIN_STDIO) */
6654 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6655 return;
6656 }
6657 
6658 /* Unit service for step timeout, originally scheduled by STEP n command
6659    Return step timeout SCP code, will cause simulation to stop */
6660 
6661 t_stat step_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6662 {
6663 return SCPE_STEP;
6664 }
6665 
6666 /* Unit service to facilitate expect matching to stop simulation.
6667    Return expect SCP code, will cause simulation to stop */
6668 
6669 t_stat expect_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6670 {
6671 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6672 }
6673 
6674 /* Signal handler for ^C signal - set stop simulation flag */
6675 
6676 void int_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help] */
6677 {
6678 stop_cpu = 1;
6679 return;
6680 }
6681 
6682 /* Examine/deposit commands */
6683 
6684 t_stat exdep_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6685 {
6686 char gbuf[CBUFSIZE];
6687 CONST char *gptr;
6688 CONST char *tptr = NULL;
6689 int32 opt;
6690 t_addr low, high;
6691 t_stat reason = SCPE_IERR;
6692 DEVICE *tdptr;
6693 REG *lowr, *highr;
6694 FILE *ofile;
6695 
6696 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6697 if (flag == EX_E)                                       /* extra for EX */
6698     opt = opt | CMD_OPT_OF;
6699 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6700 if (NULL == cptr)                                       /* error? */
6701     return reason;
6702 if (*cptr == 0)                                         /* must be more */
6703     return SCPE_2FARG;
6704 if (sim_dfunit == NULL)                                 /* got a unit? */
6705     return SCPE_NXUN;
6706 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6707 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6708 
6709     return SCPE_2FARG;
6710 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6711 
6712 for (gptr = gbuf, reason = SCPE_OK;
6713     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6714     tdptr = sim_dfdev;                                  /* working dptr */
6715     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6716         tptr = gptr + strlen ("STATE");
6717         if (*tptr && (*tptr++ != ','))
6718             return SCPE_ARG;
6719         if ((lowr = sim_dfdev->registers) == NULL)
6720             return SCPE_NXREG;
6721         for (highr = lowr; highr->name != NULL; highr++) ;
6722         sim_switches = sim_switches | SIM_SW_HIDE;
6723         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6724             lowr, --highr, 0, 0);
6725         continue;
6726         }
6727 
6728     /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
6729     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6730         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6731         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6732         low = high = 0;
6733         if ((*tptr == '-') || (*tptr == ':')) {
6734             highr = find_reg (tptr + 1, &tptr, tdptr);
6735             if (highr == NULL)
6736                 return SCPE_NXREG;
6737             }
6738         else {
6739             highr = lowr;
6740             if (*tptr == '[') {
6741                 if (lowr->depth <= 1)
6742                     return SCPE_ARG;
6743                 tptr = get_range (NULL, tptr + 1, &low, &high,
6744                     10, lowr->depth - 1, ']');
6745                 if (tptr == NULL)
6746                     return SCPE_ARG;
6747                 }
6748             }
6749         if (*tptr && (*tptr++ != ','))
6750             return SCPE_ARG;
6751         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6752             lowr, highr, (uint32) low, (uint32) high);
6753         continue;
6754         }
6755 
6756     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6757         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6758         sim_dfunit->capac - sim_dfdev->aincr), 0);
6759     if (tptr == NULL)
6760         return SCPE_ARG;
6761     if (*tptr && (*tptr++ != ','))
6762         return SCPE_ARG;
6763     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6764         sim_dfdev, sim_dfunit);
6765     }                                                   /* end for */
6766 if (sim_ofile)                                          /* close output file */
6767     fclose (sim_ofile);
6768 return reason;
6769 }
6770 
6771 /* Loop controllers for examine/deposit
6772 
6773    exdep_reg_loop       examine/deposit range of registers
6774    exdep_addr_loop      examine/deposit range of addresses
6775 */
6776 
6777 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6778     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6779 {
6780 t_stat reason;
6781 uint32 idx, val_start=lows;
6782 t_value val, last_val;
6783 REG *rptr;
6784 
6785 if ((lowr == NULL) || (highr == NULL))
6786     return SCPE_IERR;
6787 if (lowr > highr)
6788     return SCPE_ARG;
6789 for (rptr = lowr; rptr <= highr; rptr++) {
6790     if ((sim_switches & SIM_SW_HIDE) &&
6791         (rptr->flags & REG_HIDDEN))
6792         continue;
6793     val = last_val = 0;
6794     for (idx = lows; idx <= highs; idx++) {
6795         if (idx >= rptr->depth)
6796             return SCPE_SUB;
6797         val = get_rval (rptr, idx);
6798         if (schptr && !test_search (&val, schptr))
6799             continue;
6800         if (flag == EX_E) {
6801             if ((idx > lows) && (val == last_val))
6802                 continue;
6803             if (idx > val_start+1) {
6804                 if (idx-1 == val_start+1) {
6805                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6806                     if (reason != SCPE_OK)
6807                         return reason;
6808                     if (sim_log && (ofile == stdout))
6809                         ex_reg (sim_log, val, flag, rptr, idx-1);
6810                     }
6811                 else {
6812                     if (val_start+1 != idx-1) {
6813                         (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6814                         if (sim_log && (ofile == stdout))
6815                             (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6816                         }
6817                     else {
6818                         (void)Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6819                         if (sim_log && (ofile == stdout))
6820                             (void)Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6821                         }
6822                     }
6823                 }
6824             sim_last_val = last_val = val;
6825             val_start = idx;
6826             reason = ex_reg (ofile, val, flag, rptr, idx);
6827             if (reason != SCPE_OK)
6828                 return reason;
6829             if (sim_log && (ofile == stdout))
6830                 ex_reg (sim_log, val, flag, rptr, idx);
6831             }
6832         if (flag != EX_E) {
6833             reason = dep_reg (flag, cptr, rptr, idx);
6834             if (reason != SCPE_OK)
6835                 return reason;
6836             }
6837         }
6838     if ((flag == EX_E) && (val_start != highs)) {
6839         if (highs == val_start+1) {
6840             reason = ex_reg (ofile, val, flag, rptr, highs);
6841             if (reason != SCPE_OK)
6842                 return reason;
6843             if (sim_log && (ofile == stdout))
6844                 ex_reg (sim_log, val, flag, rptr, highs);
6845             }
6846         else {
6847             if (val_start+1 != highs) {
6848                 (void)Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6849                 if (sim_log && (ofile == stdout))
6850                     (void)Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6851                 }
6852             else {
6853                 (void)Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6854                 if (sim_log && (ofile == stdout))
6855                     (void)Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6856                 }
6857             }
6858         }
6859     }
6860 return SCPE_OK;
6861 }
6862 
6863 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6864     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6865 {
6866 t_addr i, mask;
6867 t_stat reason;
6868 
6869 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6870     return SCPE_UDIS;
6871 mask = (t_addr) width_mask[dptr->awidth];
6872 if ((low > mask) || (high > mask) || (low > high))
6873     return SCPE_ARG;
6874 for (i = low; i <= high; ) {                            /* all paths must incr!! */
6875     reason = get_aval (i, dptr, uptr);                  /* get data */
6876     if (reason != SCPE_OK)                              /* return if error */
6877         return reason;
6878     if (schptr && !test_search (sim_eval, schptr))
6879         i = i + dptr->aincr;                            /* sch fails, incr */
6880     else {                                              /* no sch or success */
6881         if (flag != EX_D) {                             /* ex, ie, or id? */
6882             reason = ex_addr (ofile, flag, i, dptr, uptr);
6883             if (reason > SCPE_OK)
6884                 return reason;
6885             if (sim_log && (ofile == stdout))
6886                 ex_addr (sim_log, flag, i, dptr, uptr);
6887             }
6888         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
6889         if (flag != EX_E) {                             /* ie, id, or d? */
6890             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6891             if (reason > SCPE_OK)
6892                 return reason;
6893             }
6894         i = i + (1 - reason);                           /* incr */
6895         }
6896     }
6897 return SCPE_OK;
6898 }
6899 
6900 /* Examine register routine
6901 
6902    Inputs:
6903         ofile   =       output stream
6904         val     =       current register value
6905         flag    =       type of ex/mod command (ex, iex, idep)
6906         rptr    =       pointer to register descriptor
6907         idx     =       index
6908    Outputs:
6909         return  =       error status
6910 */
6911 
6912 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6913 {
6914 int32 rdx;
6915 
6916 if (rptr == NULL)
6917     return SCPE_IERR;
6918 if (rptr->depth > 1)
6919     (void)Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6920 else
6921     (void)Fprintf (ofile, "%s:\t", rptr->name);
6922 if (!(flag & EX_E))
6923     return SCPE_OK;
6924 GET_RADIX (rdx, rptr->radix);
6925 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6926     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6927 else if (!(rptr->flags & REG_VMFLAGS) ||
6928     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6929                  NULL, sim_switches | SIM_SW_REG) > 0)) {
6930         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6931         if (rptr->fields) {
6932             (void)Fprintf (ofile, "\t");
6933             fprint_fields (ofile, val, val, rptr->fields);
6934             }
6935         }
6936 if (flag & EX_I)
6937     (void)Fprintf (ofile, "\t");
6938 else
6939     (void)Fprintf (ofile, "\n");
6940 return SCPE_OK;
6941 }
6942 
6943 /* Get register value
6944 
6945    Inputs:
6946         rptr    =       pointer to register descriptor
6947         idx     =       index
6948    Outputs:
6949         return  =       register value
6950 */
6951 
6952 t_value get_rval (REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6953 {
6954 size_t sz;
6955 t_value val;
6956 uint32 *ptr;
6957 
6958 sz = SZ_R (rptr);
6959 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6960     idx = idx + rptr->qptr;
6961     if (idx >= rptr->depth) idx = idx - rptr->depth;
6962     }
6963 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6964     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6965     if (sz <= sizeof (uint32))
6966         val = *ptr;
6967     else val = *((t_uint64 *) ptr); //-V1032
6968     }
6969 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6970     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6971     if (sz <= sizeof (uint32))
6972         val = *ptr;
6973     else val = *((t_uint64 *) ptr);
6974     }
6975 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6976     (sz == sizeof (uint8)))
6977     val = *(((uint8 *) rptr->loc) + idx);
6978 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6979     (sz == sizeof (uint16)))
6980     val = *(((uint16 *) rptr->loc) + idx);
6981 else if (sz <= sizeof (uint32))
6982      val = *(((uint32 *) rptr->loc) + idx);
6983 else val = *(((t_uint64 *) rptr->loc) + idx);
6984 val = (val >> rptr->offset) & width_mask[rptr->width];
6985 return val;
6986 }
6987 
6988 /* Deposit register routine
6989 
6990    Inputs:
6991         flag    =       type of deposit (normal/interactive)
6992         cptr    =       pointer to input string
6993         rptr    =       pointer to register descriptor
6994         idx     =       index
6995    Outputs:
6996         return  =       error status
6997 */
6998 
6999 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
7000 {
7001 t_stat r;
7002 t_value val, mask;
7003 int32 rdx;
7004 CONST char *tptr;
7005 char gbuf[CBUFSIZE];
7006 
7007 if ((cptr == NULL) || (rptr == NULL))
7008     return SCPE_IERR;
7009 if (rptr->flags & REG_RO)
7010     return SCPE_RO;
7011 if (flag & EX_I) {
7012     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7013     if (sim_log)
7014         (void)fprintf (sim_log, "%s\n", cptr? cptr: "");
7015     if (cptr == NULL)                                   /* force exit */
7016         return 1;
7017     if (*cptr == 0)                                     /* success */
7018         return SCPE_OK;
7019     }
7020 mask = width_mask[rptr->width];
7021 GET_RADIX (rdx, rptr->radix);
7022 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
7023     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
7024     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
7025         return SCPE_ARG;
7026     }
7027 else
7028     if (!(rptr->flags & REG_VMFLAGS) ||                 /* don't use sym? */
7029         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
7030                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
7031     val = get_uint (cptr, rdx, mask, &r);
7032     if (r != SCPE_OK)
7033         return SCPE_ARG;
7034     }
7035 if ((rptr->flags & REG_NZ) && (val == 0))
7036     return SCPE_ARG;
7037 put_rval (rptr, idx, val);
7038 return SCPE_OK;
7039 }
7040 
7041 /* Put register value
7042 
7043    Inputs:
7044         rptr    =       pointer to register descriptor
7045         idx     =       index
7046         val     =       new value
7047         mask    =       mask
7048    Outputs:
7049         none
7050 */
7051 
7052 void put_rval (REG *rptr, uint32 idx, t_value val)
     /* [previous][next][first][last][top][bottom][index][help] */
7053 {
7054 size_t sz;
7055 t_value mask;
7056 uint32 *ptr;
7057 
7058 #define PUT_RVAL(sz,rp,id,v,m)            \
7059     *(((sz *) rp->loc) + id) =            \
7060         (sz)((*(((sz *) rp->loc) + id) &  \
7061             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
7062 
7063 if (rptr == sim_PC)
7064     sim_brk_npc (0);
7065 sz = SZ_R (rptr);
7066 mask = width_mask[rptr->width];
7067 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
7068     idx = idx + rptr->qptr;
7069     if (idx >= rptr->depth)
7070         idx = idx - rptr->depth;
7071     }
7072 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
7073     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
7074     if (sz <= sizeof (uint32))
7075         *ptr = (*ptr &
7076         ~(((uint32) mask) << rptr->offset)) |
7077         (((uint32) val) << rptr->offset);
7078     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr) //-V1032
7079         & ~(mask << rptr->offset)) | (val << rptr->offset);
7080     }
7081 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
7082     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
7083     if (sz <= sizeof (uint32))
7084         *((uint32 *) ptr) = (*((uint32 *) ptr) &
7085         ~(((uint32) mask) << rptr->offset)) |
7086         (((uint32) val) << rptr->offset);
7087     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
7088         & ~(mask << rptr->offset)) | (val << rptr->offset);
7089     }
7090 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7091     (sz == sizeof (uint8)))
7092     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
7093 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
7094     (sz == sizeof (uint16)))
7095     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
7096 else if (sz <= sizeof (uint32))
7097     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
7098 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
7099 return;
7100 }
7101 
7102 /* Examine address routine
7103 
7104    Inputs: (sim_eval is an implicit argument)
7105         ofile   =       output stream
7106         flag    =       type of ex/mod command (ex, iex, idep)
7107         addr    =       address to examine
7108         dptr    =       pointer to device
7109         uptr    =       pointer to unit
7110    Outputs:
7111         return  =       if > 0, error status
7112                         if <= 0,-number of extra addr units retired
7113 */
7114 
7115 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7116 {
7117 t_stat reason;
7118 int32 rdx;
7119 
7120 if (sim_vm_fprint_addr)
7121     sim_vm_fprint_addr (ofile, dptr, addr);
7122 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
7123 (void)Fprintf (ofile, ":\t");
7124 if (!(flag & EX_E))
7125     return (1 - dptr->aincr);
7126 
7127 GET_RADIX (rdx, dptr->dradix);
7128 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
7129     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
7130     reason = 1 - dptr->aincr;
7131     }
7132 if (flag & EX_I)
7133     (void)Fprintf (ofile, "\t");
7134 else
7135     (void)Fprintf (ofile, "\n");
7136 return reason;
7137 }
7138 
7139 /* Get address routine
7140 
7141    Inputs:
7142         flag    =       type of ex/mod command (ex, iex, idep)
7143         addr    =       address to examine
7144         dptr    =       pointer to device
7145         uptr    =       pointer to unit
7146    Outputs: (sim_eval is an implicit output)
7147         return  =       error status
7148 */
7149 
7150 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7151 {
7152 int32 i;
7153 t_value mask;
7154 t_addr j, loc;
7155 size_t sz;
7156 t_stat reason = SCPE_OK;
7157 
7158 if ((dptr == NULL) || (uptr == NULL))
7159     return SCPE_IERR;
7160 mask = width_mask[dptr->dwidth];
7161 for (i = 0; i < sim_emax; i++)
7162     sim_eval[i] = 0;
7163 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
7164     if (dptr->examine != NULL) {
7165         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
7166         if (reason != SCPE_OK)
7167             break;
7168         }
7169     else {
7170         if (!(uptr->flags & UNIT_ATT))
7171             return SCPE_UNATT;
7172         if (uptr->dynflags & UNIT_NO_FIO)
7173             return SCPE_NOFNC;
7174         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
7175             reason = SCPE_NXM;
7176             break;
7177             }
7178         sz = SZ_D (dptr);
7179         loc = j / dptr->aincr;
7180         if (uptr->flags & UNIT_BUF) {
7181             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
7182             }
7183         else {
7184             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7185             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
7186             if ((feof (uptr->fileref)) &&
7187                !(uptr->flags & UNIT_FIX)) {
7188                 reason = SCPE_EOF;
7189                 break;
7190                 }
7191             else if (ferror (uptr->fileref)) {
7192                 clearerr (uptr->fileref);
7193                 reason = SCPE_IOERR;
7194                 break;
7195                 }
7196             }
7197         }
7198     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
7199     }
7200 if ((reason != SCPE_OK) && (i == 0))
7201     return reason;
7202 return SCPE_OK;
7203 }
7204 
7205 /* Deposit address routine
7206 
7207    Inputs:
7208         flag    =       type of deposit (normal/interactive)
7209         cptr    =       pointer to input string
7210         addr    =       address to examine
7211         dptr    =       pointer to device
7212         uptr    =       pointer to unit
7213         dfltinc =       value to return on cr input
7214    Outputs:
7215         return  =       if > 0, error status
7216                         if <= 0, -number of extra address units retired
7217 */
7218 
7219 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
7220     UNIT *uptr, int32 dfltinc)
7221 {
7222 int32 i, count, rdx;
7223 t_addr j, loc;
7224 t_stat r, reason;
7225 t_value mask;
7226 size_t sz;
7227 char gbuf[CBUFSIZE];
7228 
7229 if (dptr == NULL)
7230     return SCPE_IERR;
7231 if (flag & EX_I) {
7232     cptr = read_line (gbuf, sizeof(gbuf), stdin);
7233     if (sim_log)
7234         (void)fprintf (sim_log, "%s\n", cptr? cptr: "");
7235     if (cptr == NULL)                                   /* force exit */
7236         return 1;
7237     if (*cptr == 0)                                     /* success */
7238         return dfltinc;
7239     }
7240 if (uptr->flags & UNIT_RO)                              /* read only? */
7241     return SCPE_RO;
7242 mask = width_mask[dptr->dwidth];
7243 
7244 GET_RADIX (rdx, dptr->dradix);
7245 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
7246     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
7247     if (reason != SCPE_OK)
7248         return reason;
7249     reason = dfltinc;
7250     }
7251 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
7252 
7253 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
7254     sim_eval[i] = sim_eval[i] & mask;
7255     if (dptr->deposit != NULL) {
7256         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
7257         if (r != SCPE_OK)
7258             return r;
7259         }
7260     else {
7261         if (!(uptr->flags & UNIT_ATT))
7262             return SCPE_UNATT;
7263         if (uptr->dynflags & UNIT_NO_FIO)
7264             return SCPE_NOFNC;
7265         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
7266             return SCPE_NXM;
7267         sz = SZ_D (dptr);
7268         loc = j / dptr->aincr;
7269         if (uptr->flags & UNIT_BUF) {
7270             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
7271             if (loc >= uptr->hwmark)
7272                 uptr->hwmark = (uint32) loc + 1;
7273             }
7274         else {
7275             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
7276             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
7277             if (ferror (uptr->fileref)) {
7278                 clearerr (uptr->fileref);
7279                 return SCPE_IOERR;
7280                 }
7281             }
7282         }
7283     }
7284 return reason;
7285 }
7286 
7287 /* Evaluate command */
7288 
7289 t_stat eval_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7290 {
7291 if (!sim_dflt_dev)
7292   return SCPE_ARG;
7293 DEVICE *dptr = sim_dflt_dev;
7294 int32 i, rdx, a, lim;
7295 t_stat r;
7296 
7297 GET_SWITCHES (cptr);
7298 GET_RADIX (rdx, dptr->dradix);
7299 for (i = 0; i < sim_emax; i++)
7300 sim_eval[i] = 0;
7301 if (*cptr == 0)
7302     return SCPE_2FARG;
7303 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
7304     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
7305     if (r != SCPE_OK)
7306         return r;
7307     }
7308 lim = 1 - r;
7309 for (i = a = 0; a < lim; ) {
7310     sim_printf ("%d:\t", a);
7311     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7312         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7313     if (sim_log) {
7314         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
7315             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
7316         }
7317     sim_printf ("\n");
7318     if (r < 0)
7319         a = a + 1 - r;
7320     else a = a + dptr->aincr;
7321     i = a / dptr->aincr;
7322     }
7323 return SCPE_OK;
7324 }
7325 
7326 /* String processing routines
7327 
7328    read_line            read line
7329 
7330    Inputs:
7331         cptr    =       pointer to buffer
7332         size    =       maximum size
7333         stream  =       pointer to input stream
7334    Outputs:
7335         optr    =       pointer to first non-blank character
7336                         NULL if EOF
7337 */
7338 
7339 char *read_line (char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7340 {
7341 return read_line_p (NULL, cptr, size, stream);
7342 }
7343 
7344 /* read_line_p          read line with prompt
7345 
7346    Inputs:
7347         prompt  =       pointer to prompt string
7348         cptr    =       pointer to buffer
7349         size    =       maximum size
7350         stream  =       pointer to input stream
7351    Outputs:
7352         optr    =       pointer to first non-blank character
7353                         NULL if EOF
7354 */
7355 
7356 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
7357 {
7358 char *tptr;
7359 
7360 if (prompt) {                                           /* interactive? */
7361 #if defined(HAVE_LINEHISTORY)
7362         char *tmpc = linenoise (prompt);                /* get cmd line */
7363         if (tmpc == NULL)                               /* bad result? */
7364             cptr = NULL;
7365         else {
7366             strncpy (cptr, tmpc, size-1);               /* copy result */
7367             linenoiseHistoryAdd (tmpc);                 /* add to history */
7368             FREE (tmpc);                                /* free temp */
7369             }
7370         }
7371 #else
7372         (void)fflush (stdout);                          /* flush output */
7373         (void)printf ("%s", prompt);                    /* display prompt */
7374         (void)fflush (stdout);                          /* flush output */
7375         cptr = fgets (cptr, size, stream);              /* get cmd line */
7376         }
7377 #endif /* if defined(HAVE_LINEHISTORY) */
7378 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
7379 
7380 if (cptr == NULL) {
7381     clearerr (stream);                                  /* clear error */
7382     return NULL;                                        /* ignore EOF */
7383     }
7384 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
7385     if ((*tptr == '\n') || (*tptr == '\r') ||
7386         (tptr == (cptr + size - 1))) {                  /* str max length? */
7387         *tptr = 0;                                      /* terminate */
7388         break;
7389         }
7390     }
7391 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
7392     memmove (cptr, cptr + 3, strlen (cptr + 3));
7393 while (sim_isspace (*cptr))                             /* trim leading spc */
7394     cptr++;
7395 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
7396     if (sim_do_echo)                                    /* echo comments if -v */
7397         sim_printf("%s> %s\n", do_position(), cptr);
7398     *cptr = 0;
7399     }
7400 
7401 return cptr;
7402 }
7403 
7404 /* get_glyph            get next glyph (force upper case)
7405    get_glyph_nc         get next glyph (no conversion)
7406    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
7407    get_glyph_cmd        get command glyph (force upper case, extract leading !)
7408    get_glyph_gen        get next glyph (general case)
7409 
7410    Inputs:
7411         iptr        =   pointer to input string
7412         optr        =   pointer to output string
7413         mchar       =   optional end of glyph character
7414         uc          =   TRUE for convert to upper case (_gen only)
7415         quote       =   TRUE to allow quote enclosing values (_gen only)
7416         escape_char =   optional escape character within quoted strings (_gen only)
7417 
7418    Outputs
7419         result      =   pointer to next character in input string
7420 */
7421 
7422 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] */
7423 {
7424 t_bool quoting = FALSE;
7425 t_bool escaping = FALSE;
7426 char quote_char = 0;
7427 
7428 while ((*iptr != 0) &&
7429        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
7430     if (quote) {
7431         if (quoting) {
7432             if (!escaping) {
7433                 if (*iptr == escape_char)
7434                     escaping = TRUE;
7435                 else
7436                     if (*iptr == quote_char)
7437                         quoting = FALSE;
7438                 }
7439             else
7440                 escaping = FALSE;
7441             }
7442         else {
7443             if ((*iptr == '"') || (*iptr == '\'')) {
7444                 quoting = TRUE;
7445                 quote_char = *iptr;
7446                 }
7447             }
7448         }
7449     if (sim_islower (*iptr) && uc)
7450         *optr = (char)toupper (*iptr);
7451     else *optr = *iptr;
7452     iptr++; optr++;
7453     }
7454 if (mchar && (*iptr == mchar))              /* skip input terminator */
7455     iptr++;
7456 *optr = 0;                                  /* terminate result string */
7457 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
7458     iptr++;
7459 return iptr;
7460 }
7461 
7462 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7463 {
7464 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7465 }
7466 
7467 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7468 {
7469 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7470 }
7471 
7472 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7473 {
7474 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7475 }
7476 
7477 CONST char *get_glyph_cmd (const char *iptr, char *optr)
     /* [previous][next][first][last][top][bottom][index][help] */
7478 {
7479 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7480 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7481     strcpy (optr, "!");                     /* return ! as command glyph */
7482     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
7483     }
7484 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7485 }
7486 
7487 /* Trim trailing spaces from a string
7488 
7489     Inputs:
7490         cptr    =       pointer to string
7491     Outputs:
7492         cptr    =       pointer to string
7493 */
7494 
7495 char *sim_trim_endspc (char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7496 {
7497 char *tptr;
7498 
7499 tptr = cptr + strlen (cptr);
7500 while ((--tptr >= cptr) && sim_isspace (*tptr))
7501     *tptr = 0;
7502 return cptr;
7503 }
7504 
7505 int sim_isspace (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7506 {
7507 return (c & 0x80) ? 0 : isspace (c);
7508 }
7509 
7510 int sim_islower (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7511 {
7512 return (c & 0x80) ? 0 : islower (c);
7513 }
7514 
7515 int sim_isalpha (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7516 {
7517 return (c & 0x80) ? 0 : isalpha (c);
7518 }
7519 
7520 int sim_isprint (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7521 {
7522 return (c & 0x80) ? 0 : isprint (c);
7523 }
7524 
7525 int sim_isdigit (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7526 {
7527 return (c & 0x80) ? 0 : isdigit (c);
7528 }
7529 
7530 int sim_isgraph (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7531 {
7532 return (c & 0x80) ? 0 : isgraph (c);
7533 }
7534 
7535 int sim_isalnum (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7536 {
7537 return (c & 0x80) ? 0 : isalnum (c);
7538 }
7539 
7540 /* get_uint             unsigned number
7541 
7542    Inputs:
7543         cptr    =       pointer to input string
7544         radix   =       input radix
7545         max     =       maximum acceptable value
7546         *status =       pointer to error status
7547    Outputs:
7548         val     =       value
7549 */
7550 
7551 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
7552 {
7553 t_value val;
7554 CONST char *tptr;
7555 
7556 *status = SCPE_OK;
7557 val = strtotv ((CONST char *)cptr, &tptr, radix);
7558 if ((cptr == tptr) || (val > max))
7559     *status = SCPE_ARG;
7560 else {
7561     while (sim_isspace (*tptr)) tptr++;
7562     if (*tptr != 0)
7563         *status = SCPE_ARG;
7564     }
7565 return val;
7566 }
7567 
7568 /* get_range            range specification
7569 
7570    Inputs:
7571         dptr    =       pointer to device (NULL if none)
7572         cptr    =       pointer to input string
7573         *lo     =       pointer to low result
7574         *hi     =       pointer to high result
7575         aradix  =       radix
7576         max     =       default high value
7577         term    =       terminating character, 0 if none
7578    Outputs:
7579         tptr    =       input pointer after processing
7580                         NULL if error
7581 */
7582 
7583 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
     /* [previous][next][first][last][top][bottom][index][help] */
7584     uint32 rdx, t_addr max, char term)
7585 {
7586 CONST char *tptr;
7587 
7588 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
7589     tptr = cptr + strlen ("ALL");
7590     *lo = 0;
7591     *hi = max;
7592     }
7593 else {
7594     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
7595         ((cptr[1] == '\0') ||
7596          (cptr[1] == '-')  ||
7597          (cptr[1] == ':')  ||
7598          (cptr[1] == '/'))) {
7599         tptr = cptr + strlen (".");
7600         *lo = *hi = sim_last_addr;
7601         }
7602     else {
7603         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
7604             tptr = cptr + strlen ("$");
7605             *hi = *lo = (t_addr)sim_last_val;
7606             }
7607         else {
7608             if (dptr && sim_vm_parse_addr)                      /* get low */
7609                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7610             else
7611                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7612             if (cptr == tptr)                                   /* error? */
7613                     return NULL;
7614             }
7615         }
7616     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
7617         cptr = tptr + 1;
7618         if (dptr && sim_vm_parse_addr)                  /* get high */
7619             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7620         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7621         if (cptr == tptr)
7622             return NULL;
7623         if (*lo > *hi)
7624             return NULL;
7625         }
7626     else if (*tptr == '/') {                            /* relative? */
7627         cptr = tptr + 1;
7628         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7629         if ((cptr == tptr) || (*hi == 0))
7630             return NULL;
7631         *hi = *lo + *hi - 1;
7632         }
7633     else *hi = *lo;
7634     }
7635 sim_last_addr = *hi;
7636 if (term && (*tptr++ != term))
7637     return NULL;
7638 return tptr;
7639 }
7640 
7641 /* sim_decode_quoted_string
7642 
7643    Inputs:
7644         iptr        =   pointer to input string
7645         optr        =   pointer to output buffer
7646                         the output buffer must be allocated by the caller
7647                         and to avoid overrunat it must be at least as big
7648                         as the input string.
7649 
7650    Outputs
7651         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7652         osize       =   size of the data in the optr buffer
7653 
7654    The input string must be quoted.  Quotes may be either single or
7655    double but the opening anc closing quote characters must match.
7656    Within quotes C style character escapes are allowed.
7657 */
7658 
7659 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
     /* [previous][next][first][last][top][bottom][index][help] */
7660 {
7661 char quote_char;
7662 uint8 *ostart = optr;
7663 
7664 *osize = 0;
7665 if ((strlen(iptr) == 1) ||
7666     (iptr[0] != iptr[strlen(iptr)-1]) ||
7667     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7668     return SCPE_ARG;            /* String must be quote delimited */
7669 quote_char = *iptr++;           /* Save quote character */
7670 while (iptr[1]) {               /* Skip trailing quote */
7671     if (*iptr != '\\') {
7672         if (*iptr == quote_char)
7673             return SCPE_ARG;    /* Embedded quotes must be escaped */
7674         *(optr++) = (uint8)(*(iptr++));
7675         continue;
7676         }
7677     ++iptr; /* Skip backslash */
7678     switch (*iptr) {
7679         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7680             *(optr++) = 13; ++iptr;
7681             break;
7682         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7683             *(optr++) = 10; ++iptr;
7684             break;
7685         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7686             *(optr++) = 12; ++iptr;
7687             break;
7688         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7689             *(optr++) = 9; ++iptr;
7690             break;
7691         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7692             *(optr++) = 11; ++iptr;
7693             break;
7694         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7695             *(optr++) = 8; ++iptr;
7696             break;
7697         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7698             *(optr++) = 92; ++iptr;
7699             break;
7700         case 'e':   /* ASCII Escape character (Decimal value 27) */
7701             *(optr++) = 27; ++iptr;
7702             break;
7703         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7704             *(optr++) = 39; ++iptr;
7705             break;
7706         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7707             *(optr++) = 34; ++iptr;
7708             break;
7709         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7710             *(optr++) = 63; ++iptr;
7711             break;
7712         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7713             *optr = *(iptr++) - '0';
7714             if ((*iptr >= '0') && (*iptr <= '7'))
7715                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7716             if ((*iptr >= '0') && (*iptr <= '7'))
7717                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7718             ++optr;
7719             break;
7720         case 'x':
7721             if (1) {
7722                 static const char *hex_digits = "0123456789ABCDEF";
7723                 const char *c;
7724 
7725                 ++iptr;
7726                 *optr = 0;
7727                 c = strchr (hex_digits, toupper(*iptr));
7728                 if (c) {
7729                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7730                     ++iptr;
7731                     }
7732                 c = strchr (hex_digits, toupper(*iptr));
7733                 if (c) {
7734                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7735                     ++iptr;
7736                     }
7737                 ++optr;
7738                 }
7739             break;
7740         default:
7741             return SCPE_ARG;    /* Invalid escape */
7742         }
7743     }
7744 *optr = '\0';
7745 *osize = (uint32)(optr-ostart);
7746 return SCPE_OK;
7747 }
7748 
7749 /* sim_encode_quoted_string
7750 
7751    Inputs:
7752         iptr        =   pointer to input buffer
7753         size        =   number of bytes of data in the buffer
7754 
7755    Outputs
7756         optr        =   pointer to output buffer
7757                         the output buffer must be freed by the caller
7758 
7759    The input data will be encoded into a simply printable form.
7760 */
7761 
7762 char *sim_encode_quoted_string (const uint8 *iptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
7763 {
7764 size_t i;
7765 t_bool double_quote_found = FALSE;
7766 t_bool single_quote_found = FALSE;
7767 char quote = '"';
7768 char *tptr, *optr;
7769 
7770 optr = (char *)malloc (4*size + 3);
7771 if (optr == NULL)
7772     return NULL;
7773 tptr = optr;
7774 for (i=0; i<size; i++)
7775     switch ((char)iptr[i]) {
7776         case '"':
7777             double_quote_found = TRUE;
7778             break;
7779         case '\'':
7780             single_quote_found = TRUE;
7781             break;
7782         }
7783 if (double_quote_found && (!single_quote_found))
7784     quote = '\'';
7785 *tptr++ = quote;
7786 while (size--) {
7787     switch (*iptr) {
7788         case '\r':
7789             *tptr++ = '\\'; *tptr++ = 'r'; break;
7790         case '\n':
7791             *tptr++ = '\\'; *tptr++ = 'n'; break;
7792         case '\f':
7793             *tptr++ = '\\'; *tptr++ = 'f'; break;
7794         case '\t':
7795             *tptr++ = '\\'; *tptr++ = 't'; break;
7796         case '\v':
7797             *tptr++ = '\\'; *tptr++ = 'v'; break;
7798         case '\b':
7799             *tptr++ = '\\'; *tptr++ = 'b'; break;
7800         case '\\':
7801             *tptr++ = '\\'; *tptr++ = '\\'; break;
7802         case '"':
7803         case '\'':
7804             if (quote == *iptr)
7805                 *tptr++ = '\\';
7806         /*FALLTHRU*/ /* fall through */ /* fallthrough */
7807         default:
7808             if (sim_isprint (*iptr))
7809                 *tptr++ = *iptr;
7810             else {
7811                 (void)sprintf (tptr, "\\%03o", *iptr);
7812                 tptr += 4;
7813                 }
7814             break;
7815         }
7816     ++iptr;
7817     }
7818 *tptr++ = quote;
7819 *tptr++ = '\0';
7820 return optr;
7821 }
7822 
7823 void fprint_buffer_string (FILE *st, const uint8 *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
7824 {
7825 char *string;
7826 
7827 string = sim_encode_quoted_string (buf, size);
7828 (void)fprintf (st, "%s", string);
7829 FREE (string);
7830 }
7831 
7832 /* Find_device          find device matching input string
7833 
7834    Inputs:
7835         cptr    =       pointer to input string
7836    Outputs:
7837         result  =       pointer to device
7838 */
7839 
7840 DEVICE *find_dev (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7841 {
7842 int32 i;
7843 DEVICE *dptr;
7844 
7845 if (cptr == NULL)
7846     return NULL;
7847 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7848     if ((strcmp (cptr, dptr->name) == 0) ||
7849         (dptr->lname &&
7850         (strcmp (cptr, dptr->lname) == 0)))
7851         return dptr;
7852     }
7853 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7854     if ((strcmp (cptr, dptr->name) == 0) ||
7855         (dptr->lname &&
7856         (strcmp (cptr, dptr->lname) == 0)))
7857         return dptr;
7858     }
7859 return NULL;
7860 }
7861 
7862 /* Find_unit            find unit matching input string
7863 
7864    Inputs:
7865         cptr    =       pointer to input string
7866         uptr    =       pointer to unit pointer
7867    Outputs:
7868         result  =       pointer to device (null if no dev)
7869         *iptr   =       pointer to unit (null if nx unit)
7870 
7871 */
7872 
7873 DEVICE *find_unit (const char *cptr, UNIT **uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7874 {
7875 uint32 i, u;
7876 const char *nptr;
7877 const char *tptr;
7878 t_stat r;
7879 DEVICE *dptr;
7880 
7881 if (uptr == NULL)                                       /* arg error? */
7882     return NULL;
7883 *uptr = NULL;
7884 if ((dptr = find_dev (cptr))) {                         /* exact match? */
7885     if (qdisable (dptr))                                /* disabled? */
7886         return NULL;
7887     *uptr = dptr->units;                                /* unit 0 */
7888     return dptr;
7889     }
7890 
7891 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
7892     if (qdisable (dptr))                                /* device disabled? */
7893         continue;
7894     if (dptr->numunits && /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/ /* any units? */
7895         (((nptr = dptr->name) &&
7896           (strncmp (cptr, nptr, strlen (nptr)) == 0)) || /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7897          ((nptr = dptr->lname) &&
7898           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7899         tptr = cptr + strlen (nptr);
7900         if (sim_isdigit (*tptr)) {
7901             if (qdisable (dptr))                        /* disabled? */
7902                 return NULL;
7903             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7904             if (r != SCPE_OK)                           /* error? */
7905                 *uptr = NULL;
7906             else
7907                 *uptr = dptr->units + u;
7908             return dptr;
7909             }
7910         }
7911     for (u = 0; u < dptr->numunits; u++) {
7912         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7913             *uptr = &dptr->units[u];
7914             return dptr;
7915             }
7916         }
7917     }
7918 return NULL;
7919 }
7920 
7921 /* sim_register_internal_device   Add device to internal device list
7922 
7923    Inputs:
7924         dptr    =       pointer to device
7925 */
7926 
7927 t_stat sim_register_internal_device (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7928 {
7929 uint32 i;
7930 
7931 for (i = 0; (sim_devices[i] != NULL); i++)
7932     if (sim_devices[i] == dptr)
7933         return SCPE_OK;
7934 for (i = 0; i < sim_internal_device_count; i++)
7935     if (sim_internal_devices[i] == dptr)
7936         return SCPE_OK;
7937 ++sim_internal_device_count;
7938 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7939 if (!sim_internal_devices)
7940   {
7941     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7942                    __func__, __FILE__, __LINE__);
7943 #if defined(USE_BACKTRACE)
7944 # if defined(SIGUSR2)
7945     (void)raise(SIGUSR2);
7946     /*NOTREACHED*/ /* unreachable */
7947 # endif /* if defined(SIGUSR2) */
7948 #endif /* if defined(USE_BACKTRACE) */
7949     abort();
7950   }
7951 sim_internal_devices[sim_internal_device_count-1] = dptr;
7952 sim_internal_devices[sim_internal_device_count] = NULL;
7953 return SCPE_OK;
7954 }
7955 
7956 /* Find_dev_from_unit   find device for unit
7957 
7958    Inputs:
7959         uptr    =       pointer to unit
7960    Outputs:
7961         result  =       pointer to device
7962 */
7963 
7964 DEVICE *find_dev_from_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7965 {
7966 DEVICE *dptr;
7967 uint32 i, j;
7968 
7969 if (uptr == NULL)
7970     return NULL;
7971 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7972     for (j = 0; j < dptr->numunits; j++) {
7973         if (uptr == (dptr->units + j))
7974             return dptr;
7975         }
7976     }
7977 for (i = 0; i<sim_internal_device_count; i++) {
7978     dptr = sim_internal_devices[i];
7979     for (j = 0; j < dptr->numunits; j++) {
7980         if (uptr == (dptr->units + j))
7981             return dptr;
7982         }
7983     }
7984 return NULL;
7985 }
7986 
7987 /* Test for disabled device */
7988 
7989 t_bool qdisable (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7990 {
7991 return (dptr->flags & DEV_DIS? TRUE: FALSE);
7992 }
7993 
7994 /* find_reg_glob        find globally unique register
7995 
7996    Inputs:
7997         cptr    =       pointer to input string
7998         optr    =       pointer to output pointer (can be null)
7999         gdptr   =       pointer to global device
8000    Outputs:
8001         result  =       pointer to register, NULL if error
8002         *optr   =       pointer to next character in input string
8003         *gdptr  =       pointer to device where found
8004 */
8005 
8006 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8007 {
8008 int32 i;
8009 DEVICE *dptr;
8010 REG *rptr, *srptr = NULL;
8011 
8012 *gdptr = NULL;
8013 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
8014     if (dptr->flags & DEV_DIS)                          /* skip disabled */
8015         continue;
8016     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
8017         if (srptr)                                      /* ambig? err */
8018             return NULL;
8019         srptr = rptr;                                   /* save reg */
8020         *gdptr = dptr;                                  /* save unit */
8021         }
8022     }
8023 return srptr;
8024 }
8025 
8026 /* find_reg             find register matching input string
8027 
8028    Inputs:
8029         cptr    =       pointer to input string
8030         optr    =       pointer to output pointer (can be null)
8031         dptr    =       pointer to device
8032    Outputs:
8033         result  =       pointer to register, NULL if error
8034         *optr   =       pointer to next character in input string
8035 */
8036 
8037 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8038 {
8039 CONST char *tptr;
8040 REG *rptr;
8041 size_t slnt;
8042 
8043 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
8044     return NULL;
8045 tptr = cptr;
8046 do {
8047     tptr++;
8048     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
8049 slnt = tptr - cptr;
8050 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
8051     if ((slnt == strlen (rptr->name)) &&
8052         (strncmp (cptr, rptr->name, slnt) == 0)) {
8053         if (optr != NULL)
8054             *optr = tptr;
8055         return rptr;
8056         }
8057     }
8058 return NULL;
8059 }
8060 
8061 /* get_switches         get switches from input string
8062 
8063    Inputs:
8064         cptr    =       pointer to input string
8065    Outputs:
8066         sw      =       switch bit mask
8067                         0 if no switches, -1 if error
8068 */
8069 
8070 int32 get_switches (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8071 {
8072 int32 sw;
8073 
8074 if (*cptr != '-')
8075     return 0;
8076 sw = 0;
8077 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
8078     if (sim_isalpha (*cptr) == 0)
8079         return -1;
8080     sw = sw | SWMASK (toupper (*cptr));
8081     }
8082 return sw;
8083 }
8084 
8085 /* get_sim_sw           accumulate sim_switches
8086 
8087    Inputs:
8088         cptr    =       pointer to input string
8089    Outputs:
8090         ptr     =       pointer to first non-string glyph
8091                         NULL if error
8092 */
8093 
8094 CONST char *get_sim_sw (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8095 {
8096 int32 lsw;
8097 char gbuf[CBUFSIZE];
8098 
8099 while (*cptr == '-') {                                  /* while switches */
8100     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
8101     lsw = get_switches (gbuf);                          /* parse */
8102     if (lsw <= 0)                                       /* invalid? */
8103         return NULL;
8104     sim_switches = sim_switches | lsw;                  /* accumulate */
8105     }
8106 return cptr;
8107 }
8108 
8109 /* get_sim_opt          get simulator command options
8110 
8111    Inputs:
8112         opt     =       command options
8113         cptr    =       pointer to input string
8114    Outputs:
8115         ptr     =       pointer to next glyph, NULL if error
8116         *stat   =       error status
8117 */
8118 
8119 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
     /* [previous][next][first][last][top][bottom][index][help] */
8120 {
8121 int32 t;
8122 char gbuf[CBUFSIZE];
8123 CONST char *svptr;
8124 DEVICE *tdptr;
8125 UNIT *tuptr;
8126 
8127 sim_switches = 0;                                       /* no switches */
8128 sim_ofile = NULL;                                       /* no output file */
8129 sim_schrptr = NULL;                                     /* no search */
8130 sim_schaptr = NULL;                                     /* no search */
8131 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
8132 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
8133 sim_stabr.count = 1;
8134 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
8135 if (!sim_stabr.mask)
8136   {
8137     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8138                    __func__, __FILE__, __LINE__);
8139 #if defined(USE_BACKTRACE)
8140 # if defined(SIGUSR2)
8141     (void)raise(SIGUSR2);
8142     /*NOTREACHED*/ /* unreachable */
8143 # endif /* if defined(SIGUSR2) */
8144 #endif /* if defined(USE_BACKTRACE) */
8145     abort();
8146   }
8147 (void)memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
8148 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
8149 if (!sim_stabr.comp)
8150   {
8151     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8152                    __func__, __FILE__, __LINE__);
8153 #if defined(USE_BACKTRACE)
8154 # if defined(SIGUSR2)
8155     (void)raise(SIGUSR2);
8156     /*NOTREACHED*/ /* unreachable */
8157 # endif /* if defined(SIGUSR2) */
8158 #endif /* if defined(USE_BACKTRACE) */
8159     abort();
8160   }
8161 (void)memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
8162 sim_staba.count = sim_emax;
8163 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
8164 if (!sim_staba.mask)
8165   {
8166     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8167                    __func__, __FILE__, __LINE__);
8168 #if defined(USE_BACKTRACE)
8169 # if defined(SIGUSR2)
8170     (void)raise(SIGUSR2);
8171     /*NOTREACHED*/ /* unreachable */
8172 # endif /* if defined(SIGUSR2) */
8173 #endif /* if defined(USE_BACKTRACE) */
8174     abort();
8175   }
8176 (void)memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
8177 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
8178 if (!sim_staba.comp)
8179   {
8180     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8181                    __func__, __FILE__, __LINE__);
8182 #if defined(USE_BACKTRACE)
8183 # if defined(SIGUSR2)
8184     (void)raise(SIGUSR2);
8185     /*NOTREACHED*/ /* unreachable */
8186 # endif /* if defined(SIGUSR2) */
8187 #endif /* if defined(USE_BACKTRACE) */
8188     abort();
8189   }
8190 (void)memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
8191 if (! sim_dflt_dev)
8192   return NULL;
8193 sim_dfdev = sim_dflt_dev;
8194 sim_dfunit = sim_dfdev->units;
8195 sim_opt_out = 0;                                        /* no options yet */
8196 *st = SCPE_OK;
8197 while (*cptr) {                                         /* loop through modifiers */
8198     svptr = cptr;                                       /* save current position */
8199     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
8200         if (sim_ofile) {                                /* already got one? */
8201             fclose (sim_ofile);                         /* one per customer */
8202             *st = SCPE_ARG;
8203             return NULL;
8204             }
8205         cptr = get_glyph (cptr + 1, gbuf, 0);
8206         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
8207         if (sim_ofile == NULL) {                        /* open failed? */
8208             *st = SCPE_OPENERR;
8209             return NULL;
8210             }
8211         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
8212         continue;
8213         }
8214     cptr = get_glyph (cptr, gbuf, 0);
8215     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
8216         if (t < 0) {                                    /* err if bad switch */
8217             *st = SCPE_INVSW;
8218             return NULL;
8219             }
8220         sim_switches = sim_switches | t;                /* or in new switches */
8221         }
8222     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
8223         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
8224         sim_schrptr = &sim_stabr;                       /* set search */
8225         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
8226         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
8227         }
8228     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
8229         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
8230         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
8231         (tuptr != NULL)) {
8232         sim_dfdev = tdptr;                              /* set as default */
8233         sim_dfunit = tuptr;
8234         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
8235         }
8236     else return svptr;                                  /* not rec, break out */
8237     }
8238 return cptr;
8239 }
8240 
8241 /* put_switches         put switches into string
8242 
8243    Inputs:
8244         buf     =       pointer to string buffer
8245         bufsize =       size of string buffer
8246         sw      =       switch bit mask
8247    Outputs:
8248         buf     =       buffer with switches converted to text
8249 */
8250 
8251 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8252 {
8253 char *optr = buf;
8254 int32 bit;
8255 
8256 (void)memset (buf, 0, bufsize);
8257 if ((sw == 0) || (bufsize < 3))
8258     return buf;
8259 --bufsize;                          /* leave room for terminating NUL */
8260 *optr++ = '-';
8261 for (bit=0; bit <= ('Z'-'A'); bit++)
8262     if (sw & (1 << bit))
8263         if ((size_t)(optr - buf) < bufsize)
8264             *optr++ = 'A' + bit;
8265 return buf;
8266 }
8267 
8268 /* Get register search specification
8269 
8270    Inputs:
8271         cptr    =       pointer to input string
8272         radix   =       radix for numbers
8273         schptr =        pointer to search table
8274    Outputs:
8275         return =        NULL if error
8276                         schptr if valid search specification
8277 */
8278 
8279 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8280 {
8281 int32 c;
8282 size_t logop, cmpop;
8283 t_value logval, cmpval;
8284 const char *sptr;
8285 CONST char *tptr;
8286 const char logstr[] = "|&^", cmpstr[] = "=!><";
8287 /* Using a const instead of a #define. */
8288 const size_t invalid_op = (size_t) -1;
8289 
8290 logval = cmpval = 0;
8291 if (*cptr == 0)                                         /* check for clause */
8292     return NULL;
8293 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8294 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8295     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
8296         logop = sptr - logstr;
8297         logval = strtotv (cptr, &tptr, radix);
8298         if (cptr == tptr)
8299             return NULL;
8300         cptr = tptr;
8301         }
8302     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
8303         cmpop = sptr - cmpstr;
8304         if (*cptr == '=') {
8305             cmpop = cmpop + strlen (cmpstr);
8306             cptr++;
8307             }
8308         cmpval = strtotv (cptr, &tptr, radix);
8309         if (cptr == tptr)
8310             return NULL;
8311         cptr = tptr;
8312         }
8313     else return NULL;
8314     }                                                   /* end for */
8315 if (schptr->count != 1) {
8316     FREE (schptr->mask);
8317     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8318     FREE (schptr->comp);
8319     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8320     }
8321 if (logop != invalid_op) {
8322     schptr->logic = logop;
8323     schptr->mask[0] = logval;
8324     }
8325 if (cmpop != invalid_op) {
8326     schptr->boolop = cmpop;
8327     schptr->comp[0] = cmpval;
8328     }
8329 schptr->count = 1;
8330 return schptr;
8331 }
8332 
8333 /* Get memory search specification
8334 
8335    Inputs:
8336         cptr    =       pointer to input string
8337         radix   =       radix for numbers
8338         schptr =        pointer to search table
8339    Outputs:
8340         return =        NULL if error
8341                         schptr if valid search specification
8342 */
8343 
8344 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8345 {
8346 int32 c;
8347 size_t logop, cmpop;
8348 t_value *logval, *cmpval;
8349 t_stat reason = SCPE_OK;
8350 CONST char *ocptr = cptr;
8351 const char *sptr;
8352 char gbuf[CBUFSIZE];
8353 const char logstr[] = "|&^", cmpstr[] = "=!><";
8354 /* Using a const instead of a #define. */
8355 const size_t invalid_op = (size_t) -1;
8356 
8357 if (*cptr == 0)                                         /* check for clause */
8358     return NULL;
8359 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
8360 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
8361 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
8362 for (logop = cmpop = invalid_op; (c = *cptr++); ) {     /* loop thru clauses */
8363     if (NULL != (sptr = strchr (logstr, c))) {          /* check for mask */
8364         logop = sptr - logstr;
8365         cptr = get_glyph (cptr, gbuf, 0);
8366         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
8367         if (reason > 0) {
8368             FREE (logval);
8369             FREE (cmpval);
8370             return get_rsearch (ocptr, radix, schptr);
8371             }
8372         }
8373     else if (NULL != (sptr = strchr (cmpstr, c))) {     /* check for boolop */
8374         cmpop = sptr - cmpstr;
8375         if (*cptr == '=') {
8376             cmpop = cmpop + strlen (cmpstr);
8377             cptr++;
8378             }
8379         cptr = get_glyph (cptr, gbuf, 0);
8380         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
8381         if (reason > 0) {
8382             FREE (logval);
8383             FREE (cmpval);
8384             return get_rsearch (ocptr, radix, schptr);
8385             }
8386         }
8387     else {
8388         FREE (logval);
8389         FREE (cmpval);
8390         return NULL;
8391         }
8392     }                                                   /* end for */
8393 if (schptr->count != (uint32)(1 - reason)) {
8394     schptr->count = 1 - reason;
8395     FREE (schptr->mask);
8396     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
8397     FREE (schptr->comp);
8398     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
8399     }
8400 if (logop != invalid_op) {
8401     schptr->logic = logop;
8402     FREE (schptr->mask);
8403     schptr->mask = logval;
8404     }
8405 else {
8406     FREE (logval);
8407     }
8408 if (cmpop != invalid_op) {
8409     schptr->boolop = cmpop;
8410     FREE (schptr->comp);
8411     schptr->comp = cmpval;
8412     }
8413 else {
8414     FREE (cmpval);
8415     }
8416 return schptr;
8417 }
8418 
8419 /* Test value against search specification
8420 
8421    Inputs:
8422         val    =        value list to test
8423         schptr =        pointer to search table
8424    Outputs:
8425         return =        1 if value passes search criteria, 0 if not
8426 */
8427 
8428 int32 test_search (t_value *values, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8429 {
8430 t_value *val = NULL;
8431 int32 i, updown;
8432 int32 ret = 0;
8433 
8434 if (schptr == NULL)
8435     return ret;
8436 
8437 val = (t_value *)malloc (schptr->count * sizeof (*values));
8438 if (!val)
8439   {
8440     (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8441                   __func__, __FILE__, __LINE__);
8442 #if defined(USE_BACKTRACE)
8443 # if defined(SIGUSR2)
8444     (void)raise(SIGUSR2);
8445     /*NOTREACHED*/ /* unreachable */
8446 # endif /* if defined(SIGUSR2) */
8447 #endif /* if defined(USE_BACKTRACE) */
8448     abort();
8449   }
8450 for (i=0; i<(int32)schptr->count; i++) {
8451     val[i] = values[i];
8452     switch (schptr->logic) {                            /* case on logical */
8453 
8454         case SCH_OR:
8455             val[i] = val[i] | schptr->mask[i];
8456             break;
8457 
8458         case SCH_AND:
8459             val[i] = val[i] & schptr->mask[i];
8460             break;
8461 
8462         case SCH_XOR:
8463             val[i] = val[i] ^ schptr->mask[i];
8464             break;
8465             }
8466     }
8467 
8468 ret = 1;
8469 if (1) {    /* Little Endian VM */
8470     updown = -1;
8471     i=schptr->count-1;
8472     }
8473 else {      /* Big Endian VM */
8474     updown = 1;
8475     i=0;
8476     }
8477 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
8478     switch (schptr->boolop) {                           /* case on comparison */
8479 
8480         case SCH_E: case SCH_EE:
8481             if (val[i] != schptr->comp[i])
8482                 ret = 0;
8483             break;
8484 
8485         case SCH_N: case SCH_NE:
8486             if (val[i] == schptr->comp[i])
8487                 ret = 0;
8488             break;
8489 
8490         case SCH_G:
8491             if (val[i] <= schptr->comp[i])
8492                 ret = 0;
8493             break;
8494 
8495         case SCH_GE:
8496             if (val[i] < schptr->comp[i])
8497                 ret = 0;
8498             break;
8499 
8500         case SCH_L:
8501             if (val[i] >= schptr->comp[i])
8502                 ret = 0;
8503             break;
8504 
8505         case SCH_LE:
8506             if (val[i] > schptr->comp[i])
8507                 ret = 0;
8508             break;
8509         }
8510     }
8511 FREE (val);
8512 return ret;
8513 }
8514 
8515 /* Radix independent input/output package
8516 
8517    strtotv - general radix input routine
8518 
8519    Inputs:
8520         inptr   =       string to convert
8521         endptr  =       pointer to first unconverted character
8522         radix   =       radix for input
8523    Outputs:
8524         value   =       converted value
8525 
8526    On an error, the endptr will equal the inptr.
8527 */
8528 
8529 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
     /* [previous][next][first][last][top][bottom][index][help] */
8530 {
8531 int32 nodigit;
8532 t_value val;
8533 uint32 c, digit;
8534 
8535 *endptr = inptr;                                        /* assume fails */
8536 if ((radix < 2) || (radix > 36))
8537     return 0;
8538 while (sim_isspace (*inptr))                            /* bypass white space */
8539     inptr++;
8540 val = 0;
8541 nodigit = 1;
8542 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
8543     if (sim_islower (c))
8544         c = toupper (c);
8545     if (sim_isdigit (c))                                /* digit? */
8546         digit = c - (uint32) '0';
8547     else if (radix <= 10)                               /* stop if not expected */
8548         break;
8549     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
8550     if (digit >= radix)                                 /* valid in radix? */
8551         return 0;
8552     val = (val * radix) + digit;                        /* add to value */
8553     nodigit = 0;
8554     }
8555 if (nodigit)                                            /* no digits? */
8556     return 0;
8557 *endptr = inptr;                                        /* result pointer */
8558 return val;
8559 }
8560 
8561 /* fprint_val - general radix printing routine
8562 
8563    Inputs:
8564         stream  =       stream designator
8565         val     =       value to print
8566         radix   =       radix to print
8567         width   =       width to print
8568         format  =       leading zeroes format
8569    Outputs:
8570         status  =       error status
8571         if stream is NULL, returns length of output that would
8572         have been generated.
8573 */
8574 
8575 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8576                    size_t width, uint32 format)
8577 {
8578 #define MAX_WIDTH ((CHAR_BIT * sizeof (t_value) * 4 + 3) / 3)
8579 t_value owtest, wtest;
8580 size_t d;
8581 size_t digit;
8582 size_t ndigits;
8583 size_t commas = 0;
8584 char dbuf[MAX_WIDTH + 1];
8585 
8586 for (d = 0; d < MAX_WIDTH; d++)
8587     dbuf[d] = (format == PV_RZRO)? '0': ' ';
8588 dbuf[MAX_WIDTH] = 0;
8589 d = MAX_WIDTH;
8590 do {
8591     d = d - 1;
8592     digit = val % radix;
8593     val = val / radix;
8594     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8595     } while ((d > 0) && (val != 0));
8596 
8597 switch (format) {
8598     case PV_LEFT:
8599         break;
8600     case PV_RZRO:
8601         wtest = owtest = radix;
8602         ndigits = 1;
8603         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8604             owtest = wtest;
8605             wtest = wtest * radix;
8606             ndigits = ndigits + 1;
8607             }
8608         if ((MAX_WIDTH - (ndigits + commas)) < d)
8609             d = MAX_WIDTH - (ndigits + commas);
8610         break;
8611     }
8612 if (NULL == buffer)                   /* Potentially unsafe wraparound if */
8613     return ((t_stat) strlen(dbuf+d)); /*  sizeof(t_stat) < sizeof(size_t) */
8614 *buffer = '\0';
8615 if (width < strlen(dbuf+d))
8616     return SCPE_IOERR;
8617 strcpy(buffer, dbuf+d);
8618 return SCPE_OK;
8619 }
8620 
8621 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8622     uint32 width, uint32 format)
8623 {
8624 char dbuf[MAX_WIDTH + 1];
8625 
8626 if (!stream)
8627     return sprint_val (NULL, val, radix, width, format);
8628 if (width > MAX_WIDTH)
8629     width = MAX_WIDTH;
8630 sprint_val (dbuf, val, radix, width, format);
8631 if (Fprintf (stream, "%s", dbuf) < 0)
8632     return SCPE_IOERR;
8633 return SCPE_OK;
8634 }
8635 
8636 const char *sim_fmt_secs (double seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
8637 {
8638 static char buf[60];
8639 char frac[16] = "";
8640 const char *sign = "";
8641 double val = seconds;
8642 double days, hours, mins, secs, msecs, usecs;
8643 
8644 if (val == 0.0)
8645     return "";
8646 if (val < 0.0) {
8647     sign = "-";
8648     val = -val;
8649     }
8650 days = floor (val / (24.0*60.0*60.0));
8651 val -= (days * 24.0*60.0*60.0);
8652 hours = floor (val / (60.0*60.0));
8653 val -= (hours * 60.0 * 60.0);
8654 mins = floor (val / 60.0);
8655 val -= (mins * 60.0);
8656 secs = floor (val);
8657 val -= secs;
8658 val *= 1000.0;
8659 msecs = floor (val);
8660 val -= msecs;
8661 val *= 1000.0;
8662 usecs = floor (val+0.5);
8663 if (usecs == 1000.0) {
8664     usecs = 0.0;
8665     msecs += 1;
8666     }
8667 if ((msecs > 0.0) || (usecs > 0.0)) {
8668     (void)sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8669     while (frac[strlen (frac) - 1] == '0') //-V557
8670         frac[strlen (frac) - 1] = '\0'; //-V557
8671     if (strlen (frac) == 1)
8672         frac[0] = '\0';
8673     }
8674 if (days > 0)
8675     (void)sprintf (buf, "%s%.0f day%s %02.0f:%02.0f:%02.0f%s hour%s",
8676                    sign, days, (days != 1) ? "s" : "", hours, mins,
8677                    secs, frac, (days == 1) ? "s" : "");
8678 else
8679     if (hours > 0)
8680         (void)sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour",
8681                        sign, hours, mins, secs, frac);
8682     else
8683         if (mins > 0)
8684             (void)sprintf (buf, "%s%.0f:%02.0f%s minute",
8685                            sign, mins, secs, frac);
8686         else
8687             if (secs > 0)
8688                 (void)sprintf (buf, "%s%.0f%s second",
8689                                sign, secs, frac);
8690             else
8691                 if (msecs > 0) {
8692                     if (usecs > 0)
8693                         (void)sprintf (buf, "%s%.0f.%s msec",
8694                                        sign, msecs, frac+4);
8695                     else
8696                         (void)sprintf (buf, "%s%.0f msec",
8697                                        sign, msecs);
8698                     }
8699                 else
8700                     (void)sprintf (buf, "%s%.0f usec",
8701                                    sign, usecs);
8702 if (0 != strncmp ("1 ", buf, 2))
8703     strcpy (&buf[strlen (buf)], "s");
8704 return buf;
8705 }
8706 
8707 /*
8708  * Event queue package
8709  *
8710  *      sim_activate            add entry to event queue
8711  *      sim_activate_abs        add entry to event queue even if event already scheduled
8712  *      sim_activate_after      add entry to event queue after a specified amount of wall time
8713  *      sim_cancel              remove entry from event queue
8714  *      sim_process_event       process entries on event queue
8715  *      sim_is_active           see if entry is on event queue
8716  *      sim_activate_time       return time until activation
8717  *      sim_atime               return absolute time for an entry
8718  *      sim_gtime               return global time
8719  *      sim_qcount              return event queue entry count
8720  */
8721 
8722 t_stat sim_process_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8723 {
8724 UNIT *uptr;
8725 t_stat reason;
8726 #if defined(TESTING)
8727 cpu_state_t * cpup = _cpup;
8728 #endif
8729 
8730 if (stop_cpu)                                           /* stop CPU? */
8731   {
8732 #if defined(WIN_STDIO)
8733     (void)fflush(stdout);
8734     (void)fflush(stderr);
8735 #endif /* if defined(WIN_STDIO) */
8736     return SCPE_STOP;
8737   }
8738 UPDATE_SIM_TIME;                                        /* update sim time */
8739 
8740 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8741     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8742     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8743                "Queue Empty New Interval = %d\n",
8744                sim_interval);
8745     return SCPE_OK;
8746     }
8747 sim_processing_event = TRUE;
8748 do {
8749     uptr = sim_clock_queue;                             /* get first */
8750     sim_clock_queue = uptr->next;                       /* remove first */
8751     uptr->next = NULL;                                  /* hygiene */
8752     sim_interval -= uptr->time;
8753     uptr->time = 0;
8754     if (sim_clock_queue != QUEUE_LIST_END)
8755         sim_interval = sim_clock_queue->time;
8756     else
8757         sim_interval = noqueue_time = NOQUEUE_WAIT;
8758     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8759                "Processing Event for %s\n", sim_uname (uptr));
8760     if (uptr->action != NULL)
8761         reason = uptr->action (uptr);
8762     else
8763         reason = SCPE_OK;
8764     } while ((reason == SCPE_OK) &&
8765              (sim_interval <= 0) &&
8766              (sim_clock_queue != QUEUE_LIST_END) &&
8767              (!stop_cpu));
8768 
8769 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8770     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8771     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8772                "Processing Queue Complete New Interval = %d\n",
8773                sim_interval);
8774     }
8775 else
8776     sim_debug (SIM_DBG_EVENT, sim_dflt_dev,
8777                "Processing Queue Complete New Interval = %d(%s)\n",
8778                sim_interval, sim_uname(sim_clock_queue));
8779 
8780 if ((reason == SCPE_OK) && stop_cpu)
8781   {
8782 #if defined(WIN_STDIO)
8783     (void)fflush(stdout);
8784     (void)fflush(stderr);
8785 #endif /* if defined(WIN_STDIO) */
8786     reason = SCPE_STOP;
8787   }
8788 sim_processing_event = FALSE;
8789 return reason;
8790 }
8791 
8792 /* sim_activate - activate (queue) event
8793 
8794    Inputs:
8795         uptr    =       pointer to unit
8796         event_time =    relative timeout
8797    Outputs:
8798         reason  =       result (SCPE_OK if ok)
8799 */
8800 
8801 t_stat sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8802 {
8803 if (uptr->dynflags & UNIT_TMR_UNIT)
8804     return sim_timer_activate (uptr, event_time);
8805 return _sim_activate (uptr, event_time);
8806 }
8807 
8808 t_stat _sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8809 {
8810 UNIT *cptr, *prvptr;
8811 int32 accum;
8812 #if defined(TESTING)
8813 cpu_state_t * cpup = _cpup;
8814 #endif
8815 
8816 if (sim_is_active (uptr))                               /* already active? */
8817     return SCPE_OK;
8818 UPDATE_SIM_TIME;                                        /* update sim time */
8819 
8820 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8821 
8822 prvptr = NULL;
8823 accum = 0;
8824 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8825     if (event_time < (accum + cptr->time))
8826         break;
8827     accum = accum + cptr->time;
8828     prvptr = cptr;
8829     }
8830 if (prvptr == NULL) {                                   /* insert at head */
8831     cptr = uptr->next = sim_clock_queue;
8832     sim_clock_queue = uptr;
8833     }
8834 else {
8835     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
8836     prvptr->next = uptr;
8837     }
8838 uptr->time = event_time - accum;
8839 if (cptr != QUEUE_LIST_END)
8840     cptr->time = cptr->time - uptr->time;
8841 sim_interval = sim_clock_queue->time;
8842 return SCPE_OK;
8843 }
8844 
8845 /* sim_activate_abs - activate (queue) event even if event already scheduled
8846 
8847    Inputs:
8848         uptr    =       pointer to unit
8849         event_time =    relative timeout
8850    Outputs:
8851         reason  =       result (SCPE_OK if ok)
8852 */
8853 
8854 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8855 {
8856 sim_cancel (uptr);
8857 return _sim_activate (uptr, event_time);
8858 }
8859 
8860 /* sim_activate_after - activate (queue) event
8861 
8862    Inputs:
8863         uptr    =       pointer to unit
8864         usec_delay =    relative timeout (in microseconds)
8865    Outputs:
8866         reason  =       result (SCPE_OK if ok)
8867 */
8868 
8869 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8870 {
8871 return _sim_activate_after (uptr, usec_delay);
8872 }
8873 
8874 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8875 {
8876 if (sim_is_active (uptr))                               /* already active? */
8877     return SCPE_OK;
8878 return sim_timer_activate_after (uptr, usec_delay);
8879 }
8880 
8881 /* sim_cancel - cancel (dequeue) event
8882 
8883    Inputs:
8884         uptr    =       pointer to unit
8885    Outputs:
8886         reason  =       result (SCPE_OK if ok)
8887 
8888 */
8889 
8890 t_stat sim_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8891 {
8892 UNIT *cptr, *nptr;
8893 #if defined(TESTING)
8894 cpu_state_t * cpup = _cpup;
8895 #endif
8896 
8897 if (sim_clock_queue == QUEUE_LIST_END)
8898     return SCPE_OK;
8899 if (!sim_is_active (uptr))
8900     return SCPE_OK;
8901 UPDATE_SIM_TIME;                                        /* update sim time */
8902 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8903 nptr = QUEUE_LIST_END;
8904 
8905 if (sim_clock_queue == uptr) {
8906     nptr = sim_clock_queue = uptr->next;
8907     uptr->next = NULL;                                  /* hygiene */
8908     }
8909 else {
8910     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8911         if (cptr->next == uptr) {
8912             nptr = cptr->next = uptr->next;
8913             uptr->next = NULL;                          /* hygiene */
8914             break;                                      /* end queue scan */
8915             }
8916         }
8917     }
8918 if (nptr != QUEUE_LIST_END)
8919     nptr->time += (uptr->next) ? 0 : uptr->time;
8920 if (!uptr->next)
8921     uptr->time = 0;
8922 if (sim_clock_queue != QUEUE_LIST_END)
8923     sim_interval = sim_clock_queue->time;
8924 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8925 if (uptr->next) {
8926     sim_printf ("\rCancel failed for '%s'!\r\n", sim_uname(uptr));
8927     if (sim_deb)
8928         fclose(sim_deb);
8929     (void)fprintf (stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
8930                    __func__, __FILE__, __LINE__);
8931     abort ();
8932     }
8933 return SCPE_OK;
8934 }
8935 
8936 /* sim_is_active - test for entry in queue
8937 
8938    Inputs:
8939         uptr    =       pointer to unit
8940    Outputs:
8941         result =        TRUE if unit is busy, FALSE inactive
8942 */
8943 
8944 t_bool sim_is_active (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8945 {
8946 if (uptr->next == NULL)
8947   return FALSE;
8948 else
8949 return TRUE;
8950 }
8951 
8952 /* sim_activate_time - return activation time
8953 
8954    Inputs:
8955         uptr    =       pointer to unit
8956    Outputs:
8957         result =        absolute activation time + 1, 0 if inactive
8958 */
8959 
8960 int32 sim_activate_time (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8961 {
8962 UNIT *cptr;
8963 int32 accum = 0;
8964 
8965 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8966     if (cptr == sim_clock_queue) {
8967         if (sim_interval > 0)
8968             accum = accum + sim_interval;
8969         }
8970     else
8971         accum = accum + cptr->time;
8972     if (cptr == uptr)
8973         return accum + 1;
8974     }
8975 return 0;
8976 }
8977 
8978 /* sim_gtime - return global time
8979 
8980    Inputs: none
8981    Outputs:
8982         time    =       global time
8983 */
8984 
8985 double sim_gtime (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8986 {
8987 return sim_time;
8988 }
8989 
8990 /* sim_qcount - return queue entry count
8991 
8992    Inputs: none
8993    Outputs:
8994         count   =       number of entries on the queue
8995 */
8996 
8997 int32 sim_qcount (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8998 {
8999 int32 cnt;
9000 UNIT *uptr;
9001 
9002 cnt = 0;
9003 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
9004     cnt++;
9005 return cnt;
9006 }
9007 
9008 /* Breakpoint package.  This module replaces the VM-implemented one
9009    instruction breakpoint capability.
9010 
9011    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
9012    efficient binary searching.  A breakpoint consists of a six entry structure:
9013 
9014         addr                    address of the breakpoint
9015         type                    types of breakpoints set on the address
9016                                 a bit mask representing letters A-Z
9017         cnt                     number of iterations before breakp is taken
9018         action                  pointer command string to be executed
9019                                 when break is taken
9020         next                    list of other breakpoints with the same addr specifier
9021         time_fired              array of when this breakpoint was fired for each class
9022 
9023    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
9024    is the bitwise OR of all the type fields).  A simulator need only check for
9025    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
9026 
9027    The package contains the following public routines:
9028 
9029         sim_brk_init            initialize
9030         sim_brk_set             set breakpoint
9031         sim_brk_clr             clear breakpoint
9032         sim_brk_clrall          clear all breakpoints
9033         sim_brk_show            show breakpoint
9034         sim_brk_showall         show all breakpoints
9035         sim_brk_test            test for breakpoint
9036         sim_brk_npc             PC has been changed
9037         sim_brk_getact          get next action
9038         sim_brk_clract          clear pending actions
9039 
9040    Initialize breakpoint system.
9041 */
9042 
9043 t_stat sim_brk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9044 {
9045 int32 i;
9046 
9047 for (i=0; i<sim_brk_lnt; i++) {
9048     BRKTAB *bp;
9049     if (sim_brk_tab)
9050       {
9051         bp = sim_brk_tab[i];
9052 
9053         while (bp)
9054           {
9055             BRKTAB *bpt = bp->next;
9056 
9057             FREE (bp->act);
9058             FREE (bp);
9059             bp = bpt;
9060           }
9061       }
9062 }
9063 if (sim_brk_tab != NULL)
9064     (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9065 sim_brk_lnt = SIM_BRK_INILNT;
9066 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
9067 if (sim_brk_tab == NULL)
9068     return SCPE_MEM;
9069 (void)memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
9070 sim_brk_ent = sim_brk_ins = 0;
9071 sim_brk_clract ();
9072 sim_brk_npc (0);
9073 return SCPE_OK;
9074 }
9075 
9076 /* Search for a breakpoint in the sorted breakpoint table */
9077 
9078 BRKTAB *sim_brk_fnd (t_addr loc)
     /* [previous][next][first][last][top][bottom][index][help] */
9079 {
9080 int32 lo, hi, p;
9081 BRKTAB *bp;
9082 
9083 if (sim_brk_ent == 0) {                                 /* table empty? */
9084     sim_brk_ins = 0;                                    /* insrt at head */
9085     return NULL;                                        /* sch fails */
9086     }
9087 lo = 0;                                                 /* initial bounds */
9088 hi = sim_brk_ent - 1;
9089 do {
9090     p = (lo + hi) >> 1;                                 /* probe */
9091     bp = sim_brk_tab[p];                                /* table addr */
9092     if (loc == bp->addr) {                              /* match? */
9093         sim_brk_ins = p;
9094         return bp;
9095         }
9096     else if (loc < bp->addr)                            /* go down? p is upper */
9097         hi = p - 1;
9098     else lo = p + 1;                                    /* go up? p is lower */
9099     } while (lo <= hi);
9100 if (loc < bp->addr)                                     /* insrt before or */
9101     sim_brk_ins = p;
9102 else sim_brk_ins = p + 1;                               /* after last sch */
9103 return NULL;
9104 }
9105 
9106 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
     /* [previous][next][first][last][top][bottom][index][help] */
9107 {
9108 BRKTAB *bp = sim_brk_fnd (loc);
9109 
9110 while (bp) {
9111     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
9112                   (bp->typ == btyp))
9113         return bp;
9114     bp = bp->next;
9115     }
9116 return bp;
9117 }
9118 
9119 /* Insert a breakpoint */
9120 
9121 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9122 {
9123 int32 i, t;
9124 BRKTAB *bp, **newp;
9125 
9126 if (sim_brk_ins < 0)
9127     return NULL;
9128 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
9129     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
9130     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
9131     if (newp == NULL)                                   /* can't extend */
9132         return NULL;
9133     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
9134     (void)memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
9135     FREE (sim_brk_tab);                                 /* free old table */
9136     sim_brk_tab = newp;                                 /* new base, lnt */
9137     sim_brk_lnt = t;
9138     }
9139 if ((sim_brk_ins == sim_brk_ent) ||
9140     ((sim_brk_ins != sim_brk_ent) && //-V728
9141      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
9142     for (i = sim_brk_ent; i > sim_brk_ins; --i)
9143         sim_brk_tab[i] = sim_brk_tab[i - 1];
9144     sim_brk_tab[sim_brk_ins] = NULL;
9145     }
9146 bp = (BRKTAB *)calloc (1, sizeof (*bp));
9147 if (!bp)
9148   {
9149     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9150                    __func__, __FILE__, __LINE__);
9151 #if defined(USE_BACKTRACE)
9152 # if defined(SIGUSR2)
9153     (void)raise(SIGUSR2);
9154     /*NOTREACHED*/ /* unreachable */
9155 # endif /* if defined(SIGUSR2) */
9156 #endif /* if defined(USE_BACKTRACE) */
9157     abort();
9158   }
9159 bp->next = sim_brk_tab[sim_brk_ins];
9160 sim_brk_tab[sim_brk_ins] = bp;
9161 if (bp->next == NULL)
9162     sim_brk_ent += 1;
9163 bp->addr = loc;
9164 bp->typ = btyp;
9165 bp->cnt = 0;
9166 bp->act = NULL;
9167 for (i = 0; i < SIM_BKPT_N_SPC; i++)
9168     bp->time_fired[i] = -1.0;
9169 return bp;
9170 }
9171 
9172 /* Set a breakpoint of type sw */
9173 
9174 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
9175 {
9176 BRKTAB *bp;
9177 
9178 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
9179     sw |= sim_brk_dflt;
9180 if (~sim_brk_types & sw) {
9181     char gbuf[CBUFSIZE];
9182 
9183     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
9184     }
9185 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
9186     return SCPE_ARG;
9187 bp = sim_brk_fnd (loc);                                 /* loc present? */
9188 if (!bp)                                                /* no, allocate */
9189     bp = sim_brk_new (loc, sw);
9190 else {
9191     while (bp && (bp->typ != (uint32)sw))
9192         bp = bp->next;
9193     if (!bp)
9194         bp = sim_brk_new (loc, sw);
9195     }
9196 if (!bp)                                                /* still no? mem err */
9197     return SCPE_MEM;
9198 bp->cnt = ncnt;                                         /* set count */
9199 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
9200     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
9201     FREE (bp->act);                                     /* deallocate */
9202     bp->act = NULL;                                     /* now no action */
9203     }
9204 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9205     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
9206     if (newp == NULL)                                   /* mem err? */
9207         return SCPE_MEM;
9208     strncpy (newp, act, CBUFSIZE);                      /* copy action */
9209     bp->act = newp;                                     /* set pointer */
9210     }
9211 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
9212 return SCPE_OK;
9213 }
9214 
9215 /* Clear a breakpoint */
9216 
9217 t_stat sim_brk_clr (t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9218 {
9219 BRKTAB *bpl = NULL;
9220 BRKTAB *bp = sim_brk_fnd (loc);
9221 int32 i;
9222 
9223 if (!bp)                                                /* not there? ok */
9224     return SCPE_OK;
9225 if (sw == 0)
9226     sw = SIM_BRK_ALLTYP;
9227 
9228 #if !defined(__clang_analyzer__)
9229 while (bp) {
9230     if (bp->typ == (bp->typ & sw)) {
9231         FREE (bp->act);                                 /* deallocate action */
9232         if (bp == sim_brk_tab[sim_brk_ins])
9233             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
9234         else
9235           {
9236             if (bpl)
9237               bpl->next = bp->next;
9238           }
9239         FREE (bp);
9240         bp = bpl;
9241         }
9242     else {
9243         bpl = bp;
9244         bp = bp->next;
9245         }
9246     }
9247 #endif /* if !defined(__clang_analyzer__) */
9248 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
9249     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
9250     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
9251         sim_brk_tab[i] = sim_brk_tab[i+1];
9252     }
9253 sim_brk_summ = 0;                                       /* recalc summary */
9254 for (i = 0; i < sim_brk_ent; i++) {
9255     bp = sim_brk_tab[i];
9256     while (bp) {
9257         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
9258         bp = bp->next;
9259         }
9260     }
9261 return SCPE_OK;
9262 }
9263 
9264 /* Clear all breakpoints */
9265 
9266 t_stat sim_brk_clrall (int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9267 {
9268 int32 i;
9269 
9270 if (sw == 0)
9271     sw = SIM_BRK_ALLTYP;
9272 for (i = 0; i < sim_brk_ent;) {
9273     t_addr loc = sim_brk_tab[i]->addr;
9274     sim_brk_clr (loc, sw);
9275     if ((i < sim_brk_ent) &&
9276         (loc == sim_brk_tab[i]->addr))
9277         ++i;
9278     }
9279 return SCPE_OK;
9280 }
9281 
9282 /* Show a breakpoint */
9283 
9284 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9285 {
9286 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
9287 DEVICE *dptr;
9288 uint32 i, any;
9289 
9290 if ((sw == 0) || (sw == SWMASK ('C'))) {
9291     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0); }
9292 if (!bp || (!(bp->typ & sw))) {
9293     return SCPE_OK; }
9294 dptr = sim_dflt_dev;
9295 if (dptr == NULL) {
9296     return SCPE_OK; }
9297 if (sw & SWMASK ('C')) {
9298     if (st != NULL) {
9299         (void)fprintf (st, "SET BREAK "); }
9300 } else {
9301     if (sim_vm_fprint_addr) {
9302         sim_vm_fprint_addr
9303             (st, dptr, loc);
9304     } else {
9305         fprint_val
9306             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9307     if (st != NULL) {
9308         (void)fprintf (st, ":\t"); }
9309     }
9310 for (i = any = 0; i < 26; i++) {
9311     if ((bp->typ >> i) & 1) {
9312         if ((sw & SWMASK ('C')) == 0) {
9313             if (any) {
9314                 if (st != NULL) {
9315                     (void)fprintf (st, ", "); } }
9316             if (st != NULL) {
9317                 fputc (i + 'A', st); }
9318             }
9319         } else {
9320             if (st != NULL) {
9321                 (void)fprintf (st, "-%c", i + 'A'); }
9322         any = 1;
9323         }
9324     }
9325 if (sw & SWMASK ('C')) {
9326     if (st != NULL) {
9327         (void)fprintf (st, " "); }
9328     if (sim_vm_fprint_addr) {
9329         if (st != NULL) {
9330             sim_vm_fprint_addr (st, dptr, loc); }
9331     } else {
9332         fprint_val
9333             (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); }
9334     }
9335 if (bp->cnt > 0) {
9336     if (st != NULL) {
9337         (void)fprintf (st, "[%d]", bp->cnt); } }
9338 if (bp->act != NULL) {
9339     if (st != NULL) {
9340         (void)fprintf (st, "; %s", bp->act); } }
9341 (void)fprintf (st, "\n");
9342 return SCPE_OK;
9343 }
9344 
9345 /* Show all breakpoints */
9346 
9347 t_stat sim_brk_showall (FILE *st, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
9348 {
9349 int32 bit, mask, types;
9350 BRKTAB **bpt;
9351 
9352 if ((sw == 0) || (sw == SWMASK ('C')))
9353     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
9354 for (types=bit=0; bit <= ('Z'-'A'); bit++)
9355     if (sim_brk_types & (1 << bit))
9356         ++types;
9357 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
9358     (void)fprintf (st, "Supported Breakpoint Types:");
9359     for (bit=0; bit <= ('Z'-'A'); bit++)
9360         if (sim_brk_types & (1 << bit))
9361             (void)fprintf (st, " -%c", 'A' + bit);
9362     (void)fprintf (st, "\n");
9363     }
9364 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
9365     mask = (sw & sim_brk_types);
9366     (void)fprintf (st, "Displaying Breakpoint Types:");
9367     for (bit=0; bit <= ('Z'-'A'); bit++)
9368         if (mask & (1 << bit))
9369             (void)fprintf (st, " -%c", 'A' + bit);
9370     (void)fprintf (st, "\n");
9371     }
9372 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9373     BRKTAB *prev = NULL;
9374     BRKTAB *cur = *bpt;
9375     BRKTAB *next;
9376     /* First reverse the list */
9377     while (cur) {
9378         next = cur->next;
9379         cur->next = prev;
9380         prev = cur;
9381         cur = next;
9382         }
9383     /* save reversed list in the head pointer so lookups work */
9384     *bpt = prev;
9385     /* Walk the reversed list and print it in the order it was defined in */
9386     cur = prev;
9387     while (cur) {
9388         if (cur->typ & sw)
9389             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
9390         cur = cur->next;
9391         }
9392     /* reversing the list again */
9393     cur = prev;
9394     prev = NULL;
9395     while (cur) {
9396         next = cur->next;
9397         cur->next = prev;
9398         prev = cur;
9399         cur = next;
9400         }
9401     /* restore original list */
9402     *bpt = prev;
9403     }
9404 return SCPE_OK;
9405 }
9406 
9407 /* Test for breakpoint */
9408 
9409 uint32 sim_brk_test (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9410 {
9411 BRKTAB *bp;
9412 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
9413 
9414 if (sim_brk_summ & BRK_TYP_DYN_ALL)
9415     btyp |= BRK_TYP_DYN_ALL;
9416 
9417 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
9418     double s_gtime = sim_gtime ();                      /* get time now */
9419 
9420     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
9421         return 0;
9422     bp->time_fired[spc] = s_gtime;                      /* remember match time */
9423     if (--bp->cnt > 0)                                  /* count > 0? */
9424         return 0;
9425     bp->cnt = 0;                                        /* reset count */
9426     sim_brk_setact (bp->act);                           /* set up actions */
9427     sim_brk_match_type = btyp & bp->typ;                /* set return value */
9428     if (bp->typ & BRK_TYP_TEMP)
9429         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
9430     sim_brk_match_addr = loc;
9431     return sim_brk_match_type;
9432     }
9433 return 0;
9434 }
9435 
9436 /* Get next pending action, if any */
9437 
9438 CONST char *sim_brk_getact (char *buf, int32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
9439 {
9440 char *ep;
9441 size_t lnt;
9442 
9443 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
9444     return NULL;
9445 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
9446     sim_brk_act[sim_do_depth]++;
9447 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
9448     return sim_brk_clract ();
9449     }
9450 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
9451     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
9452     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
9453     buf[lnt] = 0;                                       /* erase ; */
9454     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
9455     }
9456 else {
9457     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
9458     sim_brk_clract ();                                  /* no more */
9459     }
9460 return buf;
9461 }
9462 
9463 /* Clear pending actions */
9464 
9465 char *sim_brk_clract (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9466 {
9467 FREE (sim_brk_act_buf[sim_do_depth]);
9468 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
9469 }
9470 
9471 /* Set up pending actions */
9472 
9473 void sim_brk_setact (const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
9474 {
9475 if (action) {
9476     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
9477     if (!sim_brk_act_buf[sim_do_depth])
9478       {
9479         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9480                        __func__, __FILE__, __LINE__);
9481 #if defined(USE_BACKTRACE)
9482 # if defined(SIGUSR2)
9483         (void)raise(SIGUSR2);
9484         /*NOTREACHED*/ /* unreachable */
9485 # endif /* if defined(SIGUSR2) */
9486 #endif /* if defined(USE_BACKTRACE) */
9487         abort();
9488       }
9489     strcpy (sim_brk_act_buf[sim_do_depth], action);
9490     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
9491     }
9492 else
9493     sim_brk_clract ();
9494 }
9495 
9496 /* New PC */
9497 
9498 void sim_brk_npc (uint32 cnt)
     /* [previous][next][first][last][top][bottom][index][help] */
9499 {
9500 uint32 spc;
9501 BRKTAB **bpt, *bp;
9502 
9503 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
9504     cnt = SIM_BKPT_N_SPC;
9505 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9506     for (bp = *bpt; bp; bp = bp->next) {
9507         for (spc = 0; spc < cnt; spc++)
9508             bp->time_fired[spc] = -1.0;
9509         }
9510     }
9511 }
9512 
9513 /* Clear breakpoint space */
9514 
9515 void sim_brk_clrspc (uint32 spc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9516 {
9517 BRKTAB **bpt, *bp;
9518 
9519 if (spc < SIM_BKPT_N_SPC) {
9520     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9521         for (bp = *bpt; bp; bp = bp->next) {
9522             if (bp->typ & btyp)
9523                 bp->time_fired[spc] = -1.0;
9524             }
9525         }
9526     }
9527 }
9528 
9529 const char *sim_brk_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
9530 {
9531 static char msg[256];
9532 char addr[65] = "";
9533 char buf[32];
9534 
9535 msg[0] = '\0';
9536 if (sim_dflt_dev) {
9537   if (sim_vm_sprint_addr)
9538     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
9539   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
9540 }
9541 if (sim_brk_type_desc) {
9542     BRKTYPTAB *brk = sim_brk_type_desc;
9543 
9544     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
9545         if (brk->btyp == sim_brk_match_type) {
9546             (void)sprintf (msg, "%s: %s", brk->desc, addr);
9547             break;
9548             }
9549         brk++;
9550         }
9551     }
9552 if (!msg[0])
9553     (void)sprintf (msg, "%s Breakpoint at: %s\n",
9554                    put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9555 
9556 return msg;
9557 }
9558 
9559 /* Expect package.  This code provides a mechanism to stop and control simulator
9560    execution based on traffic coming out of simulated ports and as well as a means
9561    to inject data into those ports.  It can conceptually viewed as a string
9562    breakpoint package.
9563 
9564    Expect rules are stored in tables associated with each port which can use this
9565    facility.  An expect rule consists of a five entry structure:
9566 
9567         match                   the expect match string
9568         size                    the number of bytes in the match string
9569         match_pattern           the expect match string in display format
9570         cnt                     number of iterations before match is declared
9571         action                  command string to be executed when match occurs
9572 
9573    All active expect rules are contained in an expect match context structure.
9574 
9575         rules                   the match rules
9576         size                    the count of match rules
9577         buf                     the buffer of output data which has been produced
9578         buf_ins                 the buffer insertion point for the next output data
9579         buf_size                the buffer size
9580 
9581    The package contains the following public routines:
9582 
9583         sim_set_expect          expect command parser and initializer
9584         sim_set_noexpect        noexpect command parser
9585         sim_exp_set             set or add an expect rule
9586         sim_exp_clr             clear or delete an expect rule
9587         sim_exp_clrall          clear all expect rules
9588         sim_exp_show            show an expect rule
9589         sim_exp_showall         show all expect rules
9590         sim_exp_check           test for rule match
9591 */
9592 
9593 /* Set expect */
9594 
9595 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9596 {
9597 char gbuf[CBUFSIZE];
9598 CONST char *tptr;
9599 CONST char *c1ptr;
9600 t_bool after_set = FALSE;
9601 uint32 after;
9602 int32 cnt = 0;
9603 t_stat r;
9604 
9605 if (exp == NULL)
9606     return sim_messagef (SCPE_ARG, "Null exp!\n");
9607 after = exp->after;
9608 
9609 if ((cptr == NULL) || (*cptr == 0))
9610     return SCPE_2FARG;
9611 if (*cptr == '[') {
9612     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9613     if ((cptr == c1ptr) || (*c1ptr != ']'))
9614         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
9615     cptr = c1ptr + 1;
9616     while (sim_isspace(*cptr))
9617         ++cptr;
9618     }
9619 tptr = get_glyph (cptr, gbuf, ',');
9620 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9621     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9622     if (r != SCPE_OK)
9623         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
9624     after_set = TRUE;
9625     cptr = tptr;
9626     }
9627 if ((*cptr != '"') && (*cptr != '\''))
9628     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9629 cptr = get_glyph_quoted (cptr, gbuf, 0);
9630 
9631 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9632 }
9633 
9634 /* Clear expect */
9635 
9636 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9637 {
9638 char gbuf[CBUFSIZE];
9639 
9640 if (NULL == cptr || !*cptr)
9641     return sim_exp_clrall (exp);                    /* clear all rules */
9642 if ((*cptr != '"') && (*cptr != '\''))
9643     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9644 cptr = get_glyph_quoted (cptr, gbuf, 0);
9645 if (*cptr != '\0')
9646     return SCPE_2MARG;                              /* No more arguments */
9647 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9648 }
9649 
9650 /* Search for an expect rule in an expect context */
9651 
9652 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, size_t start_rule)
     /* [previous][next][first][last][top][bottom][index][help] */
9653 {
9654 size_t i;
9655 
9656 if (NULL == exp->rules)
9657     return NULL;
9658 for (i=start_rule; i<exp->size; i++)
9659     if (!strcmp (exp->rules[i].match_pattern, match))
9660         return &exp->rules[i];
9661 return NULL;
9662 }
9663 
9664 /* Clear (delete) an expect rule */
9665 
9666 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9667 {
9668 size_t i;
9669 
9670 if (NULL == ep)                                         /* not there? ok */
9671     return SCPE_OK;
9672 FREE (ep->match);                                       /* deallocate match string */
9673 FREE (ep->match_pattern);                               /* deallocate the display format match string */
9674 FREE (ep->act);                                         /* deallocate action */
9675 exp->size -= 1;                                         /* decrement count */
9676 #if !defined(__clang_analyzer__)
9677 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9678     exp->rules[i] = exp->rules[i+1];
9679 if (exp->size == 0) {                                   /* No rules left? */
9680     FREE (exp->rules);
9681     exp->rules = NULL;
9682     }
9683 #endif /* if !defined(__clang_analyzer__) */
9684 return SCPE_OK;
9685 }
9686 
9687 t_stat sim_exp_clr (EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9688 {
9689 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9690 
9691 while (ep) {
9692     sim_exp_clr_tab (exp, ep);
9693     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9694     }
9695 return SCPE_OK;
9696 }
9697 
9698 /* Clear all expect rules */
9699 
9700 t_stat sim_exp_clrall (EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9701 {
9702 int32 i;
9703 
9704 for (i=0; i<exp->size; i++) {
9705     FREE (exp->rules[i].match);                         /* deallocate match string */
9706     FREE (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9707     FREE (exp->rules[i].act);                           /* deallocate action */
9708     }
9709 FREE (exp->rules);
9710 exp->rules = NULL;
9711 exp->size = 0;
9712 FREE (exp->buf);
9713 exp->buf = NULL;
9714 exp->buf_size = 0;
9715 exp->buf_ins = 0;
9716 return SCPE_OK;
9717 }
9718 
9719 /* Set/Add an expect rule */
9720 
9721 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] */
9722 {
9723 EXPTAB *ep;
9724 uint8 *match_buf;
9725 uint32 match_size;
9726 size_t i;
9727 
9728 /* Validate the match string */
9729 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9730 if (!match_buf)
9731     return SCPE_MEM;
9732 if (switches & EXP_TYP_REGEX) {
9733     FREE (match_buf);
9734     return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9735     }
9736 else {
9737     if (switches & EXP_TYP_REGEX_I) {
9738         FREE (match_buf);
9739         return sim_messagef (SCPE_ARG, "Case independent matching is only valid for RegEx expect rules\n");
9740         }
9741     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9742     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9743         FREE (match_buf);
9744         return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9745         }
9746     }
9747 FREE (match_buf);
9748 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9749     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9750         (exp->rules[i].switches & EXP_TYP_PERSIST))
9751         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9752     }
9753 if (after && exp->size)
9754     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9755 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9756 if (!exp->rules)
9757   {
9758     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9759                    __func__, __FILE__, __LINE__);
9760 #if defined(USE_BACKTRACE)
9761 # if defined(SIGUSR2)
9762     (void)raise(SIGUSR2);
9763     /*NOTREACHED*/ /* unreachable */
9764 # endif /* if defined(SIGUSR2) */
9765 #endif /* if defined(USE_BACKTRACE) */
9766     abort();
9767   }
9768 ep = &exp->rules[exp->size];
9769 exp->size += 1;
9770 exp->after = after;                                     /* set halt after value */
9771 (void)memset (ep, 0, sizeof(*ep));
9772 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9773 if (ep->match_pattern)
9774     strcpy (ep->match_pattern, match);
9775 ep->cnt = cnt;                                          /* set proceed count */
9776 ep->switches = switches;                                /* set switches */
9777 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9778 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9779     sim_exp_clr_tab (exp, ep);                          /* clear it */
9780     FREE (match_buf);                                   /* release allocation */
9781     return SCPE_MEM;
9782     }
9783 if (switches & EXP_TYP_REGEX) {
9784     FREE (match_buf);
9785     match_buf = NULL;
9786     }
9787 else {
9788     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9789     sim_decode_quoted_string (match, match_buf, &match_size);
9790     ep->match = match_buf;
9791     ep->size = match_size;
9792     }
9793 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9794 if (!ep->match_pattern)
9795   {
9796     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9797                    __func__, __FILE__, __LINE__);
9798 #if defined(USE_BACKTRACE)
9799 # if defined(SIGUSR2)
9800     (void)raise(SIGUSR2);
9801     /*NOTREACHED*/ /* unreachable */
9802 # endif /* if defined(SIGUSR2) */
9803 #endif /* if defined(USE_BACKTRACE) */
9804     abort();
9805   }
9806 strcpy (ep->match_pattern, match);
9807 if (ep->act) {                                          /* replace old action? */
9808     FREE (ep->act);                                     /* deallocate */
9809     ep->act = NULL;                                     /* now no action */
9810     }
9811 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
9812 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9813     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9814     if (newp == NULL)                                   /* mem err? */
9815         return SCPE_MEM;
9816     strcpy (newp, act);                                 /* copy action */
9817     ep->act = newp;                                     /* set pointer */
9818     }
9819 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9820 for (i=0; i<exp->size; i++) {
9821     size_t compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9822     if (compare_size >= exp->buf_size) {
9823         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9824         exp->buf_size = compare_size + 1;
9825         }
9826     }
9827 return SCPE_OK;
9828 }
9829 
9830 /* Show an expect rule */
9831 
9832 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9833 {
9834 if (!ep)
9835     return SCPE_OK;
9836 (void)fprintf (st, "EXPECT");
9837 if (ep->switches & EXP_TYP_PERSIST)
9838     (void)fprintf (st, " -p");
9839 if (ep->switches & EXP_TYP_CLEARALL)
9840     (void)fprintf (st, " -c");
9841 if (ep->switches & EXP_TYP_REGEX)
9842     (void)fprintf (st, " -r");
9843 if (ep->switches & EXP_TYP_REGEX_I)
9844     (void)fprintf (st, " -i");
9845 (void)fprintf (st, " %s", ep->match_pattern);
9846 if (ep->cnt > 0)
9847     (void)fprintf (st, " [%d]", ep->cnt);
9848 if (ep->act)
9849     (void)fprintf (st, " %s", ep->act);
9850 (void)fprintf (st, "\n");
9851 return SCPE_OK;
9852 }
9853 
9854 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9855 {
9856 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9857 
9858 if (exp->buf_size) {
9859     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9860 
9861     (void)fprintf (st, "Match Buffer Size: %lld\n",
9862                    (long long)exp->buf_size);
9863     (void)fprintf (st, "Buffer Insert Offset: %lld\n",
9864                    (long long)exp->buf_ins);
9865     (void)fprintf (st, "Buffer Contents: %s\n",
9866                    bstr);
9867     FREE (bstr);
9868     }
9869 if (exp->after)
9870     (void)fprintf (st, "Halt After: %lld instructions\n",
9871                    (long long)exp->after);
9872 if (exp->dptr && exp->dbit)
9873     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\n",
9874                    sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "",
9875                    exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
9876 (void)fprintf (st, "Match Rules:\n");
9877 if (!*match)
9878     return sim_exp_showall (st, exp);
9879 if (!ep) {
9880     (void)fprintf (st, "No Rules match '%s'\n", match);
9881     return SCPE_ARG;
9882     }
9883 do {
9884     sim_exp_show_tab (st, exp, ep);
9885     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9886     } while (ep);
9887 return SCPE_OK;
9888 }
9889 
9890 /* Show all expect rules */
9891 
9892 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9893 {
9894 size_t i;
9895 
9896 for (i=0; i < exp->size; i++)
9897     sim_exp_show_tab (st, exp, &exp->rules[i]);
9898 return SCPE_OK;
9899 }
9900 
9901 /* Test for expect match */
9902 
9903 t_stat sim_exp_check (EXPECT *exp, uint8 data)
     /* [previous][next][first][last][top][bottom][index][help] */
9904 {
9905 size_t i;
9906 EXPTAB *ep = NULL;
9907 char *tstr = NULL;
9908 #if defined(TESTING)
9909 cpu_state_t * cpup = _cpup;
9910 #endif
9911 
9912 if ((!exp) || (!exp->rules))                            /* Anything to check? */
9913     return SCPE_OK;
9914 
9915 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
9916 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
9917 
9918 for (i=0; i < exp->size; i++) {
9919     ep = &exp->rules[i];
9920     if (ep == NULL)
9921         break;
9922     if (ep->switches & EXP_TYP_REGEX) {
9923         }
9924     else {
9925         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
9926             /*
9927              * First compare the newly deposited data at the beginning
9928              * of buffer with the end of the match string
9929              */
9930             if (exp->buf_ins > 0) {
9931                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9932                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9933                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9934 
9935                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%lld]: %s\n",
9936                                (long long)exp->buf_ins, estr);
9937                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9938                     FREE (estr);
9939                     FREE (mstr);
9940                     }
9941                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9942                     continue;
9943                 }
9944             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9945                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9946                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9947 
9948                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\n",
9949                            (long long)exp->buf_size-(ep->size-exp->buf_ins),
9950                            (long long)ep->size-exp->buf_ins, estr);
9951                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9952                 FREE (estr);
9953                 FREE (mstr);
9954                 }
9955             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9956                 continue;
9957             break;
9958             }
9959         else {
9960             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9961                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9962                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9963 
9964                 sim_debug (exp->dbit, exp->dptr, "Checking String[%lld:%lld]: %s\n",
9965                            (long long)exp->buf_ins-ep->size,
9966                            (long long)ep->size, estr);
9967                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9968                 FREE (estr);
9969                 FREE (mstr);
9970                 }
9971             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9972                 continue;
9973             break;
9974             }
9975         }
9976     }
9977 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
9978         exp->buf_ins = 0;                               /* wrap around to beginning */
9979         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9980     }
9981 if ((ep != NULL) && (i != exp->size)) {                 /* Found? */
9982     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern!\n");
9983     if (ep->cnt > 0) {
9984         ep->cnt -= 1;
9985         sim_debug (exp->dbit, exp->dptr, "Waiting for %lld more match%s before stopping\n",
9986                    (long long)ep->cnt, (ep->cnt == 1) ? "" : "es");
9987         }
9988     else {
9989         uint32 after   = exp->after;
9990         int32 switches = ep->switches;
9991         if (ep->act && *ep->act) {
9992             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
9993             }
9994         else {
9995             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
9996             }
9997         sim_brk_setact (ep->act);                       /* set up actions */
9998         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
9999             sim_exp_clrall (exp);                       /* delete all rules */
10000         else {
10001             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
10002                 sim_exp_clr_tab (exp, ep);              /* delete it */
10003             }
10004         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
10005                       (switches & EXP_TYP_TIME) ?
10006                             (uint32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
10007                              after);
10008         }
10009     /* Matched data is no longer available for future matching */
10010     exp->buf_ins = 0;
10011     }
10012 if (tstr)  //-V547
10013   FREE (tstr);
10014 return SCPE_OK;
10015 }
10016 
10017 /* Queue input data for sending */
10018 
10019 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
     /* [previous][next][first][last][top][bottom][index][help] */
10020 {
10021 if (snd->extoff != 0) {
10022     if (snd->insoff > snd->extoff)
10023         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10024     snd->insoff -= snd->extoff;
10025     snd->extoff  = 0;
10026     }
10027 if (snd->insoff+size > snd->bufsize) {
10028     snd->bufsize = snd->insoff+size;
10029     snd->buffer  = (uint8 *)realloc(snd->buffer, snd->bufsize);
10030     if (!snd->buffer)
10031       {
10032         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10033                        __func__, __FILE__, __LINE__);
10034 #if defined(USE_BACKTRACE)
10035 # if defined(SIGUSR2)
10036         (void)raise(SIGUSR2);
10037         /*NOTREACHED*/ /* unreachable */
10038 # endif /* if defined(SIGUSR2) */
10039 #endif /* if defined(USE_BACKTRACE) */
10040         abort();
10041       }
10042     }
10043 memcpy(snd->buffer+snd->insoff, data, size);
10044 snd->insoff += size;
10045 if (delay)
10046     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
10047 if (after)
10048     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
10049 if (snd->after == 0)
10050     snd->after = snd->delay;
10051 snd->next_time = sim_gtime() + snd->after;
10052 return SCPE_OK;
10053 }
10054 
10055 /* Cancel Queued input data */
10056 t_stat sim_send_clear (SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10057 {
10058 snd->insoff = 0;
10059 snd->extoff = 0;
10060 return SCPE_OK;
10061 }
10062 
10063 /* Display console Queued input data status */
10064 
10065 t_stat sim_show_send_input (FILE *st, const SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
10066 {
10067 if (snd->extoff < snd->insoff) {
10068     (void)fprintf (st, "%lld bytes of pending input Data:\n    ",
10069                    (long long)snd->insoff-snd->extoff);
10070     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
10071     (void)fprintf (st, "\n");
10072     }
10073 else
10074     (void)fprintf (st, "No Pending Input Data\n");
10075 if ((snd->next_time - sim_gtime()) > 0) {
10076     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
10077         (void)fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n",
10078                        (int)(snd->next_time - sim_gtime()),
10079         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
10080     else
10081         (void)fprintf (st, "Minimum of %d instructions before sending first character\n",
10082                        (int)(snd->next_time - sim_gtime()));
10083     }
10084 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
10085     (void)fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n",
10086                    (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
10087 else
10088     (void)fprintf (st, "Minimum of %d instructions between characters\n",
10089                    (int)snd->delay);
10090 if (snd->dptr && snd->dbit)
10091     (void)fprintf (st, "Debugging via: SET %s DEBUG%s%s\n",
10092                    sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "",
10093                    snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
10094 return SCPE_OK;
10095 }
10096 
10097 /* Poll for Queued input data */
10098 
10099 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10100 {
10101 #if defined(TESTING)
10102 cpu_state_t * cpup = _cpup;
10103 #endif
10104 if ((NULL != snd) && (snd->extoff < snd->insoff)) {     /* pending input characters available? */
10105     if (sim_gtime() < snd->next_time) {                 /* too soon? */
10106         *stat = SCPE_OK;
10107         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
10108         }
10109     else {
10110         char dstr[8] = "";
10111         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
10112         snd->next_time = sim_gtime() + snd->delay;
10113         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
10114             (void)sprintf (dstr, " '%c'", *stat & 0xFF);
10115         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
10116         }
10117     return TRUE;
10118     }
10119 return FALSE;
10120 }
10121 
10122 /* Message Text */
10123 
10124 const char *sim_error_text (t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10125 {
10126 static char msgbuf[64];
10127 
10128 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
10129 if (stat == SCPE_OK)
10130     return "No Error";
10131 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
10132     return scp_errors[stat-SCPE_BASE].message;
10133 (void)sprintf(msgbuf, "Error %d", stat);
10134 return msgbuf;
10135 }
10136 
10137 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
10138 {
10139 char gbuf[CBUFSIZE];
10140 size_t cond;
10141 
10142 *stat = SCPE_ARG;
10143 cptr = get_glyph (cptr, gbuf, 0);
10144 if (0 == memcmp("SCPE_", gbuf, 5))
10145     memmove (gbuf, gbuf + 5, 1 + strlen (gbuf + 5));  /* skip leading SCPE_ */
10146 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
10147     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
10148         cond += SCPE_BASE;
10149         break;
10150         }
10151 if (0 == strcmp(gbuf, "OK"))
10152     cond = SCPE_OK;
10153 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
10154     unsigned long numeric_cond = strtol(gbuf, NULL, 0);
10155     if (0 == numeric_cond)                    /* try explicit number */
10156         return SCPE_ARG;
10157     cond = (t_stat) numeric_cond;
10158     }
10159 if (cond > SCPE_MAX_ERR)
10160     return SCPE_ARG;
10161 *stat = cond;
10162 return SCPE_OK;
10163 }
10164 
10165 /* Debug printout routines, from Dave Hittner */
10166 
10167 const char* debug_bstates = "01_^";
10168 char debug_line_prefix[256];
10169 int32 debug_unterm  = 0;
10170 
10171 /* Finds debug phrase matching bitmask from from device DEBTAB table */
10172 
10173 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10174 {
10175 static const char *debtab_none    = "DEBTAB_ISNULL";
10176 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
10177 const char *some_match = NULL;
10178 int32 offset = 0;
10179 
10180 if (dptr->debflags == 0)
10181     return debtab_none;
10182 
10183 dbits &= dptr->dctrl;                           /* Look for just the bits that matched */
10184 
10185 /* Find matching words for bitmask */
10186 
10187 while ((offset < 32) && dptr->debflags[offset].name) {
10188     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
10189         return dptr->debflags[offset].name;
10190     if (dptr->debflags[offset].mask & dbits)
10191         some_match = dptr->debflags[offset].name;
10192     offset++;
10193     }
10194 return some_match ? some_match : debtab_nomatch;
10195 }
10196 
10197 /* Prints standard debug prefix unless previous call unterminated */
10198 
10199 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
10200 {
10201 const char* debug_type = get_dbg_verb (dbits, dptr);
10202 char tim_t[32] = "";
10203 char tim_a[32] = "";
10204 char  pc_s[64] = "";
10205 struct timespec time_now;
10206 
10207 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
10208     clock_gettime(CLOCK_REALTIME, &time_now);
10209     if (sim_deb_switches & SWMASK ('R'))
10210         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
10211     if (sim_deb_switches & SWMASK ('T')) {
10212         time_t tnow = (time_t)time_now.tv_sec;
10213         struct tm *now = gmtime(&tnow);
10214         (void)sprintf(tim_t, "%02d:%02d:%02d.%03ld ",
10215                       (int)now->tm_hour,
10216                       (int)now->tm_min,
10217                       (int)now->tm_sec,
10218                       (long)(time_now.tv_nsec / 1000000));
10219         }
10220     if (sim_deb_switches & SWMASK ('A')) {
10221         (void)sprintf(tim_t, "%d.%03ld ",
10222                       (int)(time_now.tv_sec),
10223                       (long)(time_now.tv_nsec / 1000000));
10224         }
10225     }
10226 if (sim_deb_switches & SWMASK ('P')) {
10227     t_value val;
10228 
10229     if (sim_vm_pc_value)
10230         val = (*sim_vm_pc_value)();
10231     else
10232         val = get_rval (sim_PC, 0);
10233     (void)sprintf(pc_s, "-%s:", sim_PC->name);
10234     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
10235     }
10236 (void)sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ",
10237               tim_t, tim_a, sim_gtime(), pc_s,
10238               "", dptr->name, debug_type);
10239 return debug_line_prefix;
10240 }
10241 
10242 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
     /* [previous][next][first][last][top][bottom][index][help] */
10243 {
10244 int32 i, fields, offset;
10245 uint32 value, beforevalue, mask;
10246 
10247 for (fields=offset=0; bitdefs[fields].name; ++fields) {
10248     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
10249         bitdefs[fields].offset = offset;
10250     offset += bitdefs[fields].width;
10251     }
10252 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
10253     if (bitdefs[i].name[0] == '\0')
10254         continue;
10255     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
10256         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
10257         (void)Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
10258         }
10259     else {
10260         const char *delta = "";
10261         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
10262         value = (uint32)((after >> bitdefs[i].offset) & mask);
10263         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
10264         if (value < beforevalue)
10265             delta = "_";
10266         if (value > beforevalue)
10267             delta = "^";
10268         if (bitdefs[i].valuenames)
10269             (void)Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
10270         else
10271             if (bitdefs[i].format) {
10272                 (void)Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
10273                 (void)Fprintf(stream, bitdefs[i].format, value);
10274                 (void)Fprintf(stream, " ");
10275                 }
10276             else
10277                 (void)Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
10278         }
10279     }
10280 }
10281 
10282 /* Prints state of a register: bit translation + state (0,1,_,^)
10283    indicating the state and transition of the bit and bitfields. States:
10284    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
10285 
10286 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
     /* [previous][next][first][last][top][bottom][index][help] */
10287     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
10288 {
10289 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
10290     if (!debug_unterm)
10291         (void)fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
10292     if (header)
10293         (void)fprintf(sim_deb, "%s: ", header);
10294     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
10295     if (terminate)
10296         (void)fprintf(sim_deb, "\r\n");
10297     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
10298     }
10299 }
10300 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
     /* [previous][next][first][last][top][bottom][index][help] */
10301     uint32 before, uint32 after, int terminate)
10302 {
10303 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
10304 }
10305 
10306 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10307 void sim_printf (const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10308 {
10309 char stackbuf[STACKBUFSIZE];
10310 int32 bufsize = sizeof(stackbuf);
10311 char *buf = stackbuf;
10312 int32 len;
10313 va_list arglist;
10314 
10315 while (1) {                                         /* format passed string, args */
10316     va_start (arglist, fmt);
10317     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10318     va_end (arglist);
10319 
10320 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10321 
10322     if ((len < 0) || (len >= bufsize-1)) {
10323         if (buf != stackbuf)
10324             FREE (buf);
10325         if (bufsize >= (INT_MAX / 2))
10326             return;                                 /* too big */
10327         bufsize = bufsize * 2;
10328         if (bufsize < len + 2)
10329             bufsize = len + 2;
10330         buf = (char *) malloc (bufsize);
10331         if (buf == NULL)                            /* out of memory */
10332             return;
10333         buf[bufsize-1] = '\0';
10334         continue;
10335         }
10336     break;
10337     }
10338 
10339 if (sim_is_running) {
10340     char *c, *remnant = buf;
10341     while ((c = strchr(remnant, '\n'))) {
10342         if ((c != buf) && (*(c - 1) != '\r'))
10343             (void)printf("%.*s\r\n", (int)(c-remnant), remnant);
10344         else
10345             (void)printf("%.*s\n", (int)(c-remnant), remnant);
10346         remnant = c + 1;
10347         }
10348     (void)printf("%s", remnant);
10349     }
10350 else
10351     (void)printf("%s", buf);
10352 if (sim_log && (sim_log != stdout))
10353     (void)fprintf (sim_log, "%s", buf);
10354 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
10355     (void)fprintf (sim_deb, "%s", buf);
10356 
10357 if (buf != stackbuf)
10358     FREE (buf);
10359 }
10360 
10361 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
10362 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10363 {
10364 char stackbuf[STACKBUFSIZE];
10365 size_t bufsize = sizeof(stackbuf);
10366 char *buf = stackbuf;
10367 size_t len;
10368 va_list arglist;
10369 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
10370 
10371 while (1) {                                         /* format passed string, args */
10372     va_start (arglist, fmt);
10373     len = vsnprintf (buf, bufsize-1, fmt, arglist);
10374     va_end (arglist);
10375 
10376 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10377 
10378     if (len >= bufsize - 1) {
10379         if (buf != stackbuf)
10380             FREE (buf);
10381         bufsize = bufsize * 2;
10382         if (bufsize < len + 2)
10383             bufsize = len + 2;
10384         buf = (char *) malloc (bufsize);
10385         if (buf == NULL)                            /* out of memory */
10386             return SCPE_MEM;
10387         buf[bufsize-1] = '\0';
10388         continue;
10389         }
10390     break;
10391     }
10392 
10393 if (sim_do_ocptr[sim_do_depth]) {
10394     if (!sim_do_echo && !sim_quiet && !inhibit_message)
10395         sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
10396     else {
10397         if (sim_deb)                        /* Always put context in debug output */
10398             (void)fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
10399         }
10400     }
10401 if (sim_is_running && !inhibit_message) {
10402     char *c, *remnant = buf;
10403     while ((c = strchr(remnant, '\n'))) {
10404         if ((c != buf) && (*(c - 1) != '\r'))
10405             (void)printf("%.*s\r\n", (int)(c-remnant), remnant);
10406         else
10407             (void)printf("%.*s\n", (int)(c-remnant), remnant);
10408         remnant = c + 1;
10409         }
10410     (void)printf("%s", remnant);
10411     }
10412 else {
10413     if (!inhibit_message)
10414         (void)printf("%s", buf);
10415     }
10416 if (sim_log && (sim_log != stdout) && !inhibit_message)
10417     (void)fprintf (sim_log, "%s", buf);
10418 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
10419     (void)fprintf (sim_deb, "%s", buf);
10420 
10421 if (buf != stackbuf)
10422     FREE (buf);
10423 return stat | SCPE_NOMESSAGE;
10424 }
10425 
10426 /* Inline debugging - will print debug message if debug file is
10427    set and the bitmask matches the current device debug options.
10428    Extra returns are added for un*x systems, since the output
10429    device is set into 'raw' mode when the cpu is booted,
10430    and the extra returns don't hurt any other systems.
10431    Callers should be calling sim_debug() which is a macro
10432    defined in scp.h which evaluates the action condition before
10433    incurring call overhead. */
10434 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10435 {
10436 DEVICE *dptr = (DEVICE *)vdptr;
10437 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
10438     char stackbuf[STACKBUFSIZE];
10439     int32 bufsize = sizeof(stackbuf);
10440     char *buf = stackbuf;
10441     va_list arglist;
10442     int32 i, j, len;
10443     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
10444 
10445     buf[bufsize-1] = '\0';
10446     while (1) {                                         /* format passed string, args */
10447         va_start (arglist, fmt);
10448         len = vsnprintf (buf, bufsize-1, fmt, arglist);
10449         va_end (arglist);
10450 
10451 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10452 
10453         if ((len < 0) || (len >= bufsize-1)) {
10454             if (buf != stackbuf)
10455                 FREE (buf);
10456             if (bufsize >= (INT_MAX / 2))
10457                 return;                                 /* too big */
10458             bufsize = bufsize * 2;
10459             if (bufsize < len + 2)
10460                 bufsize = len + 2;
10461             buf = (char *) malloc (bufsize);
10462             if (buf == NULL)                            /* out of memory */
10463                 return;
10464             buf[bufsize-1] = '\0';
10465             continue;
10466             }
10467         break;
10468         }
10469 
10470 /* Output the formatted data expanding newlines where they exist */
10471 
10472     for (i = j = 0; i < len; ++i) {
10473         if ('\n' == buf[i]) {
10474             if (i >= j) {
10475                 if ((i != j) || (i == 0)) {
10476                     if (debug_unterm)
10477                         (void)fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
10478                     else                                /* print prefix when required */
10479                         (void)fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
10480                     }
10481                 debug_unterm = 0;
10482                 }
10483             j = i + 1;
10484             }
10485         }
10486     if (i > j) {
10487         if (debug_unterm)
10488             (void)fprintf (sim_deb, "%.*s", i-j, &buf[j]);
10489         else                                        /* print prefix when required */
10490             (void)fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
10491         }
10492 
10493 /* Set unterminated flag for next time */
10494 
10495     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
10496     if (buf != stackbuf)
10497         FREE (buf);
10498     }
10499 return;
10500 }
10501 
10502 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] */
10503 {
10504 #if defined(TESTING)
10505 cpu_state_t * cpup = _cpup;
10506 #endif
10507 if (sim_deb && (dptr->dctrl & reason)) {
10508     sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
10509     if (data && len) {
10510         size_t i, same, group, sidx, oidx, ridx, eidx, soff;
10511         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
10512         static char hex[] = "0123456789ABCDEF";
10513         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
10514         static unsigned char ebcdic2ascii[] = {
10515             0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
10516             0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
10517             0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
10518             0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
10519             0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
10520             0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
10521             0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
10522             0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
10523             0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
10524             0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041,
10525             0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
10526             0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136,
10527             0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
10528             0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077,
10529             0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
10530             0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
10531             0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
10532             0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
10533             0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
10534             0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320,
10535             0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170,
10536             0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327,
10537             0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
10538             0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
10539             0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
10540             0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
10541             0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
10542             0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
10543             0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
10544             0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
10545             0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
10546             0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377,
10547             };
10548 
10549         for (i=same=0; i<len; i += 16) {
10550             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
10551                 ++same;
10552                 continue;
10553                 }
10554             if (same > 0) {
10555                 sim_debug (reason, dptr, "%04lx thru %04lx same as above\n",
10556                            i - (16*same),
10557                            i - 1);
10558                 same = 0;
10559                 }
10560             group = (((len - i) > 16) ? 16 : (len - i));
10561             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10562             eidx = strlen(ebcdicbuf);
10563             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10564             ridx = strlen(rad50buf);
10565             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10566             soff = strlen(strbuf);
10567             for (sidx=oidx=0; sidx<group; ++sidx) {
10568                 outbuf[oidx++] = ' ';
10569                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10570                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10571                 if (sim_isprint (data[i+sidx]))
10572                     strbuf[soff+sidx] = data[i+sidx];
10573                 else
10574                     strbuf[soff+sidx] = '.';
10575                 if (ridx && ((sidx&1) == 0)) {
10576                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10577 
10578                     if (word >= 64000) {
10579                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10580                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10581                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10582                         }
10583                     else {
10584                         rad50buf[ridx++] = rad50[word/1600];
10585                         rad50buf[ridx++] = rad50[(word/40)%40];
10586                         rad50buf[ridx++] = rad50[word%40];
10587                         }
10588                     }
10589                 if (eidx) {
10590                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10591                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10592                     else
10593                         ebcdicbuf[eidx++] = '.';
10594                     }
10595                 }
10596             outbuf[oidx] = '\0';
10597             strbuf[soff+sidx] = '\0';
10598             ebcdicbuf[eidx] = '\0';
10599             rad50buf[ridx] = '\0';
10600             sim_debug (reason, dptr, "%04lx%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
10601             }
10602         if (same > 0) {
10603             sim_debug (reason, dptr, "%04lx thru %04lx same as above\n", i-(16*same), (long unsigned int)(len-1));
10604             }
10605         }
10606     }
10607 }
10608 
10609 int Fprintf (FILE *f, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10610 {
10611 int ret = 0;
10612 va_list args;
10613 
10614 va_start (args, fmt);
10615     ret = vfprintf (f, fmt, args);
10616 va_end (args);
10617 return ret;
10618 }
10619 
10620 /* Hierarchical help presentation
10621  *
10622  * Device help can be presented hierarchically by calling
10623  *
10624  * t_stat scp_help (FILE *st, DEVICE *dptr,
10625  *                  UNIT *uptr, int flag, const char *help, char *cptr)
10626  *
10627  * or one of its three cousins from the device HELP routine.
10628  *
10629  * *help is the pointer to the structured help text to be displayed.
10630  *
10631  * The format and usage, and some helper macros can be found in scp_help.h
10632  * If you don't use the macros, it is not necessary to #include "scp_help.h".
10633  *
10634  * Actually, if you don't specify a DEVICE pointer and don't include
10635  * other device references, it can be used for non-device help.
10636  */
10637 
10638 #define blankch(x) ((x) == ' ' || (x) == '\t')
10639 
10640 typedef struct topic {
10641     size_t         level;
10642     char          *title;
10643     char          *label;
10644     struct topic  *parent;
10645     struct topic **children;
10646     uint32         kids;
10647     char          *text;
10648     size_t         len;
10649     uint32         flags;
10650     size_t         kidwid;
10651 #define HLP_MAGIC_TOPIC  1
10652     } TOPIC;
10653 
10654 static volatile struct {
10655     const char *error;
10656     const char *prox;
10657     size_t block;
10658     size_t line;
10659     } help_where = { "", NULL, 0, 0 };
10660 jmp_buf help_env;
10661 
10662 #define FAIL(why,text,here)        \
10663   {                                \
10664     help_where.error = #text;      \
10665     help_where.prox = here;        \
10666     longjmp ( help_env, (why) );   \
10667     /*LINTED E_STMT_NOT_REACHED*/  \
10668   }
10669 
10670 /*
10671  * Add to topic text.
10672  * Expands text buffer as necessary.
10673  */
10674 
10675 static void appendText (TOPIC *topic, const char *text, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
10676 {
10677 char *newt;
10678 
10679 if (!len)
10680     return;
10681 
10682 newt = (char *)realloc (topic->text, topic->len + len +1);
10683 if (!newt) {
10684 #if !defined(SUNLINT)
10685     FAIL (SCPE_MEM, No memory, NULL);
10686 #endif /* if !defined(SUNLINT) */
10687     }
10688 topic->text = newt;
10689 memcpy (newt + topic->len, text, len);
10690 topic->len +=len;
10691 newt[topic->len] = '\0';
10692 return;
10693 }
10694 
10695 /* Release memory held by a topic and its children.
10696  */
10697 static void cleanHelp (TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10698 {
10699 TOPIC *child;
10700 size_t i;
10701 
10702 FREE (topic->title);
10703 FREE (topic->text);
10704 FREE (topic->label);
10705 for (i = 0; i < topic->kids; i++) {
10706     child = topic->children[i];
10707     cleanHelp (child);
10708     FREE (child);
10709     }
10710 FREE (topic->children);
10711 return;
10712 }
10713 
10714 /* Build a help tree from a string.
10715  * Handles substitutions, formatting.
10716  */
10717 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10718                          UNIT *uptr, const char *htext, va_list ap)
10719 {
10720 char *end;
10721 size_t n, ilvl;
10722 #define VSMAX 100
10723 char *vstrings[VSMAX];
10724 size_t vsnum = 0;
10725 char * astrings[VSMAX+1];
10726 size_t asnum = 0;
10727 char *const *hblock;
10728 const char *ep;
10729 t_bool excluded = FALSE;
10730 
10731 /* variable arguments consumed table.
10732  * The scheme used allows arguments to be accessed in random
10733  * order, but for portability, all arguments must be char *.
10734  * If you try to violate this, there ARE machines that WILL break.
10735  */
10736 
10737 (void)memset (vstrings, 0, sizeof (vstrings));
10738 (void)memset (astrings, 0, sizeof (astrings));
10739 astrings[asnum++] = (char *) htext;
10740 
10741 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10742     help_where.block = hblock - astrings;
10743     help_where.line = 0;
10744     while (*htext) {
10745         const char *start;
10746 
10747         help_where.line++;
10748         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10749             if (excluded) {                     /* Excluded topic text */
10750                 while (*htext && *htext != '\n')
10751                     htext++;
10752                 if (*htext)
10753                     ++htext;
10754                 continue;
10755                 }
10756             ilvl = 1;
10757             appendText (topic, "    ", 4);      /* Basic indentation */
10758             if (*htext == '+') {                /* More for each + */
10759                 while (*htext == '+') {
10760                     ilvl++;
10761                     appendText (topic, "    ", 4);
10762                     htext++;
10763                     }
10764                 }
10765             while (*htext && *htext != '\n' && sim_isspace (*htext))
10766                 htext++;
10767             if (!*htext)                        /* Empty after removing leading spaces */
10768                 break;
10769             start = htext;
10770             while (*htext) {                    /* Process line for substitutions */
10771                 if (*htext == '%') {
10772                     appendText (topic, start, htext - start); /* Flush up to escape */
10773                     switch (*++htext) {         /* Evaluate escape */
10774                         case 'U':
10775                             if (dptr) {
10776                                 char buf[129];
10777                                 n = uptr? uptr - dptr->units: 0;
10778                                 (void)sprintf (buf, "%s%u", dptr->name, (int)n);
10779                                 appendText (topic, buf, strlen (buf));
10780                                 }
10781                             break;
10782                         case 'D':
10783                             if (dptr != NULL)
10784                                 appendText (topic, dptr->name, strlen (dptr->name));
10785                             break;
10786                         case 'S':
10787                             appendText (topic, sim_name, strlen (sim_name));
10788                             break;
10789                         case '%':
10790                             appendText (topic, "%", 1);
10791                             break;
10792                         case '+':
10793                             appendText (topic, "+", 1);
10794                             break;
10795                         default:                    /* Check for vararg # */
10796                             if (sim_isdigit (*htext)) {
10797                                 n = 0;
10798                                 while (sim_isdigit (*htext))
10799                                     n += (n * 10) + (*htext++ - '0');
10800                                 if (( *htext != 'H' && *htext != 's') ||
10801                                     n == 0 || n >= VSMAX) {
10802 #if !defined(SUNLINT)
10803                                     FAIL (SCPE_ARG, Invalid escape, htext);
10804 #endif /* if !defined(SUNLINT) */
10805                                     }
10806                                 while (n > vsnum)   /* Get arg pointer if not cached */
10807                                     vstrings[vsnum++] = va_arg (ap, char *);
10808                                 start = vstrings[n-1]; /* Insert selected string */
10809                                 if (*htext == 'H') {   /* Append as more input */
10810                                     if (asnum >= VSMAX) {
10811 #if !defined(SUNLINT)
10812                                         FAIL (SCPE_ARG, Too many blocks, htext);
10813 #endif /* if !defined(SUNLINT) */
10814                                         }
10815                                     astrings[asnum++] = (char *)start;
10816                                     break;
10817                                     }
10818                                 ep = start;
10819                                 while (*ep) {
10820                                     if (*ep == '\n') {
10821                                         ep++;       /* Segment to \n */
10822                                         appendText (topic, start, ep - start);
10823                                         if (*ep) {  /* More past \n, indent */
10824                                             size_t i;
10825                                             for (i = 0; i < ilvl; i++)
10826                                                 appendText (topic, "    ", 4);
10827                                             }
10828                                         start = ep;
10829                                         }
10830                                     else
10831                                         ep++;
10832                                     }
10833                                 appendText (topic, start, ep-start);
10834                                 break;
10835                                 }
10836 #if !defined(SUNLINT)
10837                             FAIL (SCPE_ARG, Invalid escape, htext);
10838 #endif /* if !defined(SUNLINT) */
10839                         } /* switch (escape) */
10840                     start = ++htext;
10841                     continue;                   /* Current line */
10842                     } /* if (escape) */
10843                 if (*htext == '\n') {           /* End of line, append last segment */
10844                     htext++;
10845                     appendText (topic, start, htext - start);
10846                     break;                      /* To next line */
10847                     }
10848                 htext++;                        /* Regular character */
10849                 }
10850             continue;
10851             } /* topic text line */
10852         if (sim_isdigit (*htext)) {             /* Topic heading */
10853             TOPIC **children;
10854             TOPIC *newt;
10855             char nbuf[100];
10856 
10857             n = 0;
10858             start = htext;
10859             while (sim_isdigit (*htext))
10860                 n += (n * 10) + (*htext++ - '0');
10861             if ((htext == start) || !n) {
10862 #if !defined(SUNLINT)
10863                 FAIL (SCPE_ARG, Invalid topic heading, htext);
10864 #endif /* if !defined(SUNLINT) */
10865                 }
10866             if (n <= topic->level) {            /* Find level for new topic */
10867                 while (n <= topic->level)
10868                     topic = topic->parent;
10869                 }
10870             else {
10871                 if (n > topic->level + 1) {     /* Skipping down more than 1 */
10872 #if !defined(SUNLINT)
10873                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10874 #endif /* if !defined(SUNLINT) */
10875                     }
10876                 }
10877             while (*htext && (*htext != '\n') && sim_isspace (*htext))
10878                 htext++;
10879             if (!*htext || (*htext == '\n')) {  /* Name missing */
10880 #if !defined(SUNLINT)
10881                 FAIL (SCPE_ARG, Missing topic name, htext);
10882 #endif /* if !defined(SUNLINT) */
10883                 }
10884             start = htext;
10885             while (*htext && (*htext != '\n'))
10886                 htext++;
10887             if (start == htext) {               /* Name NULL */
10888 #if !defined(SUNLINT)
10889                 FAIL (SCPE_ARG, Null topic name, htext);
10890 #endif /* if !defined(SUNLINT) */
10891                 }
10892             excluded = FALSE;
10893             if (*start == '?') {                /* Conditional topic? */
10894                 size_t n = 0;
10895                 start++;
10896                 while (sim_isdigit (*start))    /* Get param # */
10897                     n += (n * 10) + (*start++ - '0');
10898                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX) {
10899 #if !defined(SUNLINT)
10900                     FAIL (SCPE_ARG, Invalid parameter number, start);
10901 #endif /* if !defined(SUNLINT) */
10902                     }
10903                 while (n > vsnum)               /* Get arg pointer if not cached */
10904                     vstrings[vsnum++] = va_arg (ap, char *);
10905                 end = vstrings[n-1];            /* Check for True */
10906                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10907                     excluded = TRUE;            /* False, skip topic this time */
10908                     if (*htext)
10909                         htext++;
10910                     continue;
10911                     }
10912                 }
10913             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10914             if (!newt) {
10915 #if !defined(SUNLINT)
10916                 FAIL (SCPE_MEM, No memory, NULL);
10917 #endif /* if !defined(SUNLINT) */
10918                 }
10919             newt->title = (char *) malloc ((htext - start)+1);
10920             if (!newt->title) {
10921                 FREE (newt);
10922 #if !defined(SUNLINT)
10923                 FAIL (SCPE_MEM, No memory, NULL);
10924 #endif /* if !defined(SUNLINT) */
10925                 }
10926             memcpy (newt->title, start, htext - start);
10927             newt->title[htext - start] = '\0';
10928             if (*htext)
10929                 htext++;
10930 
10931             if (newt->title[0] == '$')
10932                 newt->flags |= HLP_MAGIC_TOPIC;
10933 
10934             children = (TOPIC **) realloc (topic->children,
10935                                            (topic->kids +1) * sizeof (TOPIC *));
10936             if (NULL == children) {
10937                 FREE (newt->title);
10938                 FREE (newt);
10939 #if !defined(SUNLINT)
10940                 FAIL (SCPE_MEM, No memory, NULL);
10941 #endif /* if !defined(SUNLINT) */
10942                 }
10943             topic->children = children;
10944             topic->children[topic->kids++] = newt;
10945             newt->level = n;
10946             newt->parent = topic;
10947             n = strlen (newt->title);
10948             if (n > topic->kidwid)
10949                 topic->kidwid = n;
10950             (void)sprintf (nbuf, ".%u", topic->kids);
10951             n = strlen (topic->label) + strlen (nbuf) + 1;
10952             newt->label = (char *) malloc (n);
10953             if (NULL == newt->label) {
10954                 FREE (newt->title);
10955                 topic->children[topic->kids -1] = NULL;
10956                 FREE (newt);
10957 #if !defined(SUNLINT)
10958                 FAIL (SCPE_MEM, No memory, NULL);
10959 #endif /* if !defined(SUNLINT) */
10960                 }
10961             (void)sprintf (newt->label, "%s%s", topic->label, nbuf);
10962             topic = newt;
10963             continue;
10964             } /* digits introducing a topic */
10965         if (*htext == ';') {                    /* Comment */
10966             while (*htext && *htext != '\n')
10967                 htext++;
10968             continue;
10969             }
10970 #if !defined(SUNLINT)
10971         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
10972 #endif /* if !defined(SUNLINT) */
10973         } /* htext not at end */
10974     (void)memset (vstrings, 0, VSMAX * sizeof (char *));
10975     vsnum = 0;
10976     } /* all strings */
10977 
10978 return topic;
10979 }
10980 
10981 /*
10982  * Create prompt string - top thru current topic
10983  * Add prompt at end.
10984  */
10985 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
     /* [previous][next][first][last][top][bottom][index][help] */
10986 {
10987 char *prefix;
10988 char *newp, *newt;
10989 
10990 if (topic->level == 0) {
10991     prefix = (char *) calloc (2,1);
10992     if (!prefix) {
10993 #if !defined(SUNLINT)
10994         FAIL (SCPE_MEM, No memory, NULL);
10995 #endif /* if !defined(SUNLINT) */
10996         }
10997     prefix[0] = '\n';
10998     }
10999 else
11000     prefix = helpPrompt (topic->parent, "", oneword);
11001 
11002 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
11003                         strlen (pstring) +1);
11004 if (!newp) {
11005     FREE (prefix);
11006 #if !defined(SUNLINT)
11007     FAIL (SCPE_MEM, No memory, NULL);
11008 #endif /* if !defined(SUNLINT) */
11009     }
11010 strcpy (newp, prefix);
11011 if (topic->children) {
11012     if (topic->level != 0)
11013         strcat (newp, " ");
11014     newt = (topic->flags & HLP_MAGIC_TOPIC)?
11015             topic->title+1: topic->title;
11016     if (oneword) {
11017         char *np = newp + strlen (newp);
11018         while (*newt) {
11019             *np++ = blankch (*newt)? '_' : *newt;
11020             newt++;
11021             }
11022         *np = '\0';
11023         }
11024     else
11025         strcat (newp, newt);
11026     if (*pstring && *pstring != '?')
11027         strcat (newp, " ");
11028     }
11029 strcat (newp, pstring);
11030 FREE (prefix);
11031 return newp;
11032 }
11033 
11034 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
11035 {
11036 char tbuf[CBUFSIZE];
11037 size_t i, skiplines;
11038 #if defined(_WIN32)
11039 FILE *tmp;
11040 char *tmpnam;
11041 
11042 do {
11043     int fd;
11044     tmpnam = _tempnam (NULL, "simh");
11045     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
11046     if (fd != -1) {
11047         tmp = _fdopen (fd, "w+");
11048         break;
11049         }
11050     } while (1);
11051 #else
11052 FILE *tmp = tmpfile();
11053 #endif /* if defined(_WIN32) */
11054 
11055 if (!tmp) {
11056     (void)fprintf (st, "Unable to create temporary file: %s (Error %d)\n",
11057                    xstrerror_l(errno), errno);
11058     return;
11059     }
11060 
11061 if (topic->title)
11062     (void)fprintf (st, "%s\n", topic->title+1);
11063 
11064 skiplines = 0;
11065 if (topic->title) {
11066   if (!strcmp (topic->title+1, "Registers")) {
11067       fprint_reg_help (tmp, dptr) ;
11068       skiplines = 1;
11069       }
11070   else
11071       if (!strcmp (topic->title+1, "Set commands")) {
11072           fprint_set_help (tmp, dptr);
11073           skiplines = 3;
11074           }
11075       else
11076           if (!strcmp (topic->title+1, "Show commands")) {
11077               fprint_show_help (tmp, dptr);
11078               skiplines = 3;
11079               }
11080   }
11081 rewind (tmp);
11082 if (errno) {
11083     (void)fprintf (st, "rewind: error %d\r\n", errno);
11084 }
11085 
11086 /* Discard leading blank lines/redundant titles */
11087 
11088 for (i =0; i < skiplines; i++)
11089     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
11090 
11091 while (fgets (tbuf, sizeof (tbuf), tmp)) {
11092     if (tbuf[0] != '\n')
11093         fputs ("    ", st);
11094     fputs (tbuf, st);
11095     }
11096 fclose (tmp);
11097 #if defined(_WIN32)
11098 remove (tmpnam);
11099 FREE (tmpnam);
11100 #endif /* if defined(_WIN32) */
11101 return;
11102 }
11103 /* Flatten and display help for those who say they prefer it. */
11104 
11105 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11106                                UNIT *uptr, int32 flag,
11107                                TOPIC *topic, va_list ap )
11108 {
11109 size_t i;
11110 
11111 if (topic->flags & HLP_MAGIC_TOPIC) {
11112     (void)fprintf (st, "\n%s ", topic->label);
11113     displayMagicTopic (st, dptr, topic);
11114     }
11115 else
11116     (void)fprintf (st, "\n%s %s\n", topic->label, topic->title);
11117 
11118 /*
11119  * Topic text (for magic topics, follows for explanations)
11120  * It's possible/reasonable for a magic topic to have no text.
11121  */
11122 
11123 if (topic->text)
11124     fputs (topic->text, st);
11125 
11126 for (i = 0; i < topic->kids; i++)
11127     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
11128 
11129 return SCPE_OK;
11130 }
11131 
11132 #define HLP_MATCH_AMBIGUOUS (~0u)
11133 #define HLP_MATCH_WILDCARD  (~1U)
11134 #define HLP_MATCH_NONE      0
11135 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
     /* [previous][next][first][last][top][bottom][index][help] */
11136 {
11137 size_t i, match;
11138 char cbuf[CBUFSIZE], *cptr;
11139 
11140 if (!strcmp (token, "*"))
11141     return HLP_MATCH_WILDCARD;
11142 
11143 match = 0;
11144 for (i = 0; i < topic->kids; i++) {
11145     strcpy (cbuf,topic->children[i]->title +
11146             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11147     cptr = cbuf;
11148     while (*cptr) {
11149         if (blankch (*cptr)) {
11150             *cptr++ = '_';
11151             }
11152         else {
11153             *cptr = (char)toupper (*cptr);
11154             cptr++;
11155             }
11156         }
11157     if (!strcmp (cbuf, token))      /* Exact Match */
11158         return i+1;
11159     if (!strncmp (cbuf, token, strlen (token))) {
11160         if (match)
11161             return HLP_MATCH_AMBIGUOUS;
11162         match = i+1;
11163         }
11164     }
11165 return match;
11166 }
11167 
11168 /* Main help routine */
11169 
11170 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11171                   UNIT *uptr, int32 flag,
11172                   const char *help, const char *cptr, va_list ap)
11173 {
11174 TOPIC top;
11175 TOPIC *topic = &top;
11176 int failed;
11177 size_t match;
11178 size_t i;
11179 const char *p;
11180 t_bool flat_help = FALSE;
11181 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
11182 
11183 static const char attach_help[] = { " ATTACH" };
11184 static const char  brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help.\n" };
11185 static const char onecmd_help[] = { "%s help.\n" };
11186 static const char   help_help[] = {
11187     /****|***********************80 column width guide********************************/
11188     "    To see more HELP information, type the listed subtopic name.  To move\n"
11189     "    up a level, just type <CR>.  To review the current subtopic, type \"?\".\n"
11190     "    To view all subtopics, type \"*\".  To exit type \"EXIT\", \"^C\", or \"^D\".\n\n"
11191     };
11192 
11193 (void)memset (&top, 0, sizeof(top));
11194 top.parent = &top;
11195 if ((failed = setjmp (help_env)) != 0) {
11196     (void)fprintf (stderr, "\nHELP was unable to process HELP for this device.\n"
11197                            "Error in block %u line %u: %s\n"
11198                            "%s%*.*s%s\n",
11199                    (int)help_where.block, (int)help_where.line, help_where.error,
11200                    help_where.prox ? "Near '" : "",
11201                    help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
11202                    help_where.prox ? help_where.prox : "",
11203                    help_where.prox ? "'" : "");
11204     cleanHelp (&top);
11205     return failed;
11206     }
11207 
11208 /* Compile string into navigation tree */
11209 
11210 /* Root */
11211 
11212 if (dptr) {
11213     p = dptr->name;
11214     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
11215     }
11216 else
11217     p = sim_name;
11218 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
11219 if (!top.title)
11220   {
11221     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11222                    __func__, __FILE__, __LINE__);
11223 #if defined(USE_BACKTRACE)
11224 # if defined(SIGUSR2)
11225     (void)raise(SIGUSR2);
11226     /*NOTREACHED*/ /* unreachable */
11227 # endif /* if defined(SIGUSR2) */
11228 #endif /* if defined(USE_BACKTRACE) */
11229     abort();
11230   }
11231 for (i = 0; p[i]; i++ )
11232     top.title[i] = (char)toupper (p[i]);
11233 top.title[i] = '\0';
11234 if (flag & SCP_HELP_ATTACH)
11235     strcpy (top.title+i, attach_help);
11236 
11237 top.label = (char *) malloc (sizeof ("1"));
11238 if (!top.label)
11239   {
11240     (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
11241                    __func__, __FILE__, __LINE__);
11242 #if defined(USE_BACKTRACE)
11243 # if defined(SIGUSR2)
11244     (void)raise(SIGUSR2);
11245     /*NOTREACHED*/ /* unreachable */
11246 # endif /* if defined(SIGUSR2) */
11247 #endif /* if defined(USE_BACKTRACE) */
11248     abort();
11249   }
11250 strcpy (top.label, "1");
11251 
11252 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
11253 
11254 if (flat_help) {
11255     flag |= SCP_HELP_FLAT;
11256     if (sim_ttisatty())
11257         (void)fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
11258     else
11259         (void)fprintf (st, "%s help.\n", top.title);
11260     }
11261 else
11262     (void)fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
11263 
11264 /* Add text and subtopics */
11265 
11266 (void) buildHelp (&top, dptr, uptr, help, ap);
11267 
11268 /* Go to initial topic if provided */
11269 
11270 while (cptr && *cptr) {
11271     cptr = get_glyph (cptr, gbuf, 0);
11272     if (!gbuf[0])
11273         break;
11274     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11275         (void)fprintf (st, "\n");
11276         fputs (help_help, st);
11277         break;
11278         }
11279     match =  matchHelpTopicName (topic, gbuf);
11280     if (match == HLP_MATCH_WILDCARD) {
11281         if (dptr)
11282             displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11283         cleanHelp (&top);
11284         return SCPE_OK;
11285         }
11286     if (match == HLP_MATCH_AMBIGUOUS) {
11287         (void)fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
11288         break;
11289         }
11290     if (match == HLP_MATCH_NONE) {
11291         (void)fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
11292         break;
11293         }
11294     topic = topic->children[match-1];
11295     }
11296 cptr = NULL;
11297 
11298 if (flat_help) {
11299     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11300     cleanHelp (&top);
11301     return SCPE_OK;
11302     }
11303 
11304 /* Interactive loop displaying help */
11305 
11306 while (TRUE) {
11307     char *pstring;
11308     const char *prompt[2] = {"? ", "Subtopic? "};
11309 
11310     /* Some magic topic names for help from data structures */
11311 
11312     if (topic->flags & HLP_MAGIC_TOPIC) {
11313         fputc ('\n', st);
11314         displayMagicTopic (st, dptr, topic);
11315         }
11316     else
11317         (void)fprintf (st, "\n%s\n", topic->title);
11318 
11319     /* Topic text (for magic topics, follows for explanations)
11320      * It's possible/reasonable for a magic topic to have no text.
11321      */
11322 
11323     if (topic->text)
11324         fputs (topic->text, st);
11325 
11326     if (topic->kids) {
11327         size_t w = 0;
11328         char *p;
11329         char tbuf[CBUFSIZE];
11330 
11331         (void)fprintf (st, "\n    Additional information available:\n\n");
11332         for (i = 0; i < topic->kids; i++) {
11333             strcpy (tbuf, topic->children[i]->title +
11334                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
11335             for (p = tbuf; *p; p++) {
11336                 if (blankch (*p))
11337                     *p = '_';
11338                 }
11339             w += 4 + topic->kidwid;
11340             if (w > 80) {
11341                 w = 4 + topic->kidwid;
11342                 fputc ('\n', st);
11343                 }
11344             (void)fprintf (st, "    %-*s", (int32_t)topic->kidwid, tbuf);
11345             }
11346         (void)fprintf (st, "\n\n");
11347         if (flag & SCP_HELP_ONECMD) {
11348             pstring = helpPrompt (topic, "", TRUE);
11349             (void)fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
11350             FREE (pstring);
11351             break;
11352             }
11353         }
11354 
11355     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
11356         break;
11357 
11358   reprompt:
11359     if (NULL == cptr || !*cptr) {
11360         if (topic->kids == 0)
11361             topic = topic->parent;
11362         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
11363 
11364         cptr = read_line_p (pstring+1, cbuf, sizeof (cbuf), stdin);
11365         FREE (pstring);
11366         if ((cptr != NULL) &&                   /* Got something? */
11367             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
11368              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
11369             cptr = NULL;                        /* These are EOF synonyms */
11370         }
11371 
11372     if (NULL == cptr)                           /* EOF, exit help */
11373         break;
11374 
11375     cptr = get_glyph (cptr, gbuf, 0);
11376     if (!strcmp (gbuf, "*")) {              /* Wildcard */
11377         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
11378         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
11379         }
11380     if (!gbuf[0]) {                         /* Blank, up a level */
11381         if (topic->level == 0)
11382             break;
11383         topic = topic->parent;
11384         continue;
11385         }
11386     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
11387         continue;
11388     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
11389         fputs (help_help, st);
11390         goto reprompt;
11391         }
11392     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
11393         break;
11394 
11395     /* String - look for that topic */
11396 
11397     if (!topic->kids) {
11398         (void)fprintf (st, "No additional help at this level.\n");
11399         cptr = NULL;
11400         goto reprompt;
11401         }
11402     match = matchHelpTopicName (topic, gbuf);
11403     if (match == HLP_MATCH_AMBIGUOUS) {
11404         (void)fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
11405         cptr = NULL;
11406         goto reprompt;
11407         }
11408 
11409     if (match == HLP_MATCH_NONE) {
11410         (void)fprintf (st, "Help for %s is not available\n", gbuf);
11411         cptr = NULL;
11412         goto reprompt;
11413         }
11414     /* Found, display subtopic */
11415 
11416     topic = topic->children[match-1];
11417     }
11418 
11419 /* Free structures and return */
11420 
11421 cleanHelp (&top);
11422 
11423 return SCPE_OK;
11424 }
11425 
11426 /* variable argument list shell - most commonly used */
11427 
11428 t_stat scp_help (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
11429                  UNIT *uptr, int32 flag,
11430                  const char *help, const char *cptr, ...)
11431 {
11432 t_stat r;
11433 va_list ap;
11434 
11435 va_start (ap, cptr);
11436 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
11437 va_end (ap);
11438 
11439 return r;
11440 }
11441 
11442 #if defined(_MSC_VER)
11443 # pragma warning(pop)
11444 #endif

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