root/src/simh/scp.c

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

DEFINITIONS

This source file includes following definitions.
  1. setenv
  2. unsetenv
  3. trealloc
  4. processIsTranslated
  5. strremove
  6. strtrimspace
  7. allowCores
  8. CleanDUMA
  9. main
  10. process_stdin_commands
  11. set_prompt
  12. find_cmd
  13. exit_cmd
  14. _cmd_name_compare
  15. fprint_help
  16. fprint_header
  17. fprint_reg_help_ex
  18. fprint_reg_help
  19. fprint_attach_help_ex
  20. fprint_set_help_ex
  21. fprint_set_help
  22. fprint_show_help_ex
  23. fprint_show_help
  24. fprint_brk_help_ex
  25. help_dev_help
  26. help_cmd_output
  27. help_cmd
  28. spawn_cmd
  29. echo_cmd
  30. do_cmd
  31. do_position
  32. do_cmd_label
  33. sim_sub_args
  34. sim_cmp_string
  35. assert_cmd
  36. send_cmd
  37. sim_show_send
  38. expect_cmd
  39. sim_show_expect
  40. goto_cmd
  41. return_cmd
  42. shift_cmd
  43. call_cmd
  44. on_cmd
  45. noop_cmd
  46. set_on
  47. set_verify
  48. set_message
  49. set_localopc
  50. set_quiet
  51. sim_set_environment
  52. set_cmd
  53. find_ctab
  54. find_c1tab
  55. set_dev_radix
  56. set_dev_enbdis
  57. set_unit_enbdis
  58. set_dev_debug
  59. show_cmd
  60. show_cmd_fi
  61. find_shtab
  62. show_device
  63. fprint_sep
  64. show_unit
  65. sprint_capac
  66. fprint_capac
  67. show_default_base_system_script
  68. printp
  69. strip_spaces
  70. printpq
  71. show_prom
  72. show_buildinfo
  73. show_version
  74. show_config
  75. show_log_names
  76. show_dev_logicals
  77. show_queue
  78. show_time
  79. show_break
  80. show_dev_radix
  81. show_dev_debug
  82. show_on
  83. show_mod_names
  84. show_dev_modifiers
  85. show_all_mods
  86. show_one_mod
  87. show_show_commands
  88. show_dev_show_commands
  89. brk_cmd
  90. ssh_break
  91. ssh_break_one
  92. reset_cmd
  93. reset_all
  94. reset_all_p
  95. attach_cmd
  96. scp_attach_unit
  97. attach_unit
  98. attach_err
  99. detach_cmd
  100. detach_all
  101. scp_detach_unit
  102. detach_unit
  103. sim_dname
  104. sim_uname
  105. run_cmd
  106. run_cmd_message
  107. sim_run_boot_prep
  108. fprint_stopped_gen
  109. fprint_stopped
  110. step_svc
  111. expect_svc
  112. int_handler
  113. exdep_cmd
  114. exdep_reg_loop
  115. exdep_addr_loop
  116. ex_reg
  117. get_rval
  118. dep_reg
  119. put_rval
  120. ex_addr
  121. get_aval
  122. dep_addr
  123. eval_cmd
  124. read_line
  125. read_line_p
  126. get_glyph_gen
  127. get_glyph
  128. get_glyph_nc
  129. get_glyph_quoted
  130. get_glyph_cmd
  131. sim_trim_endspc
  132. sim_isspace
  133. sim_islower
  134. sim_isalpha
  135. sim_isprint
  136. sim_isdigit
  137. sim_isgraph
  138. sim_isalnum
  139. get_uint
  140. get_range
  141. sim_decode_quoted_string
  142. sim_encode_quoted_string
  143. fprint_buffer_string
  144. find_dev
  145. find_unit
  146. sim_register_internal_device
  147. find_dev_from_unit
  148. qdisable
  149. find_reg_glob
  150. find_reg
  151. get_switches
  152. get_sim_sw
  153. get_sim_opt
  154. put_switches
  155. get_rsearch
  156. get_asearch
  157. test_search
  158. strtotv
  159. sprint_val
  160. fprint_val
  161. sim_fmt_secs
  162. sim_process_event
  163. sim_activate
  164. _sim_activate
  165. sim_activate_abs
  166. sim_activate_after
  167. _sim_activate_after
  168. sim_cancel
  169. sim_is_active
  170. sim_activate_time
  171. sim_gtime
  172. sim_qcount
  173. sim_brk_init
  174. sim_brk_fnd
  175. sim_brk_fnd_ex
  176. sim_brk_new
  177. sim_brk_set
  178. sim_brk_clr
  179. sim_brk_clrall
  180. sim_brk_show
  181. sim_brk_showall
  182. sim_brk_test
  183. sim_brk_getact
  184. sim_brk_clract
  185. sim_brk_setact
  186. sim_brk_npc
  187. sim_brk_clrspc
  188. sim_brk_message
  189. sim_set_expect
  190. sim_set_noexpect
  191. sim_exp_fnd
  192. sim_exp_clr_tab
  193. sim_exp_clr
  194. sim_exp_clrall
  195. sim_exp_set
  196. sim_exp_show_tab
  197. sim_exp_show
  198. sim_exp_showall
  199. sim_exp_check
  200. sim_send_input
  201. sim_send_clear
  202. sim_show_send_input
  203. sim_send_poll_data
  204. sim_error_text
  205. sim_string_to_stat
  206. get_dbg_verb
  207. sim_debug_prefix
  208. fprint_fields
  209. sim_debug_bits_hdr
  210. sim_debug_bits
  211. sim_printf
  212. sim_messagef
  213. _sim_debug
  214. sim_data_trace
  215. Fprintf
  216. appendText
  217. cleanHelp
  218. buildHelp
  219. helpPrompt
  220. displayMagicTopic
  221. displayFlatHelp
  222. matchHelpTopicName
  223. scp_vhelp
  224. 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 <trnsz@pobox.com>
  12  * Copyright (c) 2006-2023 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 /* Macros and data structures */
  43 
  44 #include "sim_defs.h"
  45 #include "sim_disk.h"
  46 #include "sim_tape.h"
  47 #include "sim_sock.h"
  48 #include <signal.h>
  49 #include <ctype.h>
  50 #include <time.h>
  51 #include <math.h>
  52 #if defined(_WIN32)
  53 # ifndef WIN32_LEAN_AND_MEAN
  54 #  define WIN32_LEAN_AND_MEAN
  55 # endif
  56 # if defined(_MSC_VER)
  57 #  pragma warning(push, 3)
  58 # endif
  59 # include <direct.h>
  60 # include <io.h>
  61 # include <fcntl.h>
  62 #else
  63 # include <unistd.h>
  64 # define HAVE_UNISTD 1
  65 #endif
  66 #include <sys/stat.h>
  67 #include <sys/types.h>
  68 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  69 # include <sys/resource.h>
  70 #endif /* if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
  71 #include <setjmp.h>
  72 #include <limits.h>
  73 #include <locale.h>
  74 
  75 #include "linehistory.h"
  76 
  77 #if defined(__APPLE__)
  78 # include <sys/sysctl.h>
  79 #endif /* if defined(_APPLE_) */
  80 
  81 #if ( defined(__linux__) || defined(__linux) \
  82   ||  defined(_linux)    || defined(linux) )
  83 # include <sys/sysinfo.h>
  84 # define LINUX_OS
  85 #endif
  86 
  87 #include <uv.h>
  88 
  89 #ifndef HAVE_UNISTD
  90 # undef USE_BACKTRACE
  91 #endif /* ifndef HAVE_UNISTD */
  92 
  93 #ifdef USE_BACKTRACE
  94 # include <string.h>
  95 # include <signal.h>
  96 #endif /* ifdef USE_BACKTRACE */
  97 
  98 #ifdef __HAIKU__
  99 # include <OS.h>
 100 #endif /* ifdef __HAIKU__ */
 101 
 102 #define DBG_CTR 0
 103 
 104 #include "../dps8/dps8.h"
 105 #include "../dps8/dps8_cpu.h"
 106 #include "../dps8/ver.h"
 107 #include "../dps8/sysdefs.h"
 108 
 109 #include "../dps8/dps8_iom.h"
 110 #include "../dps8/dps8_fnp2.h"
 111 
 112 #include "../decNumber/decContext.h"
 113 #include "../decNumber/decNumberLocal.h"
 114 
 115 #include "../dps8/dps8_math128.h"
 116 
 117 #include "dispatch.h"
 118 
 119 #ifndef MAX
 120 # define MAX(a,b)  (((a) >= (b)) ? (a) : (b))
 121 #endif /* ifndef MAX */
 122 
 123 #ifdef TESTING
 124 # undef FREE
 125 # define FREE(p) free(p)
 126 #endif /* ifdef TESTING */
 127 
 128 /* search logical and boolean ops */
 129 
 130 #define SCH_OR          0                               /* search logicals */
 131 #define SCH_AND         1
 132 #define SCH_XOR         2
 133 #define SCH_E           0                               /* search booleans */
 134 #define SCH_N           1
 135 #define SCH_G           2
 136 #define SCH_L           3
 137 #define SCH_EE          4
 138 #define SCH_NE          5
 139 #define SCH_GE          6
 140 #define SCH_LE          7
 141 
 142 #define MAX_DO_NEST_LVL 20                              /* DO cmd nesting level */
 143 #define SRBSIZ          1024                            /* save/restore buffer */
 144 #define SIM_BRK_INILNT  4096                            /* bpt tbl length */
 145 #define SIM_BRK_ALLTYP  0xFFFFFFFB
 146 
 147 #define UPDATE_SIM_TIME                                         \
 148     if (1) {                                                    \
 149         int32 _x;                                               \
 150         if (sim_clock_queue == QUEUE_LIST_END)                  \
 151             _x = noqueue_time;                                  \
 152         else                                                    \
 153             _x = sim_clock_queue->time;                         \
 154         sim_time = sim_time + (_x - sim_interval);              \
 155         sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
 156         if (sim_clock_queue == QUEUE_LIST_END)                  \
 157             noqueue_time = sim_interval;                        \
 158         else                                                    \
 159             sim_clock_queue->time = sim_interval;               \
 160         }                                                       \
 161     else                                                        \
 162         (void)0
 163 
 164 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
 165 
 166 #define SZ_R(rp) \
 167     (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
 168 
 169 #define SZ_LOAD(sz,v,mb,j)                                                 \
 170     if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j));        \
 171     else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
 172     else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
 173     else v = *(((t_uint64 *) mb) + ((uint32) j));
 174 
 175 #define SZ_STORE(sz,v,mb,j)                                                         \
 176     if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v;                    \
 177     else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
 178     else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
 179     else *(((t_uint64 *) mb) + ((uint32) j)) = v;
 180 
 181 #define GET_SWITCHES(cp) \
 182     if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
 183 
 184 #define GET_RADIX(val,dft)                          \
 185     if (sim_switches & SWMASK ('O')) val = 8;       \
 186     else if (sim_switches & SWMASK ('D')) val = 10; \
 187     else if (sim_switches & SWMASK ('H')) val = 16; \
 188     else val = dft;
 189 
 190 /*
 191  * The per-simulator init routine is a weak global that defaults to NULL
 192  * The other per-simulator pointers can be overridden by the init routine
 193  */
 194 
 195 t_bool sim_asynch_enabled = FALSE;
 196 t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
 197 t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
 198 extern void (*sim_vm_init) (void);
 199 extern void (*sim_vm_exit) (void);
 200 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
 201 void (*sim_vm_post) (t_bool from_scp) = NULL;
 202 CTAB *sim_vm_cmd = NULL;
 203 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
 204 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
 205 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
 206 t_value (*sim_vm_pc_value) (void) = NULL;
 207 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
 208 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
 209 
 210 /* Prototypes */
 211 
 212 /* Set and show command processors */
 213 
 214 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 215 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 216 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 217 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 218 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
 219 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
 220 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 221 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 222 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 223 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 224 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 225 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 226 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 227 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 228 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 229 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 230 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 231 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 232 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
 233 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 234 t_stat show_default_base_system_script (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 235 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 236 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 237 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 238 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 239 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 240 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
 241 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
 242 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
 243 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
 244 t_stat sim_save (FILE *sfile);
 245 t_stat sim_rest (FILE *rfile);
 246 
 247 /* Breakpoint package */
 248 
 249 t_stat sim_brk_init (void);
 250 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
 251 t_stat sim_brk_clr (t_addr loc, int32 sw);
 252 t_stat sim_brk_clrall (int32 sw);
 253 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
 254 t_stat sim_brk_showall (FILE *st, int32 sw);
 255 CONST char *sim_brk_getact (char *buf, int32 size);
 256 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
 257 char *sim_brk_clract (void);
 258 
 259 FILE *stdnul;
 260 
 261 /* Command support routines */
 262 
 263 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 264 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
 265 int32 test_search (t_value *val, SCHTAB *schptr);
 266 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
 267 int32 get_switches (const char *cptr);
 268 CONST char *get_sim_sw (CONST char *cptr);
 269 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
 270 t_value get_rval (REG *rptr, uint32 idx);
 271 void put_rval (REG *rptr, uint32 idx, t_value val);
 272 void fprint_help (FILE *st);
 273 void fprint_stopped (FILE *st, t_stat r);
 274 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
 275 void fprint_sep (FILE *st, int32 *tokens);
 276 char *read_line (char *ptr, int32 size, FILE *stream);
 277 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
 278 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
 279 char *sim_trim_endspc (char *cptr);
 280 
 281 /* Forward references */
 282 
 283 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
 284 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
 285 t_bool qdisable (DEVICE *dptr);
 286 t_stat attach_err (UNIT *uptr, t_stat stat);
 287 t_stat detach_all (int32 start_device, t_bool shutdown);
 288 t_stat assign_device (DEVICE *dptr, const char *cptr);
 289 t_stat deassign_device (DEVICE *dptr);
 290 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
 291 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
 292     REG *lowr, REG *highr, uint32 lows, uint32 highs);
 293 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
 294 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
 295 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
 296     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
 297 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
 298 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
 299     UNIT *uptr, int32 dfltinc);
 300 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
 301 t_stat step_svc (UNIT *ptr);
 302 t_stat expect_svc (UNIT *ptr);
 303 t_stat set_on (int32 flag, CONST char *cptr);
 304 t_stat set_verify (int32 flag, CONST char *cptr);
 305 t_stat set_message (int32 flag, CONST char *cptr);
 306 t_stat set_quiet (int32 flag, CONST char *cptr);
 307 t_stat set_localopc (int32 flag, CONST char *cptr);
 308 t_stat set_asynch (int32 flag, CONST char *cptr);
 309 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
 310 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
 311 void int_handler (int signal);
 312 t_stat set_prompt (int32 flag, CONST char *cptr);
 313 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
 314 t_stat sim_set_environment (int32 flag, CONST char *cptr);
 315 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
 316 
 317 /* Global data */
 318 
 319 DEVICE *sim_dflt_dev             = NULL;
 320 UNIT *sim_clock_queue            = QUEUE_LIST_END;
 321 int32 sim_interval               = 0;
 322 int32 sim_switches               = 0;
 323 FILE *sim_ofile                  = NULL;
 324 SCHTAB *sim_schrptr              = FALSE;
 325 SCHTAB *sim_schaptr              = FALSE;
 326 DEVICE *sim_dfdev                = NULL;
 327 UNIT *sim_dfunit                 = NULL;
 328 DEVICE **sim_internal_devices    = NULL;
 329 uint32 sim_internal_device_count = 0;
 330 int32 sim_opt_out                = 0;
 331 int32 sim_is_running             = 0;
 332 t_bool sim_processing_event      = FALSE;
 333 uint32 sim_brk_summ              = 0;
 334 uint32 sim_brk_types             = 0;
 335 BRKTYPTAB *sim_brk_type_desc     = NULL;         /* type descriptions */
 336 uint32 sim_brk_dflt              = 0;
 337 uint32 sim_brk_match_type;
 338 t_addr sim_brk_match_addr;
 339 char *sim_brk_act[MAX_DO_NEST_LVL];
 340 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
 341 BRKTAB **sim_brk_tab             = NULL;
 342 int32 sim_brk_ent                = 0;
 343 int32 sim_brk_lnt                = 0;
 344 int32 sim_brk_ins                = 0;
 345 int32 sim_iglock                 = 0;
 346 int32 sim_nolock                 = 0;
 347 int32 sim_quiet                  = 0;
 348 int32 sim_localopc               = 1;
 349 int32 sim_randompst              = 0;
 350 int32 sim_randstate              = 0;
 351 int32 sim_step                   = 0;
 352 int nodist                       = 0;
 353 #ifdef PERF_STRIP
 354 int32 sim_nostate                = 1;
 355 #else
 356 int32 sim_nostate                = 0;
 357 #endif /* ifndef PERF_STRIP */
 358 static double sim_time;
 359 static uint32 sim_rtime;
 360 static int32 noqueue_time;
 361 volatile int32 stop_cpu          = 0;
 362 t_value *sim_eval                = NULL;
 363 static t_value sim_last_val;
 364 static t_addr sim_last_addr;
 365 FILE *sim_log                    = NULL;         /* log file */
 366 FILEREF *sim_log_ref             = NULL;         /* log file file reference */
 367 FILE *sim_deb                    = NULL;         /* debug file */
 368 FILEREF *sim_deb_ref             = NULL;         /* debug file file reference */
 369 int32 sim_deb_switches           = 0;            /* debug switches */
 370 struct timespec sim_deb_basetime;                /* debug timestamp relative base time */
 371 char *sim_prompt                 = NULL;         /* prompt string */
 372 static FILE *sim_gotofile;                       /* the currently open do file */
 373 static int32 sim_goto_line[MAX_DO_NEST_LVL+1];   /* the current line number in the currently open do file */
 374 static int32 sim_do_echo         = 0;            /* the echo status of the currently open do file */
 375 static int32 sim_show_message    = 1;            /* the message display status of the currently open do file */
 376 static int32 sim_on_inherit      = 0;            /* the inherit status of on state and conditions when executing do files */
 377 static int32 sim_do_depth        = 0;
 378 
 379 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
 380 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
 381 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
 382 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
 383 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
 384 
 385 t_stat sim_last_cmd_stat;                        /* Command Status */
 386 
 387 static SCHTAB sim_stabr;                         /* Register search specifier */
 388 static SCHTAB sim_staba;                         /* Memory search specifier */
 389 
 390 static UNIT sim_step_unit   = { UDATA (&step_svc, 0, 0)  };
 391 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0)  };
 392 
 393 /* Tables and strings */
 394 
 395 const char save_vercur[] = "V4.1";
 396 const char save_ver40[]  = "V4.0";
 397 const char save_ver35[]  = "V3.5";
 398 const char save_ver32[]  = "V3.2";
 399 const char save_ver30[]  = "V3.0";
 400 const struct scp_error {
 401     const char *code;
 402     const char *message;
 403     } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
 404         {{"NXM",     "Address space exceeded"},
 405          {"UNATT",   "Unit not attached"},
 406          {"IOERR",   "I/O error"},
 407          {"CSUM",    "Checksum error"},
 408          {"FMT",     "Format error"},
 409          {"NOATT",   "Unit not attachable"},
 410          {"OPENERR", "File open error"},
 411          {"MEM",     "Memory exhausted"},
 412          {"ARG",     "Invalid argument"},
 413          {"STEP",    "Step expired"},
 414          {"UNK",     "Unknown command"},
 415          {"RO",      "Read only argument"},
 416          {"INCOMP",  "Command not completed"},
 417          {"STOP",    "Simulation stopped"},
 418          {"EXIT",    "Goodbye"},
 419          {"TTIERR",  "Console input I/O error"},
 420          {"TTOERR",  "Console output I/O error"},
 421          {"EOF",     "End of file"},
 422          {"REL",     "Relocation error"},
 423          {"NOPARAM", "No settable parameters"},
 424          {"ALATT",   "Unit already attached"},
 425          {"TIMER",   "Hardware timer error"},
 426          {"SIGERR",  "Signal handler setup error"},
 427          {"TTYERR",  "Console terminal setup error"},
 428          {"SUB",     "Subscript out of range"},
 429          {"NOFNC",   "Command not allowed"},
 430          {"UDIS",    "Unit disabled"},
 431          {"NORO",    "Read only operation not allowed"},
 432          {"INVSW",   "Invalid switch"},
 433          {"MISVAL",  "Missing value"},
 434          {"2FARG",   "Too few arguments"},
 435          {"2MARG",   "Too many arguments"},
 436          {"NXDEV",   "Non-existent device"},
 437          {"NXUN",    "Non-existent unit"},
 438          {"NXREG",   "Non-existent register"},
 439          {"NXPAR",   "Non-existent parameter"},
 440          {"NEST",    "Nested DO command limit exceeded"},
 441          {"IERR",    "Internal error"},
 442          {"MTRLNT",  "Invalid magtape record length"},
 443          {"LOST",    "Console Telnet connection lost"},
 444          {"TTMO",    "Console Telnet connection timed out"},
 445          {"STALL",   "Console Telnet output stall"},
 446          {"AFAIL",   "Assertion failed"},
 447          {"INVREM",  "Invalid remote console command"},
 448          {"NOTATT",  "Not attached"},
 449          {"EXPECT",  "Expect matched"},
 450          {"REMOTE",  "Remote console command"},
 451     };
 452 
 453 const size_t size_map[] = { sizeof (int8),
 454     sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
 455     , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
 456 };
 457 
 458 const t_value width_mask[] = { 0,
 459     0x1, 0x3, 0x7, 0xF,
 460     0x1F, 0x3F, 0x7F, 0xFF,
 461     0x1FF, 0x3FF, 0x7FF, 0xFFF,
 462     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
 463     0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
 464     0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
 465     0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
 466     0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
 467     0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
 468     0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
 469     0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
 470     0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
 471     0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
 472     0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
 473     0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
 474     0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
 475     };
 476 
 477 static const char simh_help[] =
 478        /***************** 80 character line width template *************************/
 479       "1Commands\n"
 480 #define HLP_RESET       "*Commands Resetting Devices"
 481        /***************** 80 character line width template *************************/
 482       "2Resetting Devices\n"
 483       " The `RESET` command (*abbreviated* `RE`) resets a device or the entire\n"
 484       " simulator to a predefined condition.  If the switch \"`-p`\" is specified,\n"
 485       " the device is reset to its initial power-on state:\n\n"
 486       "++RESET                  resets all devices\n"
 487       "++RESET -p               power-cycle all devices\n"
 488       "++RESET ALL              resets all devices\n"
 489       "++RESET <device>         resets the specified <device>\n\n"
 490       " * Typically, `RESET` *aborts* in-progress I/O operations, *clears* any\n"
 491       " interrupt requests, and returns the device to a quiescent state.\n\n"
 492       " * It does **NOT** clear the main memory or affect associated I/O\n"
 493       " connections.\n"
 494 #define HLP_EXAMINE     "*Commands Examining_and_Changing_State"
 495 #define HLP_IEXAMINE    "*Commands Examining_and_Changing_State"
 496 #define HLP_DEPOSIT     "*Commands Examining_and_Changing_State"
 497 #define HLP_IDEPOSIT    "*Commands Examining_and_Changing_State"
 498        /***************** 80 character line width template *************************/
 499       "2Examining and Changing State\n"
 500       " There are four commands to examine and change state:\n\n"
 501       " * `EXAMINE` (*abbreviated* `E`) examines state\n"
 502       " * `DEPOSIT` (*abbreviated* `D`) changes state\n"
 503       " * `IEXAMINE` (\"interactive examine\", *abbreviated* `IE`) examines state\n"
 504       "    and allows the user to interactively change it\n"
 505       " * `IDEPOSIT` (interactive deposit, *abbreviated* `ID`) allows the user to\n"
 506       "    interactively change state\n\n"
 507       " All four commands take the form:\n\n"
 508       "++command {modifiers} <object list>\n\n"
 509       " The `DEPOSIT` command requires the deposit value at the end of the command.\n\n"
 510       " There are four kinds of modifiers: **switches**, **device/unit name**,\n"
 511       " **search specifier**, and for `EXAMINE`, **output file**.\n\n"
 512       " * **Switches** have been described previously.\n"
 513       " * A **device/unit name** identifies the device and unit whose address\n"
 514       " space is to be examined or modified. If no device is specified, the CPU\n"
 515       " main memory is selected. If a device but no unit is specified, unit `0`\n"
 516       " of the specified device is selected automatically.\n"
 517       " * The **search specifier** provides criteria for testing addresses or\n"
 518       " registers to see if they should be processed.  The search specifier\n"
 519       " consists of a \"<`logical operator`>\", a \"<`relational operator`>\", or\n"
 520       " both, optionally separated by spaces:\n\n"
 521       "++{ < logical op >  < value > }  < relational op >  < value >\n\n"
 522        /***************** 80 character line width template *************************/
 523       " * * The \"<`logical operator`>\" may be \"`&`\" (*and*), \"`|`\" (*or*),\n"
 524       " or \"`^`\" (*exclusive or*), and the \"<`relational operator`>\" may\n"
 525       " be \"`=`\" or \"`==`\" (*equal*), \"`!`\" or \"`!=`\" (*not\n"
 526       " equal*), \">=\" (*greater than or equal*), \">\" (*greater\n"
 527       " than*), \"<=\" (*less than or equal*), or \"<\" (*less than*).\n"
 528       " * * If any \"<`logical operator`>\" is specified without\n"
 529       " a \"<`relational operator`>\", it is ignored.\n"
 530       " * * If any \"<`relational operator`>\" is specified without\n"
 531       " a \"<`logical operator`>\", no logical operation is performed.\n"
 532       " * * All comparisons are unsigned.\n\n"
 533       " * The **output file** modifier redirects the command output to a file\n"
 534       " instead of the console.  The **output file** modifier is specified with\n"
 535       " the \"`@`\" (*commercial-at*) character, followed by a valid file name.\n\n"
 536       " **NOTE**: Modifiers may be specified in any order.  If multiple\n"
 537       " modifiers of the same type are specified, later modifiers override earlier\n"
 538       " modifiers. If the **device/unit name** comes *after* the search specifier,\n"
 539       " the search values will interpreted in the *radix of the CPU*, rather than\n"
 540       " of the device/unit.\n\n"
 541       " The \"<`object list`>\" argument consists of one or more of the following,\n"
 542       " separated by commas:\n\n"
 543        /***************** 80 character line width template *************************/
 544       "++register                the specified register\n"
 545       "++register[sub1-sub2]     the specified register array locations,\n"
 546       "++++++++                  starting at location sub1 up to and\n"
 547       "++++++++                  including location sub2\n"
 548       "++register[sub1/length]   the specified register array locations,\n"
 549       "++++++++                  starting at location sub1 up to but\n"
 550       "++++++++                  not including sub1+length\n"
 551       "++register[ALL]           all locations in the specified register\n"
 552       "++++++++                  array\n"
 553       "++register1-register2     all the registers starting at register1\n"
 554       "++++++++                  up to and including register2\n"
 555       "++address                 the specified location\n"
 556       "++address1-address2       all locations starting at address1 up to\n"
 557       "++++++++                  and including address2\n"
 558       "++address/length          all location starting at address up to\n"
 559       "++++++++                  but not including address+length\n"
 560       "++STATE                   all registers in the device\n"
 561       "++ALL                     all locations in the unit\n"
 562       "++$                       the last value displayed by an EXAMINE\n"
 563       "++++++++                  command interpreted as an address\n"
 564       "3Switches\n"
 565       "4Formatting Control\n"
 566       " Switches can be used to control the format of the displayed information:\n\n"
 567        /***************** 80 character line width template *************************/
 568       "5-a\n"
 569       " display as ASCII\n"
 570       "5-c\n"
 571       " display as character string\n"
 572       "5-m\n"
 573       " display as instruction mnemonics\n"
 574       "5-o\n"
 575       " display as octal\n"
 576       "5-d\n"
 577       " display as decimal\n"
 578       "5-h\n"
 579       " display as hexadecimal\n\n"
 580       "3Examples\n"
 581       "++ex 1000-1100                examine 1000 to 1100\n"
 582       "++de PC 1040                  set PC to 1040\n"
 583       "++ie 40-50                    interactively examine 40:50\n"
 584       "++ie >1000 40-50              interactively examine the subset\n"
 585       "+++++++++                     of locations 40:50 that are >1000\n"
 586       "++ex rx0 50060                examine 50060, RX unit 0\n"
 587       "++ex rx sbuf[3-6]             examine SBUF[3] to SBUF[6] in RX\n"
 588       "++de all 0                    set main memory to 0\n"
 589       "++de &77>0 0                  set all addresses whose low order\n"
 590       "+++++++++                     bits are non-zero to 0\n"
 591       "++ex -m @memdump.txt 0-7777   dump memory to file\n\n"
 592       " * **NOTE**: To terminate an interactive command, simply type any bad value\n"
 593       "           (*e.g.* `XYZ`) when input is requested.\n"
 594 #define HLP_EVALUATE    "*Commands Evaluating_Instructions"
 595        /***************** 80 character line width template *************************/
 596       "2Evaluating Instructions\n"
 597       " The `EVAL` command evaluates a symbolic expression and returns the\n"
 598       " equivalent numeric value.\n\n"
 599        /***************** 80 character line width template *************************/
 600       "2Running A Simulated Program\n"
 601 #define HLP_RUN         "*Commands Running_A_Simulated_Program RUN"
 602       "3RUN\n"
 603       " The `RUN` command (*abbreviated* `RU`) resets all devices, deposits its\n"
 604       " argument, if given, in the PC (program counter), and starts execution.\n"
 605       " If no argument is given execution starts at the current PC.\n"
 606 #define HLP_GO          "*Commands Running_A_Simulated_Program GO"
 607       "3GO\n"
 608       " The `GO` command does *not* reset devices, deposits its argument (if\n"
 609       " given) in the PC, and starts execution.  If no argument is given,\n"
 610       " execution starts at the current PC (program counter).\n"
 611 #define HLP_CONTINUE    "*Commands Running_A_Simulated_Program Continuing_Execution"
 612       "3Continuing Execution\n"
 613       " The `CONTINUE` command (*abbreviated* `CONT` or `CO`) resumes execution\n"
 614       " (if execution was stopped, possibly due to hitting a breakpoint) at the\n"
 615       " current program counter without resetting any devices.\n"
 616 #define HLP_STEP        "*Commands Running_A_Simulated_Program Step_Execution"
 617       "3Step Execution\n"
 618       " The `STEP` command (*abbreviated* `S`) resumes execution at the current\n"
 619       " PC for the number of instructions given by its argument.  If no argument\n"
 620       " is supplied, one instruction is executed.\n"
 621       "4Switches\n"
 622       "5`-T`\n"
 623       " If the `STEP` command is invoked with the \"`-T`\" switch, the step\n"
 624       " command will cause execution to run for *microseconds* rather than\n"
 625       " instructions.\n"
 626 #define HLP_NEXT        "*Commands Running_A_Simulated_Program NEXT"
 627       "3NEXT\n"
 628       " The `NEXT` command (*abbreviated* `N`) resumes execution at the current PC\n"
 629       " for one instruction, attempting to execute *through* subroutine calls.\n"
 630       " If the next instruction to be executed is *not* a subroutine call, then\n"
 631       " one instruction is executed.\n"
 632 #define HLP_BOOT        "*Commands Running_A_Simulated_Program Booting_the_system"
 633       "3Booting the system\n"
 634       " The `BOOT` command (*abbreviated* `BO`) resets all devices and bootstraps\n"
 635       " the device and unit given by its argument. If no unit is supplied,\n"
 636       " unit `0` is bootstrapped.  The specified unit must be `ATTACH`'ed.\n\n"
 637       " When booting Multics, the boot device should always be `iom0`.\n"
 638       " Assuming a tape is attached to the `tape0` device, it will be bootstrapped\n"
 639       " into memory and the system will transfer control to the boot record.\n\n"
 640       " **Example**\n\n"
 641       "++; Boot Multics using iom0\n"
 642       "++boot iom0\n\n"
 643        /***************** 80 character line width template *************************/
 644       "2Stopping The Simulator\n"
 645       " The simulator runs until the simulated hardware encounters an error, or\n"
 646       " until the user forces a stop condition.\n"
 647       "3Simulator Detected Stop Conditions\n"
 648       " These simulator-detected conditions stop simulation:\n\n"
 649       "++-  HALT instruction.  If a HALT instruction is decoded, simulation stops.\n\n"
 650       "++-  I/O error.  If an I/O error occurs during simulation of an I/O\n"
 651       "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
 652       "+++usually stops.\n\n"
 653       "++-  Processor condition.  Certain processor conditions can stop\n"
 654       "+++the simulation.\n"
 655       "3User Specified Stop Conditions\n"
 656       " Typing the interrupt character stops simulation.  The interrupt character\n"
 657       " is defined by the `WRU` (*Where aRe yoU*) console option, and is initially\n"
 658       " set to `005` (`^E`).\n\n"
 659        /***************** 80 character line width template *************************/
 660 #define HLP_BREAK       "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 661 #define HLP_NOBREAK     "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
 662       "4Breakpoints\n"
 663       " The simulator offers breakpoint capability for debugging. Users may define\n"
 664       " breakpoints of different types, identified by letter (for example, `E`\n"
 665       " for *execution*, `R` for *read*, `W` for *write*, etc).\n\n"
 666       " Associated with each breakpoint is a count and, optionally, one or more\n"
 667       " actions.  Each time a breakpoint occurs, the associated count\n"
 668       " is *decremented*.  If the count is less than or equal to `0`, the breakpoint\n"
 669       " occurs; otherwise, it is deferred.  When the breakpoint occurs, any\n"
 670       " optional actions are automatically executed.\n\n"
 671       " A breakpoint is set by the `BREAK` (or `SET BREAK`) command:\n\n"
 672       "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
 673       " If no type is specified, the default breakpoint type (`E`, *execution*) is\n"
 674       " used.  If no address range is specified, the current PC is used.  As\n"
 675       " with `EXAMINE` and `DEPOSIT`, an address range may be a single address, a\n"
 676       " range of addresses low-high, or a relative range of address/length.\n"
 677        /***************** 80 character line width template *************************/
 678       "5Displaying Breakpoints\n"
 679       " Currently set breakpoints can be displayed with the `SHOW BREAK` command:\n\n"
 680       "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
 681       " Locations with breakpoints of the specified type are displayed.\n\n"
 682       " The \"`-C`\" switch displays the selected breakpoint(s) formatted as\n"
 683       " commands which may be subsequently used to establish the same\n"
 684       " breakpoint(s).\n\n"
 685       "5Removing Breakpoints\n"
 686       " Breakpoints can be cleared by the `NOBREAK` or the `SET NOBREAK` commands.\n"
 687       "5Examples\n"
 688       " The following examples illustrate breakpoint usage:\n\n"
 689       "++BREAK                      set E break at current PC\n"
 690       "++BREAK -e 200               set E break at 200\n"
 691       "++BREAK 2000/2[2]            set E breaks at 2000,2001 with count = 2\n"
 692       "++BREAK 100;EX AC;D MQ 0     set E break at 100 with actions EX AC and\n"
 693       "+++++++++D MQ 0\n"
 694       "++BREAK 100;                 delete action on break at 100\n\n"
 695        /***************** 80 character line width template *************************/
 696       "2Connecting and Disconnecting Devices\n"
 697       " Units are simulated as files on the host file system.  Before using any\n"
 698       " simulated unit, the user must specify the file to be accessed by that unit.\n"
 699 #define HLP_ATTACH      "*Commands Connecting_and_Disconnecting_Devices Attaching_devices"
 700       "3Attaching devices\n"
 701       " The `ATTACH` (*abbreviation* `AT`) command associates a unit and a file:\n\n"
 702       "++ATTACH <unit> <filename>\n\n"
 703       " Some devices have more detailed or specific help available with:\n\n"
 704       "++HELP <device> ATTACH\n\n"
 705       "4Switches\n"
 706       "5-n\n"
 707       " If the \"`-n`\" switch is specified when `ATTACH` is executed, a new\n"
 708       " file will be created when the filename specified does not exist, or an\n"
 709       " existing file will have it's size truncated to zero, and an appropriate\n"
 710       " message is printed.\n"
 711       "5-e\n"
 712       " If the file does not exist, and the \"`-e`\" switch *was not* specified,\n"
 713       " a new file is created, and an appropriate message is printed.  If\n"
 714       " the \"`-e`\" switch *was* specified, a new file is *not* created, and an\n"
 715       " error message is printed.\n"
 716       "5-r\n"
 717       " If the \"`-r`\" switch is specified, or the file is write protected by\n"
 718       " host operating system, `ATTACH` tries to open the file in read only mode.\n"
 719       " If the file does not exist, or the unit does not support read only\n"
 720       " operation, an error occurs.  Input-only devices, such as card readers, or\n"
 721       " storage devices with write locking switches, such as disks or tapes,\n"
 722       " support read only operation - other devices do not.  If a file is\n"
 723       " attached read only, its contents can be examined but not modified.\n"
 724       "5-q\n"
 725       " If the \"`-q`\" switch is specified when creating a new file (\"`-n`\")\n"
 726       " or opening one read only (\"`-r`\"), the message announcing this fact\n"
 727       " is suppressed.\n"
 728       "5-f\n"
 729       " For simulated magnetic tapes, the `ATTACH` command can specify the format\n"
 730       " of the attached tape image file:\n\n"
 731       "++ATTACH -f <tape_unit> <format> <filename>\n\n"
 732       " * The currently supported magnetic tape image file formats are:\n\n"
 733       " |                  |                                                      |\n"
 734       " | ----------------:|:---------------------------------------------------- |\n"
 735       " | \"**`SIMH`**\"   | The **SIMH** / **DPS8M** native portable tape format |\n"
 736       " | \"**`E11`**\"    | The *D Bit* **Ersatz-11** simulator format           |\n"
 737       " | \"**`TPC`**\"    | The **TPC** format (*used by _SIMH_ prior to V2.3*)  |\n"
 738       " | \"**`P7B`**\"    | The **Paul Pierce** `7`-track tape archive format    |\n\n"
 739        /***************** 80 character line width template *************************/
 740       " * The default tape format can also be specified with the `SET` command\n"
 741       " prior to using the `ATTACH` command:\n\n"
 742       "++SET <tape_unit> FORMAT=<format>\n"
 743       "++ATTACH <tape_unit> <filename>\n\n"
 744       " * The format of a currently attached tape image can be displayed with\n"
 745       "   the `SHOW FORMAT` command:\n\n"
 746       "++SHOW <unit> FORMAT\n\n"
 747       " **Examples**\n\n"
 748       " The following example illustrates common `ATTACH` usage:\n"
 749       "++; Associate the tape image file \"12.7MULTICS.tap\" with the tape0 unit\n"
 750       "++; in read-only mode, where tape0 corresponds to the first tape device.\n"
 751       "++ATTACH -r tape0 12.7MULTICS.tap\n\n"
 752       "++; Associate the disk image file \"root.dsk\" with the disk0 unit.\n"
 753       "++; The disk0 unit corresponds to the first disk device.\n"
 754       "++ATTACH disk0 root.dsk\n\n"
 755        /***************** 80 character line width template *************************/
 756 #define HLP_DETACH      "*Commands Connecting_and_Disconnecting_Devices Detaching_devices"
 757       "3Detaching devices\n"
 758       " The `DETACH` (*abbreviation* `DET`) command breaks the association between\n"
 759       " a unit and its backing file or device:\n\n"
 760       "++DETACH ALL             Detach all units\n"
 761       "++DETACH <unit>          Detach specified unit\n\n"
 762       " * **NOTE:** The `EXIT` command performs an automatic `DETACH ALL`.\n"
 763 #define HLP_SET         "*Commands SET"
 764       "2SET\n"
 765        /***************** 80 character line width template *************************/
 766 #define HLP_SET_LOG    "*Commands SET Logging"
 767       "3Logging\n"
 768       " Interactions with the simulator session can be recorded to a log file.\n\n"
 769       "+SET LOG log_file            Specify the log destination\n"
 770       "++++++++                     (STDOUT, DEBUG, or filename)\n"
 771       "+SET NOLOG                   Disables any currently active logging\n"
 772       "4Switches\n"
 773       "5`-N`\n"
 774       " By default, log output is written at the *end* of the specified log file.\n"
 775       " A new log file can created if the \"`-N`\" switch is used on the command\n"
 776       " line.\n\n"
 777       "5`-B`\n"
 778       " By default, log output is written in *text* mode.  The log file can be\n"
 779       " opened for *binary* mode writing if the \"`-B`\" switch is used on the\n"
 780       " command line.\n"
 781 #define HLP_SET_DEBUG  "*Commands SET Debug_Messages"
 782        /***************** 80 character line width template *************************/
 783       "3Debug Messages\n"
 784       "+SET DEBUG debug_file        Specify the debug destination\n"
 785       "++++++++                     (STDOUT, STDERR, LOG, or filename)\n"
 786       "+SET NODEBUG                 Disables any currently active debug output\n"
 787       "4Switches\n"
 788       " Debug message output contains a timestamp which indicates the number of\n"
 789       " simulated instructions which have been executed prior to the debug event.\n\n"
 790       " Debug message output can be enhanced to contain additional, potentially\n"
 791       " useful information.\n\n\n"
 792       " **NOTE**: If neither \"`-T`\" or \"`-A`\" is specified, \"`-T`\" is implied.\n"
 793       "5-T\n"
 794       " The \"`-T`\" switch causes debug output to contain a time of day displayed\n"
 795       " as `hh:mm:ss.msec`.\n"
 796       "5-A\n"
 797       " The \"`-A`\" switch causes debug output to contain a time of day displayed\n"
 798       " as `seconds.msec`.\n"
 799       "5-R\n"
 800       " The \"`-R`\" switch causes timing to be relative to the start of debugging.\n"
 801       "5-P\n"
 802       " The \"`-P`\" switch adds the output of the PC (program counter) to each\n"
 803       " debug message.\n"
 804       "5-N\n"
 805       " The \"`-N`\" switch causes a new (empty) file to be written to.\n"
 806       " (The default is to append to an existing debug log file).\n"
 807       "5-D\n"
 808       " The \"`-D`\" switch causes data blob output to also display the data\n"
 809       " as **`RADIX-50`** characters.\n"
 810       "5-E\n"
 811       " The \"`-E`\" switch causes data blob output to also display the data\n"
 812       " as \"**EBCDIC**\" characters.\n"
 813        /***************** 80 character line width template *************************/
 814 #define HLP_SET_ENVIRON "*Commands SET Environment_Variables"
 815       "3Environment Variables\n"
 816       "+SET ENVIRONMENT NAME=val    Set environment variable\n"
 817       "+SET ENVIRONMENT NAME        Clear environment variable\n"
 818 #define HLP_SET_ON      "*Commands SET Command_Status_Trap_Dispatching"
 819       "3Command Status Trap Dispatching\n"
 820       "+SET ON                      Enables error checking command execution\n"
 821       "+SET NOON                    Disables error checking command execution\n"
 822       "+SET ON INHERIT              Enables inheritance of ON state and actions\n"
 823       "+SET ON NOINHERIT            Disables inheritance of ON state and actions\n"
 824 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
 825       "3Command Execution Display\n"
 826       "+SET VERIFY                  Enables display of processed script commands\n"
 827       "+SET VERBOSE                 Enables display of processed script commands\n"
 828       "+SET NOVERIFY                Disables display of processed script commands\n"
 829       "+SET NOVERBOSE               Disables display of processed script commands\n"
 830 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
 831       "3Command Error Status Display\n"
 832       "+SET MESSAGE                 Re-enables display of script error messages\n"
 833       "+SET NOMESSAGE               Disables display of script error messages\n"
 834 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
 835       "3Command Output Display\n"
 836       "+SET QUIET                   Disables suppression of some messages\n"
 837       "+SET NOQUIET                 Re-enables suppression of some messages\n"
 838 #define HLP_SET_LOCALOPC "*Commands SET Local_Operator_Console"
 839       "3Local Operator Console\n"
 840       "+SET LOCALOPC                Enables local operator console\n"
 841       "+SET NOLOCALOPC              Disables local operator console\n"
 842 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
 843       "3Command Prompt\n"
 844       "+SET PROMPT \"string\"         Sets an alternate simulator prompt string\n"
 845       "3Device and Unit Settings\n"
 846       "+SET <dev> OCT|DEC|HEX       Set device display radix\n"
 847       "+SET <dev> ENABLED           Enable device\n"
 848       "+SET <dev> DISABLED          Disable device\n"
 849       "+SET <dev> DEBUG{=arg}       Set device debug flags\n"
 850       "+SET <dev> NODEBUG={arg}     Clear device debug flags\n"
 851       "+SET <dev> arg{,arg...}      Set device parameters\n"
 852       "+SET <unit> ENABLED          Enable unit\n"
 853       "+SET <unit> DISABLED         Disable unit\n"
 854       "+SET <unit> arg{,arg...}     Set unit parameters\n"
 855       "+HELP <dev> SET              Displays any device specific SET commands\n"
 856       " \n\n"
 857       " See the Omnibus documentation for a complete SET command reference.\n"
 858        /***************** 80 character line width template *************************/
 859 #define HLP_SHOW        "*Commands SHOW"
 860       "2SHOW\n"
 861       "+SH{OW} B{UILDINFO}               Show build-time compilation information\n"
 862       "+SH{OW} CL{OCKS}                  Show wall clock and timer information\n"
 863       "+SH{OW} C{ONFIGURATION}           Show simulator configuration\n"
 864       "+SH{OW} D{EFAULT_BASE_SYSTEM}     Show default base system script\n"
 865       "+SH{OW} DEV{ICES}                 Show devices\n"
 866       "+SH{OW} M{ODIFIERS}               Show SET commands for all devices\n"
 867       "+SH{OW} O{N}                      Show ON condition actions\n"
 868       "+SH{OW} P{ROM}                    Show CPU ID PROM initialization data\n"
 869       "+SH{OW} Q{UEUE}                   Show event queue\n"
 870       "+SH{OW} S{HOW}                    Show SHOW commands for all devices\n"
 871       "+SH{OW} T{IME}                    Show simulated timer\n"
 872       "+SH{OW} VE{RSION}                 Show simulator version\n"
 873       "+H{ELP} <dev> SHOW                Show device-specific SHOW commands\n"
 874       "+SH{OW} <dev> {arg,...}           Show device parameters\n"
 875       "+SH{OW} <dev> DEBUG               Show device debug flags\n"
 876       "+SH{OW} <dev> MODIFIERS           Show device modifiers\n"
 877       "+SH{OW} <dev> RADIX               Show device display radix\n"
 878       "+SH{OW} <dev> SHOW                Show device SHOW commands\n"
 879       "+SH{OW} <unit> {arg,...}          Show unit parameters\n\n"
 880       " See the Omnibus documentation for a complete SHOW command reference.\n\n"
 881 #define HLP_SHOW_CONFIG         "*Commands SHOW"
 882 #define HLP_SHOW_DEVICES        "*Commands SHOW"
 883 #define HLP_SHOW_FEATURES       "*Commands SHOW"
 884 #define HLP_SHOW_QUEUE          "*Commands SHOW"
 885 #define HLP_SHOW_TIME           "*Commands SHOW"
 886 #define HLP_SHOW_MODIFIERS      "*Commands SHOW"
 887 #define HLP_SHOW_NAMES          "*Commands SHOW"
 888 #define HLP_SHOW_SHOW           "*Commands SHOW"
 889 #define HLP_SHOW_VERSION        "*Commands SHOW"
 890 #define HLP_SHOW_BUILDINFO      "*Commands SHOW"
 891 #define HLP_SHOW_PROM           "*Commands SHOW"
 892 #define HLP_SHOW_DBS            "*Commands SHOW"
 893 #define HLP_SHOW_DEFAULT        "*Commands SHOW"
 894 #define HLP_SHOW_CONSOLE        "*Commands SHOW"
 895 #define HLP_SHOW_REMOTE         "*Commands SHOW"
 896 #define HLP_SHOW_BREAK          "*Commands SHOW"
 897 #define HLP_SHOW_LOG            "*Commands SHOW"
 898 #define HLP_SHOW_DEBUG          "*Commands SHOW"
 899 #define HLP_SHOW_CLOCKS         "*Commands SHOW"
 900 #define HLP_SHOW_ON             "*Commands SHOW"
 901 #define HLP_SHOW_SEND           "*Commands SHOW"
 902 #define HLP_SHOW_EXPECT         "*Commands SHOW"
 903 #define HLP_HELP                "*Commands HELP"
 904        /***************** 80 character line width template *************************/
 905       "2HELP\n"
 906       "+H{ELP}                      Show this message\n"
 907       "+H{ELP} <command>            Show help for command\n"
 908       "+H{ELP} <dev>                Show help for device\n"
 909       "+H{ELP} <dev> REGISTERS      Show help for device register variables\n"
 910       "+H{ELP} <dev> ATTACH         Show help for device specific ATTACH command\n"
 911       "+H{ELP} <dev> SET            Show help for device specific SET commands\n"
 912       "+H{ELP} <dev> SHOW           Show help for device specific SHOW commands\n"
 913       "+H{ELP} <dev> <command>      Show help for device specific <command> command\n"
 914        /***************** 80 character line width template *************************/
 915       "2Altering The Simulated Configuration\n"
 916       " The \"SET <device> DISABLED\" command removes a device from the configuration.\n"
 917       " A `DISABLED` device is invisible to running programs.  The device can still\n"
 918       " be `RESET`, but it cannot be `ATTACH`ed, `DETACH`ed, or `BOOT`ed.\n\n"
 919       " The \"SET <device> ENABLED\" command restores a disabled device to a\n"
 920       " configuration.\n\n"
 921       " Most multi-unit devices allow units to be enabled or disabled:\n\n"
 922       "++SET <unit> ENABLED\n"
 923       "++SET <unit> DISABLED\n\n"
 924       " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
 925        /***************** 80 character line width template *************************/
 926 #define HLP_DO          "*Commands Executing_Command_Files Processing_Command_Files"
 927       "2Executing Command Files\n"
 928       "3Processing Command Files\n"
 929       " The simulator can invoke another script file with the \"`DO`\" command:\n\n"
 930       "++DO <filename> {arguments...}       execute commands in specified file\n\n"
 931       " The \"`DO`\" command allows command files to contain substitutable\n"
 932       " arguments. The string \"`%%n`\", where \"`n`\" is a number\n"
 933       " between \"`1`\" and \"`9`\", is replaced with argument \"`n`\" from\n"
 934       " the \"`DO`\" command line. (*i.e.* \"`%%0`\", \"`%%1`\", \"`%%2`\", etc.).\n"
 935       " The string \"`%%0`\" is replaced with \"<`filename`>\".\n The\n"
 936       " sequences \"`\\%%`\" and \"`\\\\`\" are replaced with the literal\n"
 937       " characters \"`%%`\" and \"`\\`\", respectively. Arguments with spaces must\n"
 938       " be enclosed in matching single or double quotation marks.\n\n"
 939       " * **NOTE**: Nested \"`DO`\" commands are supported, up to ten invocations\n"
 940       " deep.\n\n"
 941       "4Switches\n"
 942       "5`-v`\n\n"
 943       " If the switch \"`-v`\" is specified, commands in the command file are\n"
 944       " echoed *before* they are executed.\n\n"
 945       "5`-e`\n\n"
 946       " If the switch \"`-e`\" is specified, command processing (including nested\n"
 947       " command invocations) will be aborted if any command error is encountered.\n"
 948       " (A simulation stop **never** aborts processing; use `ASSERT` to catch\n"
 949       " unexpected stops.) Without this switch, all errors except `ASSERT` failures\n"
 950       " will be ignored, and command processing will continue.\n\n"
 951       "5`-o`\n\n"
 952       " If the switch \"`-o`\" is specified, the `ON` conditions and actions from\n"
 953       " the calling command file will be inherited by the command file being\n"
 954       " invoked.\n"
 955       "5`-q`\n\n"
 956       " If the switch \"`-q`\" is specified, *quiet mode* will be explicitly\n"
 957       " enabled for the called command file, otherwise the *quiet mode* setting\n"
 958       " is inherited from the calling context.\n"
 959        /***************** 80 character line width template *************************/
 960 #define HLP_GOTO        "*Commands Executing_Command_Files GOTO"
 961       "3GOTO\n"
 962       " Commands in a command file execute in sequence until either an error\n"
 963       " trap occurs (when a command completes with an error status), or when an\n"
 964       " explict request is made to start command execution elsewhere with\n"
 965       " the `GOTO` command:\n\n"
 966       "++GOTO <label>\n\n"
 967       " * Labels are lines in a command file which the first non-whitespace\n"
 968       " character is a \"`:`\".\n"
 969       " * The target of a `GOTO` is the first matching label in the current `DO`\n"
 970       " command file which is encountered.\n\n"
 971       " **Example**\n\n"
 972       " The following example illustrates usage of the `GOTO` command (by\n"
 973       " creating an infinite loop):\n\n"
 974       "++:Label\n"
 975       "++:: This is a loop.\n"
 976       "++GOTO Label\n\n"
 977 #define HLP_RETURN      "*Commands Executing_Command_Files RETURN"
 978        /***************** 80 character line width template *************************/
 979       "3RETURN\n"
 980       " The `RETURN` command causes the current procedure call to be restored to\n"
 981       " the calling context, possibly returning a specific return status.\n"
 982       " If no return status is specified, the return status from the last command\n"
 983       " executed will be returned.  The calling context may have `ON` traps defined\n"
 984       " which may redirect command flow in that context.\n\n"
 985       "++RETURN                 return from command file with last command status\n"
 986       "++RETURN {-Q} <status>   return from command file with specific status\n\n"
 987       " * The status return can be any numeric value or one of the standard SCPE_\n"
 988       " condition names.\n\n"
 989       " * The \"`-Q`\" switch on the `RETURN` command will cause the specified\n"
 990       " status to be returned, but normal error status message printing to be\n"
 991       " suppressed.\n\n"
 992       " **Condition Names**\n\n"
 993       " The available standard SCPE_ condition names and their meanings are:\n\n"
 994       " | Name    | Meaning                         | Name    | Meaning                             |\n"
 995       " | ------- | --------------------------------| ------- | ----------------------------------- |\n"
 996       " | NXM     | Address space exceeded          | UNATT   | Unit not attached                   |\n"
 997       " | IOERR   | I/O error                       | CSUM    | Checksum error                      |\n"
 998       " | FMT     | Format error                    | NOATT   | Unit not attachable                 |\n"
 999       " | OPENERR | File open error                 | MEM     | Memory exhausted                    |\n"
1000       " | ARG     | Invalid argument                | STEP    | Step expired                        |\n"
1001       " | UNK     | Unknown command                 | RO      | Read only argument                  |\n"
1002       " | INCOMP  | Command not completed           | STOP    | Simulation stopped                  |\n"
1003       " | EXIT    | Goodbye                         | TTIERR  | Console input I/O error             |\n"
1004       " | TTOERR  | Console output I/O error        | EOF     | End of file                         |\n"
1005       " | REL     | Relocation error                | NOPARAM | No settable parameters              |\n"
1006       " | ALATT   | Unit already attached           | TIMER   | Hardware timer error                |\n"
1007       " | SIGERR  | Signal handler setup error      | TTYERR  | Console terminal setup error        |\n"
1008       " | NOFNC   | Command not allowed             | UDIS    | Unit disabled                       |\n"
1009       " | NORO    | Read only operation not allowed | INVSW   | Invalid switch                      |\n"
1010       " | MISVAL  | Missing value                   | 2FARG   | Too few arguments                   |\n"
1011       " | 2MARG   | Too many arguments              | NXDEV   | Non-existent device                 |\n"
1012       " | NXUN    | Non-existent unit               | NXREG   | Non-existent register               |\n"
1013       " | NXPAR   | Non-existent parameter          | NEST    | Nested DO command limit exceeded    |\n"
1014       " | IERR    | Internal error                  | MTRLNT  | Invalid magtape record length       |\n"
1015       " | LOST    | Console Telnet connection lost  | TTMO    | Console Telnet connection timed out |\n"
1016       " | STALL   | Console Telnet output stall     | AFAIL   | Assertion failed                    |\n"
1017       " | INVREM  | Invalid remote console command  |         |                                     |\n"
1018       "\n\n"
1019 #define HLP_SHIFT       "*Commands Executing_Command_Files Shift_Parameters"
1020       "3Shift Parameters\n"
1021       " Shift the command files positional parameters\n"
1022 #define HLP_CALL        "*Commands Executing_Command_Files Call_a_subroutine"
1023       "3Call a subroutine\n"
1024       " Control can be transferred to a labeled subroutine using `CALL`.\n\n"
1025       " **Example**\n\n"
1026       "++CALL routine\n"
1027       "++BYE\n"
1028       "++\n"
1029       "++:routine\n"
1030       "++ECHO routine called\n"
1031       "++RETURN\n\n"
1032 #define HLP_ON          "*Commands Executing_Command_Files ON"
1033       "3ON\n"
1034       " The `ON` command performs actions after a condition, or clears a condition.\n"
1035       "++ON <condition> <action>  Perform action after condition\n"
1036       "++ON <condition>           Clears action of specified condition\n"
1037 #define HLP_PROCEED     "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1038 #define HLP_IGNORE      "*Commands Executing_Command_Files PROCEED_or_IGNORE"
1039        /***************** 80 character line width template *************************/
1040       "3PROCEED or IGNORE\n"
1041       " The `PROCEED` (or `IGNORE`) command does nothing.  It is potentially\n"
1042       " useful as a placeholder for any `ON` action condition that should be\n"
1043       " explicitly ignored, allowing command file execution to continue without\n"
1044       " taking any specific action.\n"
1045 #define HLP_ECHO        "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1046        /***************** 80 character line width template *************************/
1047       "3Displaying Arbitrary Text\n"
1048       " The `ECHO` command is a useful way of annotating command files.  `ECHO`\n"
1049       " prints out its arguments to the console (and to any applicable log file):\n\n"
1050       "++ECHO <string>      Output string to console\n\n"
1051       " **NOTE**: If no arguments are specified, `ECHO` prints a blank line.\n"
1052       " This may be used to provide spacing for console messages or log file\n"
1053       " output.\n"
1054        /***************** 80 character line width template *************************/
1055 #define HLP_ASSERT      "*Commands Executing_Command_Files Testing_Assertions"
1056       "3Testing Assertions\n"
1057       " The `ASSERT` command tests a simulator state condition and halts command\n"
1058       " file execution if the condition is false:\n\n"
1059       "++ASSERT <Simulator State Expressions>\n\n"
1060       " * If the indicated expression evaluates to false, the command completes\n"
1061       " with an `AFAIL` condition.  By default, when a command file encounters a\n"
1062       " command which returns the `AFAIL` condition, it will exit the running\n"
1063       " command file with the `AFAIL` status to the calling command file.  This\n"
1064       " behavior can be changed with the `ON` command as well as switches to the\n"
1065       " invoking `DO` command.\n\n"
1066       " **Examples**\n\n"
1067       " The command file below might be used to bootstrap a hypothetical system\n"
1068       " that halts after the initial load from disk. The `ASSERT` command can then\n"
1069       " be used to confirm that the load completed successfully by examining the\n"
1070       " CPU's \"`A`\" register for the expected value:\n\n"
1071       "++; Example INI file\n"
1072       "++BOOT\n"
1073       "++; A register contains error code; 0 = good boot\n"
1074       "++ASSERT A=0\n"
1075       "++RUN\n\n"
1076        /***************** 80 character line width template *************************/
1077       " * In the above example, if the \"`A`\" register is *not* `0`,\n"
1078       " the \"`ASSERT A=0`\" command will be displayed to the user, and the\n"
1079       " command file will be aborted with an \"`Assertion failed`\" message.\n"
1080       " Otherwise, the command file will continue to bring up the system.\n\n"
1081       " * See the **`IF`** command documentation for more information and details\n"
1082       " regarding simulator state expressions.\n\n"
1083 #define HLP_IF          "*Commands Executing_Command_Files Testing_Conditions"
1084       "3Testing Conditions\n"
1085       " The `IF` command tests a simulator state condition and executes additional\n"
1086       " commands if the condition is true:\n\n"
1087       "++IF <Simulator State Expressions> commandtoprocess{; additionalcommand}...\n\n"
1088       " **Examples**\n\n"
1089       " The command file below might be used to bootstrap a hypothetical system\n"
1090       " that halts after the initial load from disk. The `IF` command can then\n"
1091       " be used to confirm that the load completed successfully by examining the\n"
1092       " CPU's \"`A`\" register for an expected value:\n\n"
1093       "++; Example INI file\n"
1094       "++BOOT\n"
1095       "++; A register contains error code; 0 = good boot\n"
1096       "++IF NOT A=0 echo Boot failed - Failure Code ; EX A; exit AFAIL\n"
1097       "++RUN\n\n"
1098        /***************** 80 character line width template *************************/
1099       " * In the above example, if the \"`A`\" register is *not* `0`, the\n"
1100       " message \"`Boot failed - Failure Code `\" will be displayed, the contents\n"
1101       " of the \"`A`\" register will be displayed, and the command file will be\n"
1102       " aborted with an \"`Assertion failed`\" message.  Otherwise, the command\n"
1103       " file will continue to bring up the system.\n"
1104       "4Conditional Expressions\n"
1105       " The `IF` and `ASSERT` commands evaluate the following two different forms\n"
1106       " of conditional expressions.\n\n"
1107       "5Simulator State Expressions\n"
1108       "  &nbsp;\n \n"
1109       " The values of simulator registers can be evaluated with:\n\n"
1110       "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1111       " * If \"<`dev`>\" is not specified, `CPU` is assumed.  \"<`reg`>\" is a\n"
1112       " register belonging to the indicated device.\n"
1113       " * The \"<`addr`>\" is an address in the address space of the indicated\n"
1114       " device.\n"
1115       " * The \"<`conditional-op`>\" and optional \"<`logical-op`>\" are\n"
1116       " the same as those used for \"search specifiers\" by the `EXAMINE` and\n"
1117       " `DEPOSIT` commands.\n"
1118       " The \"<`value`>\" is expressed in the radix specified for \"<`reg`>\",\n"
1119       " not in the radix for the device when referencing a register; when an\n"
1120       " address is referenced the device radix is used as the default.\n\n"
1121       " * If \"<`logical-op`>\" and \"<`value`>\" are specified, the target\n"
1122       " register value is first altered as indicated.  The result is then compared\n"
1123       " to the \"<`value`>\" via the \"<`conditional-op`>\".\n"
1124       " * * If the result is *true*, the command(s) are executed before proceeding\n"
1125       " to the next line in the command file.\n"
1126       " * * If the result is *false*, the next command in the command file is\n"
1127       " processed.\n\n"
1128       "5String Comparison Expressions\n"
1129       "  &nbsp;\n \n"
1130       " String Values can be compared with:\n\n"
1131       "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1132       " * The \"`-i`\" switch, if present, causes a comparison to be case\n"
1133       "   insensitive.\n"
1134       " * The \"<`string1`>\" and \"<`string2`>\" arguments are quoted string\n"
1135       "   values which may have environment variables substituted as desired.\n"
1136       " * The \"<`compare-op`>\" may be one of:\n\n"
1137       " |                |                        |\n"
1138       " | --------------:|:---------------------- |\n"
1139       " | \"**`==`**\"     |  equal                 |\n"
1140       " | \"**`EQU`**\"    |  equal                 |\n"
1141       " | \"**`!=`**\"     |  not equal             |\n"
1142       " | \"**`NEQ`**\"    |  not equal             |\n"
1143       " | \"**<**\"        |  less than             |\n"
1144       " | \"**`LSS`**\"    |  less than             |\n"
1145       " | \"**<=**\"       |  less than or equal    |\n"
1146       " | \"**`LEQ`**\"    |  less than or equal    |\n"
1147       " | \"**>**\"        |  greater than          |\n"
1148       " | \"**`GTR`**\"    |  greater than          |\n"
1149       " | \"**>=**\"       |  greater than or equal |\n"
1150       " | \"**`GEQ`**\"    |  greater than or equal |\n"
1151       " * **NOTE**: Comparisons are *generic*.  This means that if\n"
1152       " both \"<`string1`>\" and \"<`string2`>\" are comprised of all numeric\n"
1153       " digits, then the strings are converted to numbers and a numeric\n"
1154       " comparison is performed.  For example, the comparison\n"
1155       " '`\"+1\"` `EQU` `\"1\"`' evaluates to *true*.\n"
1156        /***************** 80 character line width template *************************/
1157 #define HLP_EXIT        "*Commands Exiting_the_Simulator"
1158       "2Exiting the Simulator\n"
1159       " The `EXIT` command (*synonyms* `QUIT` *and* `BYE`) exits the simulator,\n"
1160       " returning control to the host operating system.\n"
1161        /***************** 80 character line width template *************************/
1162 #define HLP_SPAWN       "*Commands Executing_System_Commands"
1163       "2Executing System Commands\n"
1164       " * The simulator can execute host operating system commands with\n"
1165       " the \"`!`\" (*spawn*) command.\n\n"
1166       " |                         |                                             |\n"
1167       " |:----------------------- |:------------------------------------------- |\n"
1168       " | \"**`!`**\"             | Spawn the hosts default command interpreter |\n"
1169       " | \"**`!`** <`command`>\" | Execute the host operating system `command` |\n\n"
1170       " * **NOTE**: The *exit status* from the command which was executed is set\n"
1171       " as the *command completion status* for the \"`!`\" command.  This may\n"
1172       " influence any enabled `ON` condition traps.\n" ;
1173        /***************** 80 character line width template *************************/
1174 
1175 static CTAB cmd_table[] = {
1176     { "RESET",    &reset_cmd,  0,         HLP_RESET     },
1177     { "EXAMINE",  &exdep_cmd,  EX_E,      HLP_EXAMINE   },
1178     { "IEXAMINE", &exdep_cmd,  EX_E+EX_I, HLP_IEXAMINE  },
1179     { "DEPOSIT",  &exdep_cmd,  EX_D,      HLP_DEPOSIT   },
1180     { "IDEPOSIT", &exdep_cmd,  EX_D+EX_I, HLP_IDEPOSIT  },
1181     { "EVALUATE", &eval_cmd,   0,         HLP_EVALUATE  },
1182     { "RUN",      &run_cmd,    RU_RUN,    HLP_RUN,      NULL, &run_cmd_message },
1183     { "GO",       &run_cmd,    RU_GO,     HLP_GO,       NULL, &run_cmd_message },
1184     { "STEP",     &run_cmd,    RU_STEP,   HLP_STEP,     NULL, &run_cmd_message },
1185     { "NEXT",     &run_cmd,    RU_NEXT,   HLP_NEXT,     NULL, &run_cmd_message },
1186     { "CONTINUE", &run_cmd,    RU_CONT,   HLP_CONTINUE, NULL, &run_cmd_message },
1187     { "BOOT",     &run_cmd,    RU_BOOT,   HLP_BOOT,     NULL, &run_cmd_message },
1188     { "BREAK",    &brk_cmd,    SSH_ST,    HLP_BREAK     },
1189     { "NOBREAK",  &brk_cmd,    SSH_CL,    HLP_NOBREAK   },
1190     { "ATTACH",   &attach_cmd, 0,         HLP_ATTACH    },
1191     { "DETACH",   &detach_cmd, 0,         HLP_DETACH    },
1192     { "EXIT",     &exit_cmd,   0,         HLP_EXIT      },
1193     { "QUIT",     &exit_cmd,   0,         NULL          },
1194     { "BYE",      &exit_cmd,   0,         NULL          },
1195     { "SET",      &set_cmd,    0,         HLP_SET       },
1196     { "SHOW",     &show_cmd,   0,         HLP_SHOW      },
1197     { "DO",       &do_cmd,     1,         HLP_DO        },
1198     { "GOTO",     &goto_cmd,   1,         HLP_GOTO      },
1199     { "RETURN",   &return_cmd, 0,         HLP_RETURN    },
1200     { "SHIFT",    &shift_cmd,  0,         HLP_SHIFT     },
1201     { "CALL",     &call_cmd,   0,         HLP_CALL      },
1202     { "ON",       &on_cmd,     0,         HLP_ON        },
1203     { "IF",       &assert_cmd, 0,         HLP_IF        },
1204     { "PROCEED",  &noop_cmd,   0,         HLP_PROCEED   },
1205     { "IGNORE",   &noop_cmd,   0,         HLP_IGNORE    },
1206     { "ECHO",     &echo_cmd,   0,         HLP_ECHO      },
1207     { "ASSERT",   &assert_cmd, 1,         HLP_ASSERT    },
1208     { "SEND",     &send_cmd,   0          },            /* deprecated */
1209     { "EXPECT",   &expect_cmd, 1          },            /* deprecated */
1210     { "NOEXPECT", &expect_cmd, 0          },            /* deprecated */
1211     { "!",        &spawn_cmd,  0,         HLP_SPAWN     },
1212     { "HELP",     &help_cmd,   0,         HLP_HELP      },
1213     { NULL,       NULL,        0          }
1214     };
1215 
1216 static CTAB set_glob_tab[] = {
1217     { "CONSOLE",     &sim_set_console,        0      }, /* deprecated */
1218     { "REMOTE",      &sim_set_remote_console, 0      }, /* deprecated */
1219     { "BREAK",       &brk_cmd,                SSH_ST }, /* deprecated */
1220     { "NOBREAK",     &brk_cmd,                SSH_CL }, /* deprecated */
1221     { "TELNET",      &sim_set_telnet,         0      }, /* deprecated */
1222     { "NOTELNET",    &sim_set_notelnet,       0      }, /* deprecated */
1223     { "LOG",         &sim_set_logon,          0,     HLP_SET_LOG      },
1224     { "NOLOG",       &sim_set_logoff,         0,     HLP_SET_LOG      },
1225     { "DEBUG",       &sim_set_debon,          0,     HLP_SET_DEBUG    },
1226     { "NODEBUG",     &sim_set_deboff,         0,     HLP_SET_DEBUG    },
1227     { "ENVIRONMENT", &sim_set_environment,    1,     HLP_SET_ENVIRON  },
1228     { "ON",          &set_on,                 1,     HLP_SET_ON       },
1229     { "NOON",        &set_on,                 0,     HLP_SET_ON       },
1230     { "VERIFY",      &set_verify,             1,     HLP_SET_VERIFY   },
1231     { "VERBOSE",     &set_verify,             1,     HLP_SET_VERIFY   },
1232     { "NOVERIFY",    &set_verify,             0,     HLP_SET_VERIFY   },
1233     { "NOVERBOSE",   &set_verify,             0,     HLP_SET_VERIFY   },
1234     { "MESSAGE",     &set_message,            1,     HLP_SET_MESSAGE  },
1235     { "NOMESSAGE",   &set_message,            0,     HLP_SET_MESSAGE  },
1236     { "QUIET",       &set_quiet,              1,     HLP_SET_QUIET    },
1237     { "NOQUIET",     &set_quiet,              0,     HLP_SET_QUIET    },
1238     { "LOCALOPC",    &set_localopc,           1,     HLP_SET_LOCALOPC },
1239     { "NOLOCALOPC",  &set_localopc,           0,     HLP_SET_LOCALOPC },
1240     { "PROMPT",      &set_prompt,             0,     HLP_SET_PROMPT   },
1241     { NULL,          NULL,                    0      }
1242     };
1243 
1244 static C1TAB set_dev_tab[] = {
1245     { "OCTAL",    &set_dev_radix,   8  },
1246     { "DECIMAL",  &set_dev_radix,  10  },
1247     { "HEX",      &set_dev_radix,  16  },
1248     { "ENABLED",  &set_dev_enbdis,  1  },
1249     { "DISABLED", &set_dev_enbdis,  0  },
1250     { "DEBUG",    &set_dev_debug,   1  },
1251     { "NODEBUG",  &set_dev_debug,   0  },
1252     { NULL,       NULL,             0  }
1253     };
1254 
1255 static C1TAB set_unit_tab[] = {
1256     { "ENABLED",  &set_unit_enbdis, 1 },
1257     { "DISABLED", &set_unit_enbdis, 0 },
1258     { NULL,       NULL,             0 }
1259     };
1260 
1261 static SHTAB show_glob_tab[] = {
1262     { "CONFIGURATION",  &show_config,        0, HLP_SHOW_CONFIG    },
1263     { "DEFAULT_BASE_SYSTEM_SCRIPT",
1264            &show_default_base_system_script, 0, HLP_SHOW_DBS       },
1265     { "DEVICES",   &show_config,             1, HLP_SHOW_DEVICES   },
1266     { "FEATURES",  &show_config,             2, HLP_SHOW_FEATURES  },
1267     { "QUEUE",     &show_queue,              0, HLP_SHOW_QUEUE     },
1268     { "TIME",      &show_time,               0, HLP_SHOW_TIME      },
1269     { "MODIFIERS", &show_mod_names,          0, HLP_SHOW_MODIFIERS },
1270     { "NAMES",     &show_log_names,          0, HLP_SHOW_NAMES     },
1271     { "SHOW",      &show_show_commands,      0, HLP_SHOW_SHOW      },
1272     { "VERSION",   &show_version,            1, HLP_SHOW_VERSION   },
1273     { "BUILDINFO", &show_buildinfo,          1, HLP_SHOW_BUILDINFO },
1274     { "PROM",      &show_prom,               0, HLP_SHOW_PROM      },
1275     { "CONSOLE",   &sim_show_console,        0, HLP_SHOW_CONSOLE   },
1276     { "REMOTE",    &sim_show_remote_console, 0, HLP_SHOW_REMOTE    },
1277     { "BREAK",     &show_break,              0, HLP_SHOW_BREAK     },
1278     { "LOG",       &sim_show_log,            0, HLP_SHOW_LOG       },
1279     { "TELNET",    &sim_show_telnet,         0  },  /* deprecated */
1280     { "DEBUG",     &sim_show_debug,          0, HLP_SHOW_DEBUG     },
1281     { "CLOCKS",    &sim_show_timers,         0, HLP_SHOW_CLOCKS    },
1282     { "SEND",      &sim_show_send,           0, HLP_SHOW_SEND      },
1283     { "EXPECT",    &sim_show_expect,         0, HLP_SHOW_EXPECT    },
1284     { "ON",        &show_on,                 0, HLP_SHOW_ON        },
1285     { NULL,        NULL,                     0  }
1286     };
1287 
1288 static SHTAB show_dev_tab[] = {
1289     { "RADIX",     &show_dev_radix,         0 },
1290     { "DEBUG",     &show_dev_debug,         0 },
1291     { "MODIFIERS", &show_dev_modifiers,     0 },
1292     { "NAMES",     &show_dev_logicals,      0 },
1293     { "SHOW",      &show_dev_show_commands, 0 },
1294     { NULL,        NULL,                    0 }
1295     };
1296 
1297 static SHTAB show_unit_tab[] = {
1298     { NULL, NULL, 0 }
1299     };
1300 
1301 #if defined(_WIN32)
1302 static
1303 int setenv(const char *envname, const char *envval, int overwrite)
     /* [previous][next][first][last][top][bottom][index][help] */
1304 {
1305 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1306 int r;
1307 
1308 sprintf(envstr, "%s=%s", envname, envval);
1309 r = _putenv(envstr);
1310 FREE(envstr);
1311 return r;
1312 }
1313 
1314 static
1315 int unsetenv(const char *envname)
     /* [previous][next][first][last][top][bottom][index][help] */
1316 {
1317 setenv(envname, "", 1);
1318 return 0;
1319 }
1320 #endif /* if defined(_WIN32) */
1321 
1322 /* Testing realloc */
1323 
1324 #ifdef TESTING
1325 void *
1326 trealloc(void *ptr, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
1327 {
1328   void *r = realloc(ptr, size);
1329 
1330   if (r != ptr) return r;
1331   else if (r)
1332     {
1333       void *rm = malloc(size);
1334       if (!rm)
1335         {
1336           (void)fprintf(
1337             stderr,
1338             "\rFATAL: Out of memory?! Aborting at %s[%s:%d]\r\n",
1339             __func__, __FILE__, __LINE__);
1340 # if defined(USE_BACKTRACE)
1341 #  ifdef SIGUSR2
1342         (void)raise(SIGUSR2);
1343         /*NOTREACHED*/ /* unreachable */
1344 #  endif /* ifdef SIGUSR2 */
1345 # endif /* if defined(USE_BACKTRACE) */
1346         abort();
1347         /* NOTREACHED */ /* unreachable */
1348         }
1349       memcpy(rm, r, size);
1350       FREE(r);
1351       return rm;
1352     }
1353   else return r;
1354 }
1355 #endif /* ifdef TESTING */
1356 
1357 #ifdef TESTING
1358 # ifdef USE_TREALLOC
1359 #  undef realloc
1360 #  define realloc trealloc
1361 # endif /* ifdef USE_TREALLOC */
1362 #endif /* ifdef TESTING */
1363 
1364 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1365 
1366 /* Check if running on Rosetta 2 */
1367 
1368 #if defined(__APPLE__)
1369 int processIsTranslated(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1370 {
1371     int ret = 0;
1372     size_t size = sizeof(ret);
1373     if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1374         if (errno == ENOENT)
1375             return 0;
1376         return -1; }
1377     return ret;
1378 }
1379 #endif /* if defined(_APPLE_) */
1380 
1381 /* Substring removal hack */
1382 
1383 char *strremove(char *str, const char *sub)
     /* [previous][next][first][last][top][bottom][index][help] */
1384 {
1385     char *p, *q, *r;
1386     if (*sub && (q = r = strstr(str, sub)) != NULL) {
1387         size_t len = strlen(sub);
1388         while ((r = strstr(p = r + len, sub)) != NULL) {
1389             while (p < r)
1390                 *q++ = *p++;
1391         }
1392         while ((*q++ = *p++) != '\0')
1393             continue;
1394     }
1395     return str;
1396 }
1397 
1398 /* Trim whitespace */
1399 
1400 void strtrimspace (char *str_trimmed, const char *str_untrimmed)
     /* [previous][next][first][last][top][bottom][index][help] */
1401 {
1402     while (*str_untrimmed != '\0') {
1403       if(!isspace((unsigned char)*str_untrimmed)) {
1404         *str_trimmed = (char)*str_untrimmed;
1405         str_trimmed++;
1406       }
1407       str_untrimmed++;
1408     }
1409     *str_trimmed = '\0';
1410 }
1411 
1412 #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1413 void allowCores(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1414 {
1415   int ret;
1416   struct rlimit limit;
1417 # ifdef RLIMIT_CORE
1418   ret = getrlimit(RLIMIT_CORE, &limit);
1419   (void)ret;
1420 #  ifdef TESTING
1421   if (ret != 0)
1422     {
1423       sim_warn ("Failed to query core dump configuration.");
1424       return;
1425     }
1426 #  endif /* ifdef TESTING */
1427   limit.rlim_cur = limit.rlim_max;
1428   ret = setrlimit(RLIMIT_CORE, &limit);
1429 #  ifdef TESTING
1430   if (ret != 0)
1431     {
1432       sim_warn ("Failed to enable unlimited core dumps.");
1433       return;
1434     }
1435 #  endif /* ifdef TESTING */
1436 # else
1437 #  ifdef TESTING
1438   sim_warn ("Unable to query core dump configuration.");
1439 #  endif /* ifdef TESTING */
1440 # endif /* ifdef RLIMIT_CORE */
1441   return;
1442 }
1443 #endif /* if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1444 
1445 #ifdef USE_DUMA
1446 void CleanDUMA(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1447 {
1448   (void)fflush(stdout);
1449   DUMA_CHECKALL();
1450   (void)fflush(stderr);
1451 }
1452 # undef USE_DUMA
1453 # define USE_DUMA 1
1454 #endif /* ifdef USE_DUMA */
1455 
1456 #ifndef SIG_SETMASK
1457 # undef USE_BACKTRACE
1458 #endif /* ifndef SIG_SETMASK */
1459 
1460 #ifdef PERF_STRIP
1461 # undef USE_BACKTRACE
1462 #endif /* ifdef PERF_STRIP */
1463 
1464 #ifdef USE_BACKTRACE
1465 # include <backtrace.h>
1466 # include <backtrace-supported.h>
1467 # define BACKTRACE_SKIP 1
1468 # define BACKTRACE_MAIN "main"
1469 # undef USE_BACKTRACE
1470 # define USE_BACKTRACE 1
1471 #endif /* ifdef USE_BACKTRACE */
1472 
1473 #ifdef BACKTRACE_SUPPORTED
1474 # ifdef BACKTRACE_SUPPORTS_THREADS
1475 #  if !( BACKTRACE_SUPPORTED )
1476 #   undef USE_BACKTRACE
1477 #  endif /* if !( BACKTRACE_SUPPORTED ) */
1478 # else  /* ifdef BACKTRACE_SUPPORTS_THREADS */
1479 #  undef USE_BACKTRACE
1480 # endif /* ifdef BACKTRACE_SUPPORTS_THREADS */
1481 #else  /* ifdef BACKTRACE_SUPPORTED */
1482 # undef USE_BACKTRACE
1483 #endif /* ifdef BACKTRACE_SUPPORTED */
1484 
1485 #ifdef USE_BACKTRACE
1486 # ifdef BACKTRACE_SUPPORTED
1487 #  include "backtrace_func.c"
1488 # endif /* ifdef BACKTRACE_SUPPORTED */
1489 #endif /* ifdef USE_BACKTRACE */
1490 
1491 /* Main command loop */
1492 
1493 #ifndef PERF_STRIP
1494 int main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1495 {
1496 char *cptr, *cptr2;
1497 char nbuf[PATH_MAX + 7];
1498 char cbuf[4*CBUFSIZE];
1499 char **targv = NULL;
1500 # ifdef USE_BACKTRACE
1501 #  ifdef BACKTRACE_SUPPORTED
1502 #   ifdef _INC_BACKTRACE_FUNC
1503 bt_pid = (long)getpid();
1504 (void)bt_pid;
1505 #   endif /* ifdef _INC_BACKTRACE_FUNC */
1506 #  endif /* ifdef BACKTRACE_SUPPORTED */
1507 # endif /* ifdef USE_BACKTRACE */
1508 int32 i, sw;
1509 t_bool lookswitch;
1510 t_stat stat;
1511 
1512 # ifdef __MINGW32__
1513 #  undef IS_WINDOWS
1514 #  define IS_WINDOWS 1
1515 #  ifndef NEED_CONSOLE_SETUP
1516 #   define NEED_CONSOLE_SETUP
1517 #  endif
1518 # endif /* ifdef __MINGW32__ */
1519 
1520 # ifdef CROSS_MINGW32
1521 #  undef IS_WINDOWS
1522 #  define IS_WINDOWS 1
1523 #  ifndef NEED_CONSOLE_SETUP
1524 #   define NEED_CONSOLE_SETUP
1525 #  endif
1526 # endif /* ifdef CROSS_MINGW32 */
1527 
1528 # ifdef __MINGW64__
1529 #  undef IS_WINDOWS
1530 #  define IS_WINDOWS 1
1531 #  ifndef NEED_CONSOLE_SETUP
1532 #   define NEED_CONSOLE_SETUP
1533 #  endif
1534 # endif /* ifdef __MINGW64__ */
1535 
1536 # ifdef CROSS_MINGW64
1537 #  undef IS_WINDOWS
1538 #  define IS_WINDOWS 1
1539 #  ifndef NEED_CONSOLE_SETUP
1540 #   define NEED_CONSOLE_SETUP
1541 #  endif
1542 # endif /* ifdef CROSS_MINGW64 */
1543 
1544 # ifdef __CYGWIN__
1545 #  ifdef IS_WINDOWS
1546 #   undef IS_WINDOWS
1547 #  endif /* ifdef IS_WINDOWS */
1548 # endif /* ifdef __CYGWIN__ */
1549 
1550 # ifdef USE_DUMA
1551 #  ifdef DUMA_EXPLICIT_INIT
1552 duma_init();
1553 (void)fflush(stderr);
1554 #  endif /* ifdef DUMA_EXPLICIT_INIT */
1555 #  ifdef DUMA_MIN_ALIGNMENT
1556 #   if DUMA_MIN_ALIGNMENT > 0
1557 DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
1558 #   endif /* if DUMA_MIN_ALIGNMENT > 0 */
1559 #  endif /* ifdef DUMA_MIN_ALIGNMENT) */
1560 DUMA_SET_FILL(0x2E);
1561 (void)fflush(stderr);
1562 (void)atexit(CleanDUMA);
1563 # endif /* ifdef USE_DUMA */
1564 
1565 # ifdef USE_BACKTRACE
1566 #  ifdef BACKTRACE_SUPPORTED
1567 #   include "backtrace_main.c"
1568 #  endif /* ifdef BACKTRACE_SUPPORTED */
1569 # endif /* ifdef USE_BACKTRACE */
1570 
1571 setlocale(LC_NUMERIC, "");
1572 
1573 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1574 #  include <windows.h>
1575 #  ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
1576 #   define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1577 #  endif
1578 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1579 
1580 # ifdef NEED_CONSOLE_SETUP
1581 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1582 if (handle != INVALID_HANDLE_VALUE)
1583   {
1584     DWORD mode = 0;
1585     if (GetConsoleMode(handle, &mode))
1586       {
1587         mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1588         SetConsoleMode(handle, mode);
1589       }
1590   }
1591 puts ("\e[0m");
1592 # endif /* NEED_CONSOLE_SETUP */
1593 
1594 # ifdef __HAIKU__
1595 (void)disable_debugger(1);
1596 # endif /* ifdef __HAIKU__ */
1597 
1598 /* sanity checks */
1599 
1600 # ifdef __clang_analyzer__
1601 fprintf (stderr, "Error: Attempting to execute a Clang Analyzer build!\n");
1602 return 1;
1603 # endif
1604 
1605 if (argc == 0) {
1606     fprintf (stderr, "Error: main() called directly!\n");
1607     return 1;
1608 }
1609 
1610 /* Enable unlimited core dumps */
1611 # if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
1612 allowCores();
1613 # endif /* !defined(_WIN32) && !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64) */
1614 
1615 int testEndian = decContextTestEndian();
1616 if (testEndian != 0) {
1617   if (testEndian == 1) {
1618     fprintf (stderr,
1619       "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1620     return 1;
1621   }
1622   if (testEndian == -1) {
1623     fprintf (stderr,
1624       "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1625     return 1;
1626   }
1627   fprintf (stderr,
1628     "Error: Unable to determine system byte order; aborting.\n");
1629   return 1;
1630 }
1631 # ifdef NEED_128
1632 test_math128();
1633 # endif
1634 
1635 /* patch intel dispatcher */
1636 # ifdef __DISPATCH_H_
1637 #  if defined(__INTEL_COMPILER)      ||  \
1638      defined(__INTEL_CLANG_COMPILER) ||  \
1639      defined(__INTEL_LLVM_COMPILER)  ||  \
1640      defined(INTEL_MKL_VERSION)      ||  \
1641      defined(__INTEL_MKL__)
1642 (void)agner_compiler_patch();
1643 #  endif
1644 # endif
1645 
1646 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1647 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1648 if (!targv)
1649   {
1650     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1651              __func__, __FILE__, __LINE__);
1652 # if defined(USE_BACKTRACE)
1653 #  ifdef SIGUSR2
1654     (void)raise(SIGUSR2);
1655     /*NOTREACHED*/ /* unreachable */
1656 #  endif /* ifdef SIGUSR2 */
1657 # endif /* if defined(USE_BACKTRACE) */
1658     abort();
1659   }
1660 for (i=0; i<argc; i++)
1661     targv[i] = argv[i];
1662 argv = targv;
1663 
1664 /* setup defaults */
1665 set_prompt (0, "sim>");                                 /* start with set standard prompt */
1666 *cbuf = 0;                                              /* init arg buffer */
1667 sim_switches = 0;                                       /* init switches */
1668 lookswitch = TRUE;
1669 stdnul = fopen(NULL_DEVICE,"wb");
1670 
1671 /* process arguments */
1672 for (i = 1; i < argc; i++) {                            /* loop thru args */
1673     if (argv[i] == NULL)                                /* paranoia */
1674         continue;
1675 
1676 /* requested only version? */
1677     int onlyvers  = strcmp(argv[i], "--version");
1678     if (onlyvers == 0) {
1679 # ifdef VER_H_GIT_VERSION
1680 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1681 #   if VER_H_GIT_PATCH_INT < 1
1682         fprintf (stdout, "%s simulator %s\n",
1683                  sim_name, VER_H_GIT_VERSION);
1684 #   else
1685         fprintf (stdout, "%s simulator %s+%s\n",
1686                  sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1687 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1688 #  else
1689         fprintf (stdout, "%s simulator %s\n",
1690                  sim_name, VER_H_GIT_VERSION);
1691 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1692 # else
1693         fprintf (stdout, "%s simulator\n", sim_name);
1694 # endif /* ifdef VER_H_GIT_VERSION */
1695         FREE (targv);
1696         return 0;
1697     }
1698 
1699 /* requested short or long help? */
1700     int longhelp  = strcmp(argv[i], "--help");
1701     int shorthelp = strcmp(argv[i], "-h");
1702     if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1703     if (longhelp == 0 || shorthelp == 0) {
1704 # ifdef VER_H_GIT_VERSION
1705 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1706 #   if VER_H_GIT_PATCH_INT < 1
1707         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1708 #   else
1709         fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1710 #   endif /* if VER_H_GIT_PATCH_INT < 1 */
1711 #  else
1712         fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1713 #  endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1714 # else
1715         fprintf (stdout, "%s simulator", sim_name);
1716 # endif /* ifdef VER_H_GIT_VERSION */
1717         fprintf (stdout, "\n");
1718         fprintf (stdout, "\n USAGE: %s { [ SWITCHES ] ... } { < SCRIPT > }", argv[0]);
1719         fprintf (stdout, "\n");
1720         fprintf (stdout, "\n Invokes the %s simulator, with optional switches and/or script file.", sim_name);
1721         fprintf (stdout, "\n");
1722         fprintf (stdout, "\n Switches:");
1723         fprintf (stdout, "\n  -e, -E            Aborts script processing immediately upon any error");
1724         fprintf (stdout, "\n  -h, -H, --help    Prints only this informational help text and exit");
1725         fprintf (stdout, "\n  -k, -K            Disables all support for exclusive file locking");
1726         fprintf (stdout, "\n  -l, -L            Reports but ignores all exclusive file locking errors");
1727         fprintf (stdout, "\n  -o, -O            Makes scripting ON conditions and actions inheritable");
1728         fprintf (stdout, "\n  -q, -Q            Disables printing of non-fatal informational messages");
1729         fprintf (stdout, "\n  -r, -R            Enables an unlinked ephemeral system state file");
1730         fprintf (stdout, "\n  -s, -S            Enables a randomized persistent system state file");
1731         fprintf (stdout, "\n  -t, -T            Disables fsync and creation/usage of system state file");
1732         fprintf (stdout, "\n  -v, -V            Prints commands read from script file before execution");
1733         fprintf (stdout, "\n  --version         Prints only the simulator identification text and exit");
1734         fprintf (stdout, "\n");
1735 # ifdef USE_DUMA
1736         nodist++;
1737 # endif /* ifdef USE_DUMA */
1738 if (!nodist) {
1739         fprintf (stdout, "\n This software is made available under the terms of the ICU License, version");
1740         fprintf (stdout, "\n 1.8.1 or later.  For complete details, see the \"LICENSE.md\" file included");
1741         fprintf (stdout, "\n with the software or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1742 }
1743 else
1744 {
1745         fprintf (stdout, "\n********** LICENSE RESTRICTED BUILD ****** NOT FOR REDISTRIBUTION **********");
1746 }
1747         fprintf (stdout, "\n");
1748         FREE(argv); //-V726
1749         return 0;
1750     }
1751     /* invalid arguments? */
1752     if ((*argv[i] == '-') && lookswitch) {              /* switch? */
1753         if ((sw = get_switches (argv[i])) < 0) {
1754             fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1755             FREE(argv); //-V726
1756             return 1;
1757             }
1758         sim_switches = sim_switches | sw;
1759         }
1760     /* parse arguments */
1761     else {
1762         if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1763             fprintf (stderr, "Argument string too long\n");
1764             FREE(argv); //-V726
1765             return 1;
1766             }
1767         if (*cbuf)                                  /* concat args */
1768             strcat (cbuf, " ");
1769         sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : "");  //-V755
1770         lookswitch = FALSE;                         /* no more switches */
1771         }
1772     }                                               /* end for */
1773 sim_nolock = sim_switches & SWMASK ('K');           /* -k means skip locking     */
1774 sim_iglock = sim_switches & SWMASK ('L');           /* -l means ignore locking   */
1775 sim_randompst = sim_switches & SWMASK ('S');        /* -s means persist random   */
1776 sim_quiet = sim_switches & SWMASK ('Q');            /* -q means quiet            */
1777 sim_randstate = sim_switches & SWMASK ('R');        /* -r means random sys_state */
1778 if (sim_randompst) sim_randstate = 1;               /*    and is implied with -s */
1779 sim_nostate = sim_switches & SWMASK ('T');          /* -t means no sys_state     */
1780 if (sim_nostate)                                    /*    and disables -s and -r */
1781   {
1782     sim_randompst = 0;
1783     sim_randstate = 0;
1784   }
1785 sim_on_inherit = sim_switches & SWMASK ('O');       /* -o means inherit on state */
1786 
1787 sim_init_sock ();                                   /* init socket capabilities */
1788 if (sim_dflt_dev == NULL)                           /* if no default */
1789     sim_dflt_dev = sim_devices[0];
1790 if (sim_vm_init != NULL)                            /* call once only */
1791     (*sim_vm_init)();
1792 sim_finit ();                                       /* init fio package */
1793 for (i = 0; cmd_table[i].name; i++) {
1794     size_t alias_len = strlen (cmd_table[i].name);
1795     char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
1796     if (!cmd_name)
1797       {
1798         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1799                  __func__, __FILE__, __LINE__);
1800 # if defined(USE_BACKTRACE)
1801 #  ifdef SIGUSR2
1802         (void)raise(SIGUSR2);
1803         /*NOTREACHED*/ /* unreachable */
1804 #  endif /* ifdef SIGUSR2 */
1805 # endif /* if defined(USE_BACKTRACE) */
1806         abort();
1807       }
1808 
1809     strcpy (cmd_name, cmd_table[i].name);
1810     while (alias_len > 1) {
1811         cmd_name[alias_len] = '\0';                 /* Possible short form command name */
1812         --alias_len;
1813         if (getenv (cmd_name))                      /* Externally defined command alias? */
1814             unsetenv (cmd_name);                    /* Remove it to protect against possibly malicious aliases */
1815         }
1816     FREE (cmd_name);
1817     }
1818 stop_cpu = 0;
1819 sim_interval = 0;
1820 sim_time = sim_rtime = 0;
1821 noqueue_time = 0;
1822 sim_clock_queue = QUEUE_LIST_END;
1823 sim_is_running = 0;
1824 sim_log = NULL;
1825 if (sim_emax <= 0)
1826     sim_emax = 1;
1827 sim_timer_init ();
1828 
1829 if ((stat = sim_ttinit ()) != SCPE_OK) {
1830     fprintf (stderr, "Fatal terminal initialization error\n%s\n",
1831         sim_error_text (stat));
1832     FREE(argv); //-V726
1833     return 1;
1834     }
1835 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
1836     fprintf (stderr, "Unable to allocate examine buffer\n");
1837     FREE(argv); //-V726
1838     return 1;
1839     };
1840 if ((stat = reset_all_p (0)) != SCPE_OK) {
1841     fprintf (stderr, "Fatal simulator initialization error\n%s\n",
1842         sim_error_text (stat));
1843     FREE(argv); //-V726
1844     return 1;
1845     }
1846 if ((stat = sim_brk_init ()) != SCPE_OK) {
1847     fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
1848         sim_error_text (stat));
1849     FREE(argv); //-V726
1850     return 1;
1851     }
1852 if (!sim_quiet) {
1853     printf ("\n");
1854     show_version (stdout, NULL, NULL, 0, NULL);
1855     }
1856 
1857 cptr = getenv("HOME");
1858 if (cptr == NULL) {
1859     cptr = getenv("HOMEPATH");
1860     cptr2 = getenv("HOMEDRIVE");
1861     }
1862 else
1863     cptr2 = NULL;
1864 (void)cptr2;
1865 if ( (*cbuf) && (strcmp(cbuf, "")) )                    /* cmd file arg? */
1866     stat = do_cmd (0, cbuf);                            /* proc cmd file */
1867 else if (*argv[0]) {                                    /* sim name arg? */
1868     char *np;                                           /* "path.ini" */
1869     nbuf[0] = '"';                                      /* starting " */
1870     stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE;         /* proc default cmd file */
1871     if (stat == SCPE_OPENERR) {                         /* didn't exist/can't open? */
1872         np = strrchr (nbuf, '/');                       /* stript path and try again in cwd */
1873         if (np == NULL)
1874             np = strrchr (nbuf, '\\');                  /* windows path separator */
1875         if (np != NULL) {
1876             *np = '"';
1877             stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE;   /* proc default cmd file */
1878             }
1879         }
1880     }
1881 
1882 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
1883 
1884 if (sim_vm_exit != NULL)                                /* call once only */
1885     (*sim_vm_exit)();
1886 
1887 detach_all (0, TRUE);                                   /* close files */
1888 sim_set_deboff (0, NULL);                               /* close debug */
1889 sim_set_logoff (0, NULL);                               /* close log */
1890 sim_set_notelnet (0, NULL);                             /* close Telnet */
1891 sim_ttclose ();                                         /* close console */
1892 sim_cleanup_sock ();                                    /* cleanup sockets */
1893 fclose (stdnul);                                        /* close bit bucket file handle */
1894 FREE (targv);                                           /* release any argv copy that was made */
1895 FREE (sim_prompt);
1896 FREE (sim_eval);
1897 FREE (sim_internal_devices);
1898 FREE (sim_brk_tab);
1899 FREE (sim_staba.comp);
1900 FREE (sim_staba.mask);
1901 FREE (sim_stabr.comp);
1902 FREE (sim_stabr.mask);
1903 return 0;
1904 }
1905 #endif
1906 
1907 t_stat process_stdin_commands (t_stat stat, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
1908 {
1909 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
1910 CONST char *cptr;
1911 t_stat stat_nomessage;
1912 CTAB *cmdp = NULL;
1913 
1914 stat = SCPE_BARE_STATUS(stat);                          /* remove possible flag */
1915 while (stat != SCPE_EXIT) {                             /* in case exit */
1916     if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf))))   /* pending action? */
1917         printf ("%s%s\n", sim_prompt, cptr);            /* echo */
1918     else if (sim_vm_read != NULL) {                     /* sim routine? */
1919         printf ("%s", sim_prompt);                      /* prompt */
1920         cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
1921         }
1922     else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prompt*/
1923     if (cptr == NULL) {                                 /* EOF? */
1924         if (sim_ttisatty()) continue;                   /* ignore tty EOF */
1925         else break;                                     /* otherwise exit */
1926         }
1927     if (*cptr == 0)                                     /* ignore blank */
1928         continue;
1929     sim_sub_args (cbuf, sizeof(cbuf), argv);
1930     if (sim_log)                                        /* log cmd */
1931         fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
1932     if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
1933         fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
1934     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
1935     sim_switches = 0;                                   /* init switches */
1936     if ((cmdp = find_cmd (gbuf)))                       /* lookup command */
1937         stat = cmdp->action (cmdp->arg, cptr);          /* if found, exec */
1938     else
1939         stat = SCPE_UNK;
1940     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
1941     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
1942     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
1943     sim_last_cmd_stat = stat;                           /* save command error status */
1944     if (!stat_nomessage) {                              /* displaying message status? */
1945         if (cmdp && (cmdp->message))                    /* special message handler? */
1946             cmdp->message (NULL, stat);                 /* let it deal with display */
1947         else
1948             if (stat >= SCPE_BASE)                      /* error? */
1949                 sim_printf ("%s\n", sim_error_text (stat));
1950         }
1951     if (sim_vm_post != NULL)
1952         (*sim_vm_post) (TRUE);
1953     }                                                   /* end while */
1954 return stat;
1955 }
1956 
1957 /* Set prompt routine */
1958 
1959 t_stat set_prompt (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1960 {
1961 char gbuf[CBUFSIZE], *gptr;
1962 
1963 if ((!cptr) || (*cptr == '\0'))
1964     return SCPE_ARG;
1965 
1966 cptr = get_glyph_nc (cptr, gbuf, '"');                  /* get quote delimited token */
1967 if (gbuf[0] == '\0') {                                  /* Token started with quote */
1968     gbuf[sizeof (gbuf)-1] = '\0';
1969     strncpy (gbuf, cptr, sizeof (gbuf)-1);
1970     gptr = strchr (gbuf, '"');
1971     if (gptr)
1972         *gptr = '\0';
1973     }
1974 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2);   /* nul terminator and trailing blank */
1975 if (!sim_prompt)
1976   {
1977     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1978              __func__, __FILE__, __LINE__);
1979 #if defined(USE_BACKTRACE)
1980 # ifdef SIGUSR2
1981     (void)raise(SIGUSR2);
1982     /*NOTREACHED*/ /* unreachable */
1983 # endif /* ifdef SIGUSR2 */
1984 #endif /* if defined(USE_BACKTRACE) */
1985     abort();
1986   }
1987 sprintf (sim_prompt, "%s ", gbuf);
1988 return SCPE_OK;
1989 }
1990 
1991 /* Find command routine */
1992 
1993 CTAB *find_cmd (const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
1994 {
1995 CTAB *cmdp = NULL;
1996 
1997 if (sim_vm_cmd)                                         /* try ext commands */
1998     cmdp = find_ctab (sim_vm_cmd, gbuf);
1999 if (cmdp == NULL)                                       /* try regular cmds */
2000     cmdp = find_ctab (cmd_table, gbuf);
2001 return cmdp;
2002 }
2003 
2004 /* Exit command */
2005 
2006 t_stat exit_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2007 {
2008 return SCPE_EXIT;
2009 }
2010 
2011 /* Help command */
2012 
2013 /* Used when sorting a list of command names */
2014 static int _cmd_name_compare (const void *pa, const void *pb)
     /* [previous][next][first][last][top][bottom][index][help] */
2015 {
2016 CTAB * const *a = (CTAB * const *)pa;
2017 CTAB * const *b = (CTAB * const *)pb;
2018 
2019 return strcmp((*a)->name, (*b)->name);
2020 }
2021 
2022 void fprint_help (FILE *st)
     /* [previous][next][first][last][top][bottom][index][help] */
2023 {
2024 CTAB *cmdp;
2025 CTAB **hlp_cmdp = NULL;
2026 int cmd_cnt = 0;
2027 int cmd_size = 0;
2028 size_t max_cmdname_size = 0;
2029 int i, line_offset;
2030 
2031 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
2032     if (cmdp->help) {
2033         if (cmd_cnt >= cmd_size) {
2034             cmd_size += 20;
2035             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2036             if (!hlp_cmdp)
2037               {
2038                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2039                          __func__, __FILE__, __LINE__);
2040 #if defined(USE_BACKTRACE)
2041 # ifdef SIGUSR2
2042                 (void)raise(SIGUSR2);
2043                 /*NOTREACHED*/ /* unreachable */
2044 # endif /* ifdef SIGUSR2 */
2045 #endif /* if defined(USE_BACKTRACE) */
2046                 abort();
2047               }
2048             }
2049         hlp_cmdp[cmd_cnt] = cmdp;
2050         ++cmd_cnt;
2051         if (strlen(cmdp->name) > max_cmdname_size)
2052             max_cmdname_size = strlen(cmdp->name);
2053         }
2054     }
2055 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
2056     if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) {
2057         if (cmd_cnt >= cmd_size) {
2058             cmd_size += 20;
2059             hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
2060             if (!hlp_cmdp)
2061               {
2062                 fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2063                          __func__, __FILE__, __LINE__);
2064 #if defined(USE_BACKTRACE)
2065 # ifdef SIGUSR2
2066                 (void)raise(SIGUSR2);
2067                 /*NOTREACHED*/ /* unreachable */
2068 # endif /* ifdef SIGUSR2 */
2069 #endif /* if defined(USE_BACKTRACE) */
2070                 abort();
2071               }
2072             }
2073         hlp_cmdp[cmd_cnt] = cmdp;
2074         ++cmd_cnt;
2075         if (strlen (cmdp->name) > max_cmdname_size)
2076             max_cmdname_size = strlen(cmdp->name);
2077         }
2078     }
2079 fprintf (st, "HELP is available for the following commands:\n\n    ");
2080 if (hlp_cmdp)
2081   qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
2082 line_offset = 4;
2083 for ( i = 0 ; i < cmd_cnt ; ++i ) {
2084     fputs (hlp_cmdp[i]->name, st);
2085     line_offset += 5 + max_cmdname_size;
2086     if (line_offset + max_cmdname_size > 79) {
2087         line_offset = 4;
2088         fprintf (st, "\n    ");
2089         }
2090     else
2091         fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
2092     }
2093 FREE (hlp_cmdp);
2094 fprintf (st, "\n");
2095 return;
2096 }
2097 
2098 static void fprint_header (FILE *st, t_bool *pdone, char *context)
     /* [previous][next][first][last][top][bottom][index][help] */
2099 {
2100 if (!*pdone)
2101     fprintf (st, "%s", context);
2102 *pdone = TRUE;
2103 }
2104 
2105 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2106 {
2107 REG *rptr, *trptr;
2108 t_bool found = FALSE;
2109 t_bool all_unique = TRUE;
2110 size_t max_namelen = 0;
2111 DEVICE *tdptr;
2112 CONST char *tptr;
2113 char *namebuf;
2114 char rangebuf[32];
2115 
2116 if (dptr->registers)
2117     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2118         if (rptr->flags & REG_HIDDEN)
2119             continue;
2120         if (rptr->depth > 1)
2121             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2122         else
2123             strcpy (rangebuf, "");
2124         if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2125             max_namelen = strlen(rptr->name) + strlen (rangebuf);
2126         found = TRUE;
2127         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2128         if ((trptr == NULL) || (tdptr != dptr))
2129             all_unique = FALSE;
2130         }
2131 if (!found) {
2132     if (!silent)
2133         fprintf (st, "No register HELP available for the %s device\n", dptr->name);
2134     }
2135 else {
2136     namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2137     if (!namebuf)
2138       {
2139         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2140                  __func__, __FILE__, __LINE__);
2141 #if defined(USE_BACKTRACE)
2142 # ifdef SIGUSR2
2143         (void)raise(SIGUSR2);
2144         /*NOTREACHED*/ /* unreachable */
2145 # endif /* ifdef SIGUSR2 */
2146 #endif /* if defined(USE_BACKTRACE) */
2147         abort();
2148       }
2149     fprintf (st, "\nThe %s device implements these registers:\n\n", dptr->name);
2150     for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2151         if (rptr->flags & REG_HIDDEN)
2152             continue;
2153         if (rptr->depth <= 1)
2154             sprintf (namebuf, "%*s", -((int)max_namelen), rptr->name);
2155         else {
2156             sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2157             sprintf (namebuf, "%s%*s", rptr->name, (int)(strlen(rptr->name))-((int)max_namelen), rangebuf);
2158             }
2159         if (all_unique) {
2160             fprintf (st, "  %s %4d  %s\n", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2161             continue;
2162             }
2163         trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2164         if ((trptr == NULL) || (tdptr != dptr))
2165             fprintf (st, "  %s %s %4d  %s\n", dptr->name, namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2166         else
2167             fprintf (st, "  %*s %s %4d  %s\n", (int)strlen(dptr->name), "", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2168         }
2169     FREE (namebuf);
2170     }
2171 }
2172 
2173 void fprint_reg_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2174 {
2175 fprint_reg_help_ex (st, dptr, TRUE);
2176 }
2177 
2178 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2179 {
2180 if (dptr->attach_help) {
2181     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2182     dptr->attach_help (st, dptr, NULL, 0, NULL);
2183     return;
2184     }
2185 if (DEV_TYPE(dptr) == DEV_DISK) {
2186     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2187     sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2188     return;
2189     }
2190 if (DEV_TYPE(dptr) == DEV_TAPE) {
2191     fprintf (st, "\n%s device ATTACH commands:\n\n", dptr->name);
2192     sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2193     return;
2194     }
2195 if (!silent) {
2196     fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2197     if (dptr->help)
2198         dptr->help (st, dptr, NULL, 0, NULL);
2199     }
2200 }
2201 
2202 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2203 {
2204 MTAB *mptr;
2205 DEBTAB *dep;
2206 t_bool found = FALSE;
2207 char buf[CBUFSIZE], header[CBUFSIZE];
2208 uint32 enabled_units = dptr->numunits;
2209 uint32 unit;
2210 
2211 sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2212 for (unit=0; unit < dptr->numunits; unit++)
2213     if (dptr->units[unit].flags & UNIT_DIS)
2214         --enabled_units;
2215 if (dptr->modifiers) {
2216     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2217         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2218             continue;                                       /* skip unit only extended modifiers */
2219         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2220             continue;                                       /* skip unit only simple modifiers */
2221         if (mptr->mstring) {
2222             fprint_header (st, &found, header);
2223             sprintf (buf, "SET %s %s%s", sim_dname (dptr), mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}" : "")));
2224             if ((strlen (buf) < 30) || (!mptr->help))
2225                 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2226             else
2227                 fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2228             }
2229         }
2230     }
2231 if (dptr->flags & DEV_DISABLE) {
2232     fprint_header (st, &found, header);
2233     sprintf (buf, "SET %s ENABLE", sim_dname (dptr));
2234     fprintf (st,  "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2235     sprintf (buf, "SET %s DISABLE", sim_dname (dptr));
2236     fprintf (st,  "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2237     }
2238 if (dptr->flags & DEV_DEBUG) {
2239     fprint_header (st, &found, header);
2240     sprintf (buf, "SET %s DEBUG", sim_dname (dptr));
2241     fprintf (st,  "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2242     sprintf (buf, "SET %s NODEBUG", sim_dname (dptr));
2243     fprintf (st,  "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2244     if (dptr->debflags) {
2245         t_bool desc_available = FALSE;
2246         strcpy (buf, "");
2247         fprintf (st, "SET %s DEBUG=", sim_dname (dptr));
2248         for (dep = dptr->debflags; dep->name != NULL; dep++) {
2249             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2250             desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2251             }
2252         fprintf (st, "\n");
2253         fprintf (st,  "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2254         fprintf (st, "SET %s NODEBUG=", sim_dname (dptr));
2255         for (dep = dptr->debflags; dep->name != NULL; dep++)
2256             fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2257         fprintf (st, "\n");
2258         fprintf (st,  "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2259         if (desc_available) {
2260             fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2261             for (dep = dptr->debflags; dep->name != NULL; dep++)
2262                 fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2263             }
2264         }
2265     }
2266 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2267     if (dptr->units->flags & UNIT_DISABLE) {
2268         fprint_header (st, &found, header);
2269         sprintf (buf, "SET %sn ENABLE", sim_dname (dptr));
2270         fprintf (st,  "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2271         sprintf (buf, "SET %sn DISABLE", sim_dname (dptr));
2272         fprintf (st,  "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2273         }
2274     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2275         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2276             continue;                                           /* skip device only modifiers */
2277         if ((!mptr->valid) && MODMASK(mptr,MTAB_XTD))
2278             continue;                                           /* skip show only modifiers */
2279         if (mptr->mstring) {
2280             fprint_header (st, &found, header);
2281             sprintf (buf, "SET %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}": "")));
2282             fprintf (st, "%-30s\t%s\n", buf, (strchr(mptr->mstring, '=')) ? "" : (mptr->help ? mptr->help : ""));
2283             }
2284         }
2285     }
2286 if (!found && !silent)
2287     fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2288 }
2289 
2290 void fprint_set_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2291 {
2292   fprint_set_help_ex (st, dptr, TRUE);
2293 }
2294 
2295 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2296 {
2297 MTAB *mptr;
2298 t_bool found = FALSE;
2299 char buf[CBUFSIZE], header[CBUFSIZE];
2300 uint32 enabled_units = dptr->numunits;
2301 uint32 unit;
2302 
2303 sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2304 for (unit=0; unit < dptr->numunits; unit++)
2305     if (dptr->units[unit].flags & UNIT_DIS)
2306         --enabled_units;
2307 if (dptr->modifiers) {
2308     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2309         if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2310             continue;                                       /* skip unit only extended modifiers */
2311         if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2312             continue;                                       /* skip unit only simple modifiers */
2313         if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2314             continue;
2315         fprint_header (st, &found, header);
2316         sprintf (buf, "SHOW %s %s%s", sim_dname (dptr), mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2317         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2318         }
2319     }
2320 if (dptr->flags & DEV_DEBUG) {
2321     fprint_header (st, &found, header);
2322     sprintf (buf, "SHOW %s DEBUG", sim_dname (dptr));
2323     fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2324     }
2325 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2326     for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2327         if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2328             continue;                                           /* skip device only modifiers */
2329         if ((!mptr->disp) || (!mptr->pstring))
2330             continue;
2331         fprint_header (st, &found, header);
2332         sprintf (buf, "SHOW %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2333         fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2334         }
2335     }
2336 if (!found && !silent)
2337     fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2338 }
2339 
2340 void fprint_show_help (FILE *st, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2341     {
2342     fprint_show_help_ex (st, dptr, TRUE);
2343     }
2344 
2345 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
     /* [previous][next][first][last][top][bottom][index][help] */
2346 {
2347 BRKTYPTAB *brkt = dptr->brk_types;
2348 char gbuf[CBUFSIZE];
2349 
2350 if (sim_brk_types == 0) {
2351     if ((dptr != sim_dflt_dev) && (!silent)) {
2352         fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2353         if (dptr->help)
2354             dptr->help (st, dptr, NULL, 0, NULL);
2355         }
2356     return;
2357     }
2358 if (brkt == NULL) {
2359     int i;
2360 
2361     if (dptr == sim_dflt_dev) {
2362         if (sim_brk_types & ~sim_brk_dflt) {
2363             fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2364             for (i=0; i<26; i++) {
2365                 if (sim_brk_types & (1<<i))
2366                     fprintf (st, "  -%c\n", 'A'+i);
2367                 }
2368             }
2369         fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2370         }
2371     return;
2372     }
2373 fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2374 while (brkt->btyp) {
2375     fprintf (st, "  %s     %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2376     ++brkt;
2377     }
2378 fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2379 }
2380 
2381 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2382 {
2383 char gbuf[CBUFSIZE];
2384 CTAB *cmdp;
2385 
2386 if (*cptr) {
2387     (void)get_glyph (cptr, gbuf, 0);
2388     if ((cmdp = find_cmd (gbuf))) {
2389         if (cmdp->action == &exdep_cmd) {
2390             if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2391                 return dptr->help (st, dptr, uptr, flag, cptr);
2392             else
2393                 fprintf (st, "No HELP available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2394             return SCPE_OK;
2395             }
2396         if (cmdp->action == &set_cmd) {
2397             fprint_set_help_ex (st, dptr, FALSE);
2398             return SCPE_OK;
2399             }
2400         if (cmdp->action == &show_cmd) {
2401             fprint_show_help_ex (st, dptr, FALSE);
2402             return SCPE_OK;
2403             }
2404         if (cmdp->action == &attach_cmd) {
2405             fprint_attach_help_ex (st, dptr, FALSE);
2406             return SCPE_OK;
2407             }
2408         if (cmdp->action == &brk_cmd) {
2409             fprint_brk_help_ex (st, dptr, FALSE);
2410             return SCPE_OK;
2411             }
2412         if (dptr->help)
2413             return dptr->help (st, dptr, uptr, flag, cptr);
2414         fprintf (st, "No %s HELP is available for the %s device\n", cmdp->name, dptr->name);
2415         return SCPE_OK;
2416         }
2417     if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2418         fprint_reg_help_ex (st, dptr, FALSE);
2419         return SCPE_OK;
2420         }
2421     if (dptr->help)
2422         return dptr->help (st, dptr, uptr, flag, cptr);
2423     fprintf (st, "No %s HELP is available for the %s device\n", gbuf, dptr->name);
2424     return SCPE_OK;
2425     }
2426 if (dptr->help) {
2427     return dptr->help (st, dptr, uptr, flag, cptr);
2428     }
2429 if (dptr->description)
2430     fprintf (st, "%s %s HELP\n", dptr->description (dptr), dptr->name);
2431 else
2432     fprintf (st, "%s HELP\n", dptr->name);
2433 fprint_set_help_ex    (st, dptr, TRUE);
2434 fprint_show_help_ex   (st, dptr, TRUE);
2435 fprint_attach_help_ex (st, dptr, TRUE);
2436 fprint_reg_help_ex    (st, dptr, TRUE);
2437 fprint_brk_help_ex    (st, dptr, TRUE);
2438 return SCPE_OK;
2439 }
2440 
2441 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
     /* [previous][next][first][last][top][bottom][index][help] */
2442 {
2443 switch (help[0]) {
2444     case '*':
2445         scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2446         if (sim_log)
2447             scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2448         break;
2449     default:
2450         fputs (help, stdout);
2451         if (sim_log)
2452             fputs (help, sim_log);
2453         break;
2454     }
2455 return SCPE_OK;
2456 }
2457 
2458 t_stat help_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2459 {
2460 char gbuf[CBUFSIZE];
2461 CTAB *cmdp;
2462 
2463 GET_SWITCHES (cptr);
2464 if (sim_switches & SWMASK ('F'))
2465     flag = flag | SCP_HELP_FLAT;
2466 if (*cptr) {
2467     cptr = get_glyph (cptr, gbuf, 0);
2468     if ((cmdp = find_cmd (gbuf))) {
2469         if (*cptr) {
2470             if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2471                 DEVICE *dptr;
2472                 UNIT *uptr;
2473                 t_stat r;
2474                 cptr = get_glyph (cptr, gbuf, 0);
2475                 dptr = find_unit (gbuf, &uptr);
2476                 if (dptr == NULL)
2477                     dptr = find_dev (gbuf);
2478                 if (dptr != NULL) {
2479                     r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2480                     if (sim_log)
2481                         help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2482                     return r;
2483                     }
2484                 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2485                     /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
2486                     if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2487                          (cmdp->help))
2488                         return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2489                     }
2490                 else { /* HELP SHOW xxx (not device or unit) */
2491                     SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2492                     if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2493                         return SCPE_ARG;
2494                     return help_cmd_output (flag, shptr->help, NULL);
2495                     }
2496                 return SCPE_ARG;
2497                 }
2498             else
2499                 return SCPE_2MARG;
2500             }
2501         if (cmdp->help) {
2502             if (strcmp (cmdp->name, "HELP") == 0) {
2503 
2504 
2505 
2506 
2507 
2508 
2509 
2510 
2511 
2512 
2513 
2514 
2515 
2516 
2517 
2518 
2519 
2520 
2521 
2522 
2523 
2524 
2525 
2526 
2527 
2528 
2529 
2530 
2531 
2532 
2533                 }
2534             else {
2535                 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2536                     sim_dflt_dev && sim_dflt_dev->help) {
2537                         sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2538                         if (sim_log)
2539                             sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2540                     }
2541                 }
2542             help_cmd_output (flag, cmdp->help, cmdp->help_base);
2543             }
2544         else { /* no help so it is likely a command alias */
2545             CTAB *cmdpa;
2546             for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2547                 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2548                     sim_printf ("%s is an alias for the %s command:\n%s",
2549                                 cmdp->name, cmdpa->name, cmdpa->help);
2550                     break;
2551                     }
2552             if (cmdpa->name == NULL)                /* not found? */
2553                 sim_printf ("No help available for the %s command\n", cmdp->name);
2554             }
2555         }
2556     else {
2557         DEVICE *dptr;
2558         UNIT *uptr;
2559         t_stat r;
2560         dptr = find_unit (gbuf, &uptr);
2561         if (dptr == NULL) {
2562             dptr = find_dev (gbuf);
2563             if (dptr == NULL)
2564                 return SCPE_ARG;
2565             if (dptr->flags & DEV_DIS)
2566                 sim_printf ("Device %s is currently disabled\n", dptr->name);
2567             }
2568         r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2569         if (sim_log)
2570             help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2571         return r;
2572         }
2573     }
2574 else {
2575     fprint_help (stdout);
2576     if (sim_log)
2577         fprint_help (sim_log);
2578     }
2579 return SCPE_OK;
2580 }
2581 
2582 /* Spawn command */
2583 
2584 t_stat spawn_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2585 {
2586 t_stat status;
2587 if ((cptr == NULL) || (strlen (cptr) == 0))
2588     cptr = getenv("SHELL");
2589 if ((cptr == NULL) || (strlen (cptr) == 0))
2590     cptr = getenv("ComSpec");
2591 fflush(stdout);                                         /* flush stdout */
2592 if (sim_log)                                            /* flush log if enabled */
2593     fflush (sim_log);
2594 if (sim_deb)                                            /* flush debug if enabled */
2595     fflush (sim_deb);
2596 status = system (cptr);
2597 
2598 return status;
2599 }
2600 
2601 /* Echo command */
2602 
2603 t_stat echo_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2604 {
2605 sim_printf ("%s\n", cptr);
2606 return SCPE_OK;
2607 }
2608 
2609 /*
2610  * DO command
2611  *
2612  * Note that SCPE_STEP ("Step expired") is considered a note and
2613  * not an error; it does not abort command execution when using -E.
2614  *
2615  * Inputs:
2616  *      flag    =   caller and nesting level indicator
2617  *      fcptr   =   filename and optional arguments, space-separated
2618  * Outputs:
2619  *      status  =   error status
2620  *
2621  * The "flag" input value indicates the source of the call, as follows:
2622  *
2623  *      -1      =   initialization file (no error if not found)
2624  *       0      =   command line file
2625  *       1      =   "DO" command
2626  *      >1      =   nested "DO" command
2627  */
2628 
2629 t_stat do_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
2630 {
2631 return do_cmd_label (flag, fcptr, NULL);
2632 }
2633 
2634 static char *do_position(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2635 {
2636 static char cbuf[4*CBUFSIZE];
2637 
2638 snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth],
2639     sim_do_label[sim_do_depth] ? "::" : "",
2640     sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "",
2641     sim_goto_line[sim_do_depth]);
2642 return cbuf;
2643 }
2644 
2645 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
     /* [previous][next][first][last][top][bottom][index][help] */
2646 {
2647 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2648 CONST char *cptr;
2649 FILE *fpin;
2650 CTAB *cmdp = NULL;
2651 int32 echo, nargs, errabort, i;
2652 int32 saved_sim_do_echo = sim_do_echo,
2653       saved_sim_show_message = sim_show_message,
2654       saved_sim_on_inherit = sim_on_inherit,
2655       saved_sim_quiet = sim_quiet;
2656 t_bool staying;
2657 t_stat stat, stat_nomessage;
2658 
2659 stat = SCPE_OK;
2660 staying = TRUE;
2661 if (flag > 0)                                           /* need switches? */
2662     GET_SWITCHES (fcptr);                               /* get switches */
2663 echo = (sim_switches & SWMASK ('V')) || sim_do_echo;    /* -v means echo */
2664 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2665 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2666 errabort = sim_switches & SWMASK ('E');                 /* -e means abort on error */
2667 
2668 abuf[sizeof(abuf)-1] = '\0';
2669 strncpy (abuf, fcptr, sizeof(abuf)-1);
2670 c = abuf;
2671 do_arg[10] = NULL;                                      /* make sure the argument list always ends with a NULL */
2672 for (nargs = 0; nargs < 10; ) {                         /* extract arguments */
2673     while (sim_isspace (*c))                            /* skip blanks */
2674         c++;
2675     if (*c == 0)                                        /* all done? */
2676         do_arg [nargs++] = NULL;                        /* null argument */
2677     else {
2678         if (*c == '\'' || *c == '"')                    /* quoted string? */
2679             quote = *c++;
2680         else quote = 0;
2681         do_arg[nargs++] = c;                            /* save start */
2682         while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2683             c++;
2684         if (*c)                                         /* term at quote/spc */
2685             *c++ = 0;
2686         }
2687     }                                                   /* end for */
2688 
2689 if (do_arg [0] == NULL)                                 /* need at least 1 */
2690     return SCPE_2FARG;
2691 if ((fpin = fopen (do_arg[0], "r")) == NULL) {          /* file failed to open? */
2692     strcat (strcpy (cbuf, do_arg[0]), ".ini");          /* try again with .ini extension */
2693     if ((fpin = fopen (cbuf, "r")) == NULL) {           /* failed a second time? */
2694         if (flag == 0)                                  /* cmd line file? */
2695              fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2696         return SCPE_OPENERR;                            /* return failure */
2697         }
2698     }
2699 if (flag >= 0) {                                        /* Only bump nesting from command or nested */
2700     ++sim_do_depth;
2701     if (sim_on_inherit) {                               /* inherit ON condition actions? */
2702         sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2703         for (i=0; i<SCPE_MAX_ERR; i++) {                /* replicate any on commands */
2704             if (sim_on_actions[sim_do_depth-1][i]) {
2705                 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2706                 if (NULL == sim_on_actions[sim_do_depth][i]) {
2707                     while (--i >= 0) {
2708                         FREE(sim_on_actions[sim_do_depth][i]);
2709                         sim_on_actions[sim_do_depth][i] = NULL;
2710                         }
2711                     sim_on_check[sim_do_depth] = 0;
2712                     sim_brk_clract ();                  /* defang breakpoint actions */
2713                     --sim_do_depth;                     /* unwind nesting */
2714                     fclose(fpin);
2715                     return SCPE_MEM;
2716                     }
2717                 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2718                 }
2719             }
2720         }
2721     }
2722 
2723 strcpy( sim_do_filename[sim_do_depth], do_arg[0]);      /* stash away do file name for possible use by 'call' command */
2724 sim_do_label[sim_do_depth] = label;                     /* stash away do label for possible use in messages */
2725 sim_goto_line[sim_do_depth] = 0;
2726 if (label) {
2727     sim_gotofile = fpin;
2728     sim_do_echo = echo;
2729     stat = goto_cmd (0, label);
2730     if (stat != SCPE_OK) {
2731         strcpy(cbuf, "RETURN SCPE_ARG");
2732         cptr = get_glyph (cbuf, gbuf, 0);               /* get command glyph */
2733         cmdp = find_cmd (gbuf);                         /* return the errorStage things to the stat will be returned */
2734         goto Cleanup_Return;
2735         }
2736     }
2737 if (errabort)                                           /* -e flag? */
2738     set_on (1, NULL);                                   /* equivalent to ON ERROR RETURN */
2739 
2740 do {
2741     sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
2742     if (!sim_do_ocptr[sim_do_depth]) {                  /* no pending action? */
2743         sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
2744         sim_goto_line[sim_do_depth] += 1;
2745         }
2746     sim_sub_args (cbuf, sizeof(cbuf), do_arg);          /* substitute args */
2747     if (cptr == NULL) {                                 /* EOF? */
2748         stat = SCPE_OK;                                 /* set good return */
2749         break;
2750         }
2751     if (*cptr == 0)                                     /* ignore blank */
2752         continue;
2753     if (echo)                                           /* echo if -v */
2754         sim_printf("%s> %s\n", do_position(), cptr);
2755     if (*cptr == ':')                                   /* ignore label */
2756         continue;
2757     cptr = get_glyph_cmd (cptr, gbuf);                  /* get command glyph */
2758     sim_switches = 0;                                   /* init switches */
2759     sim_gotofile = fpin;
2760     sim_do_echo = echo;
2761     if ((cmdp = find_cmd (gbuf))) {                     /* lookup command */
2762         if (cmdp->action == &return_cmd)                /* RETURN command? */
2763             break;                                      /*    done! */
2764         if (cmdp->action == &do_cmd) {                  /* DO command? */
2765             if (sim_do_depth >= MAX_DO_NEST_LVL)        /* nest too deep? */
2766                 stat = SCPE_NEST;
2767             else
2768                 stat = do_cmd (sim_do_depth+1, cptr);   /* exec DO cmd */
2769             }
2770         else
2771             stat = cmdp->action (cmdp->arg, cptr);      /* exec other cmd */
2772         }
2773     else stat = SCPE_UNK;                               /* bad cmd given */
2774     echo = sim_do_echo;                                 /* Allow for SET VERIFY */
2775     stat_nomessage = stat & SCPE_NOMESSAGE;             /* extract possible message suppression flag */
2776     stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2777     stat = SCPE_BARE_STATUS(stat);                      /* remove possible flag */
2778     if (cmdp)
2779       if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
2780           ((cmdp->action != &return_cmd) &&
2781            (cmdp->action != &goto_cmd) &&
2782            (cmdp->action != &on_cmd) &&
2783            (cmdp->action != &echo_cmd)))
2784         sim_last_cmd_stat = stat;                       /* save command error status */
2785     switch (stat) {
2786         case SCPE_AFAIL:
2787             staying = (sim_on_check[sim_do_depth] &&        /* if trap action defined */
2788                        sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
2789             break;
2790         case SCPE_EXIT:
2791             staying = FALSE;
2792             break;
2793         case SCPE_OK:
2794         case SCPE_STEP:
2795             break;
2796         default:
2797             break;
2798         }
2799     if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) &&   /* error from cmd? */
2800         (stat != SCPE_STEP)) {
2801         if (!echo && !sim_quiet &&                      /* report if not echoing */
2802             !stat_nomessage &&                          /* and not suppressing messages */
2803             !(cmdp && cmdp->message)) {                 /* and not handling them specially */
2804             sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
2805             }
2806         }
2807     if (!stat_nomessage) {                              /* report error if not suppressed */
2808         if (cmdp && cmdp->message)                      /* special message handler */
2809             cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
2810         else
2811             if (stat >= SCPE_BASE)                      /* report error if not suppressed */
2812                 sim_printf ("%s\n", sim_error_text (stat));
2813         }
2814     if (stat == SCPE_EXPECT)                            /* EXPECT status is non actionable */
2815         stat = SCPE_OK;                                 /* so adjust it to SCPE_OK */
2816     if (staying &&
2817         (sim_on_check[sim_do_depth]) &&
2818         (stat != SCPE_OK) &&
2819         (stat != SCPE_STEP)) {
2820         if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
2821             sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
2822         else
2823             sim_brk_setact (sim_on_actions[sim_do_depth][0]);
2824         }
2825     if (sim_vm_post != NULL)
2826         (*sim_vm_post) (TRUE);
2827     } while (staying);
2828 Cleanup_Return:
2829 if (fpin) //-V547
2830     fclose (fpin);                                      /* close file */
2831 sim_gotofile = NULL;
2832 if (flag >= 0) {
2833     sim_do_echo = saved_sim_do_echo;                    /* restore echo state we entered with */
2834     sim_show_message = saved_sim_show_message;          /* restore message display state we entered with */
2835     sim_on_inherit = saved_sim_on_inherit;              /* restore ON inheritance state we entered with */
2836     }
2837 sim_quiet = saved_sim_quiet;                            /* restore quiet mode we entered with */
2838 if ((flag >= 0) || (!sim_on_inherit)) {
2839     for (i=0; i<SCPE_MAX_ERR; i++) {                    /* release any on commands */
2840         FREE (sim_on_actions[sim_do_depth][i]);
2841         sim_on_actions[sim_do_depth][i] = NULL;
2842         }
2843     sim_on_check[sim_do_depth] = 0;                     /* clear on mode */
2844     }
2845 if (flag >= 0)
2846     --sim_do_depth;                                     /* unwind nesting */
2847 sim_brk_clract ();                                      /* defang breakpoint actions */
2848 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
2849     sim_string_to_stat (cptr, &stat);
2850     sim_last_cmd_stat = stat;                           /* save explicit status as command error status */
2851     if (sim_switches & SWMASK ('Q'))
2852         stat |= SCPE_NOMESSAGE;                         /* suppress error message display (in caller) if requested */
2853     return stat;                                        /* return with explicit return status */
2854     }
2855 return stat | SCPE_NOMESSAGE;                           /* suppress message since we've already done that here */
2856 }
2857 
2858 /*
2859  * Substitute_args - replace %n tokens in 'instr' with the do command's arguments
2860  *                   and other enviroment variables
2861  *
2862  * Calling sequence
2863  * instr        =       input string
2864  * instr_size   =       sizeof input string buffer
2865  * do_arg[10]   =       arguments
2866  *
2867  * Token "%0" expands to the command file name.
2868  * Token %n (n being a single digit) expands to the n'th argument
2869  * Tonen %* expands to the whole set of arguments (%1 ... %9)
2870  *
2871  * The input sequence "\%" represents a literal "%", and "\\" represents a
2872  * literal "\".  All other character combinations are rendered literally.
2873  *
2874  * Omitted parameters result in null-string substitutions.
2875  *
2876  * A Tokens preceeded and followed by % characters are expanded as environment
2877  * variables, and if one isn't found then can be one of several special
2878  * variables:
2879  *   %DATE%              yyyy-mm-dd
2880  *   %TIME%              hh:mm:ss
2881  *   %STIME%             hh_mm_ss
2882  *   %CTIME%             Www Mmm dd hh:mm:ss yyyy
2883  *   %STATUS%            Status value from the last command executed
2884  *   %TSTATUS%           The text form of the last status value
2885  *   %SIM_VERIFY%        The Verify/Verbose mode of the current Do command file
2886  *   %SIM_VERBOSE%       The Verify/Verbose mode of the current Do command file
2887  *   %SIM_QUIET%         The Quiet mode of the current Do command file
2888  *   %SIM_MESSAGE%       The message display status of the current Do command file
2889  * Environment variable lookups are done first with the precise name between
2890  * the % characters and if that fails, then the name between the % characters
2891  * is upcased and a lookup of that value is attempted.
2892 
2893  * The first Space delimited token on the line is extracted in uppercase and
2894  * then looked up as an environment variable.  If found it the value is
2895  * substituted for the original string before expanding everything else.  If
2896  * it is not found, then the original beginning token on the line is left
2897  * untouched.
2898  */
2899 
2900 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
     /* [previous][next][first][last][top][bottom][index][help] */
2901 {
2902 char gbuf[CBUFSIZE];
2903 char *ip = instr, *op, *oend, *tmpbuf;
2904 const char *ap;
2905 char rbuf[CBUFSIZE];
2906 int i;
2907 time_t now;
2908 struct tm *tmnow;
2909 
2910 time(&now);
2911 tmnow = localtime(&now);
2912 tmpbuf = (char *)malloc(instr_size);
2913 if (!tmpbuf)
2914   {
2915      fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
2916              __func__, __FILE__, __LINE__);
2917 #if defined(USE_BACKTRACE)
2918 # ifdef SIGUSR2
2919      (void)raise(SIGUSR2);
2920      /*NOTREACHED*/ /* unreachable */
2921 # endif /* ifdef SIGUSR2 */
2922 #endif /* if defined(USE_BACKTRACE) */
2923      abort();
2924   }
2925 op = tmpbuf;
2926 oend = tmpbuf + instr_size - 2;
2927 while (sim_isspace (*ip))                               /* skip leading spaces */
2928     *op++ = *ip++;
2929 for (; *ip && (op < oend); ) {
2930     if ((ip [0] == '\\') &&                             /* literal escape? */
2931         ((ip [1] == '%') || (ip [1] == '\\'))) {        /*   and followed by '%' or '\'? */
2932         ip++;                                           /* skip '\' */
2933         *op++ = *ip++;                                  /* copy escaped char */
2934         }
2935     else
2936         if ((*ip == '%') &&
2937             (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
2938             if ((ip[1] >= '0') && (ip[1] <= ('9'))) {   /* %n = sub */
2939                 ap = do_arg[ip[1] - '0'];
2940                 for (i=0; i<ip[1] - '0'; ++i)           /* make sure we're not past the list end */
2941                     if (do_arg[i] == NULL) {
2942                         ap = NULL;
2943                         break;
2944                         }
2945                 ip = ip + 2;
2946                 }
2947             else if (ip[1] == '*') {                    /* %1 ... %9 = sub */
2948                 memset (rbuf, '\0', sizeof(rbuf));
2949                 ap = rbuf;
2950                 for (i=1; i<=9; ++i)
2951                     if (do_arg[i] == NULL)
2952                         break;
2953                     else
2954                         if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
2955                             if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
2956                                 char quote = '"';
2957                                 if (strchr(do_arg[i], quote))
2958                                     quote = '\'';
2959                                 sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"", (i != 1) ? " " : "", quote, do_arg[i], quote);
2960                                 }
2961                             else
2962                                 sprintf(&rbuf[strlen(rbuf)], "%s%s", (i != 1) ? " " : "", do_arg[i]);
2963                             }
2964                         else
2965                             break;
2966                 ip = ip + 2;
2967                 }
2968             else {                                      /* environment variable */
2969                 ap = NULL;
2970                 (void)get_glyph_nc (ip+1, gbuf, '%');   /* first try using the literal name */
2971                 ap = getenv(gbuf);
2972                 if (!ap) {
2973                     (void)get_glyph (ip+1, gbuf, '%');  /* now try using the upcased name */
2974                     ap = getenv(gbuf);
2975                     }
2976                 ip += 1 + strlen (gbuf);
2977                 if (*ip == '%') ++ip;
2978                 if (!ap) {
2979                     /* ISO 8601 format date/time info */
2980                     if (!strcmp ("DATE", gbuf)) {
2981                         sprintf (rbuf, "%4d-%02d-%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday);
2982                         ap = rbuf;
2983                         }
2984                     else if (!strcmp ("TIME", gbuf)) {
2985                         sprintf (rbuf, "%02d:%02d:%02d", tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2986                         ap = rbuf;
2987                         }
2988                     else if (!strcmp ("DATETIME", gbuf)) {
2989                         sprintf (rbuf, "%04d-%02d-%02dT%02d:%02d:%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2990                         ap = rbuf;
2991                         }
2992                     /* Locale oriented formatted date/time info */
2993                     if (!strcmp ("LDATE", gbuf)) {
2994                         strftime (rbuf, sizeof(rbuf), "%x", tmnow);
2995                         ap = rbuf;
2996                         }
2997                     else if (!strcmp ("LTIME", gbuf)) {
2998                         strftime (rbuf, sizeof(rbuf), "%I:%M:%S %p", tmnow);
2999                         ap = rbuf;
3000                         }
3001                     else if (!strcmp ("CTIME", gbuf)) {
3002                         strftime (rbuf, sizeof(rbuf), "%c", tmnow);
3003                         ap = rbuf;
3004                         }
3005                     /* Separate Date/Time info */
3006                     else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
3007                         strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
3008                         ap = rbuf;
3009                         }
3010                     else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
3011                         strftime (rbuf, sizeof(rbuf), "%y", tmnow);
3012                         ap = rbuf;
3013                         }
3014                     else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
3015                         sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
3016                         ap = rbuf;
3017                         }
3018                     else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
3019                              (!strcmp ("DATE_19XX_YYYY", gbuf))) {
3020                         int year = tmnow->tm_year + 1900;
3021                         int days = year - 2001;
3022                         int leaps = days/4 - days/100 + days/400;
3023                         int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
3024                         int selector = ((days + leaps + 7) % 7) + lyear * 7;
3025                         static int years[] = {90, 91, 97, 98, 99, 94, 89,
3026                                               96, 80, 92, 76, 88, 72, 84};
3027                         int cal_year = years[selector];
3028 
3029                         if (!strcmp ("DATE_19XX_YY", gbuf))
3030                             sprintf (rbuf, "%d", cal_year);        /* 2 digit year */
3031                         else
3032                             sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
3033                         ap = rbuf;
3034                         }
3035                     else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
3036                         strftime (rbuf, sizeof(rbuf), "%m", tmnow);
3037                         ap = rbuf;
3038                         }
3039                     else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
3040                         strftime (rbuf, sizeof(rbuf), "%b", tmnow);
3041                         ap = rbuf;
3042                         }
3043                     else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
3044                         strftime (rbuf, sizeof(rbuf), "%d", tmnow);
3045                         ap = rbuf;
3046                         }
3047                     else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
3048                         sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
3049                         ap = rbuf;
3050                         }
3051                     else if ((!strcmp ("DATE_WW", gbuf)) ||   /* ISO 8601 week number (01-53) */
3052                              (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
3053                         int iso_yr = tmnow->tm_year + 1900;
3054                         int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
3055 
3056                         if (iso_wk == 0) {
3057                             iso_yr = iso_yr - 1;
3058                             tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0);  /* Adjust for Leap Year (Correct thru 2099) */
3059                             iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
3060                             }
3061                         else
3062                             if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
3063                                 ++iso_yr;
3064                                 iso_wk = 1;
3065                                 }
3066                         if (!strcmp ("DATE_WW", gbuf))
3067                             sprintf (rbuf, "%02d", iso_wk);
3068                         else
3069                             sprintf (rbuf, "%04d", iso_yr);
3070                         ap = rbuf;
3071                         }
3072                     else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
3073                         strftime (rbuf, sizeof(rbuf), "%j", tmnow);
3074                         ap = rbuf;
3075                         }
3076                     else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
3077                         strftime (rbuf, sizeof(rbuf), "%H", tmnow);
3078                         ap = rbuf;
3079                         }
3080                     else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
3081                         strftime (rbuf, sizeof(rbuf), "%M", tmnow);
3082                         ap = rbuf;
3083                         }
3084                     else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
3085                         strftime (rbuf, sizeof(rbuf), "%S", tmnow);
3086                         ap = rbuf;
3087                         }
3088                     else if (!strcmp ("STATUS", gbuf)) {
3089                         sprintf (rbuf, "%08X", sim_last_cmd_stat);
3090                         ap = rbuf;
3091                         }
3092                     else if (!strcmp ("TSTATUS", gbuf)) {
3093                         sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
3094                         ap = rbuf;
3095                         }
3096                     else if (!strcmp ("SIM_VERIFY", gbuf)) {
3097                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3098                         ap = rbuf;
3099                         }
3100                     else if (!strcmp ("SIM_VERBOSE", gbuf)) {
3101                         sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
3102                         ap = rbuf;
3103                         }
3104                     else if (!strcmp ("SIM_LOCALOPC", gbuf)) {
3105                         sprintf (rbuf, "%s", sim_localopc ? "1" : "");
3106                         ap = rbuf;
3107                         }
3108                     else if (!strcmp ("SIM_QUIET", gbuf)) {
3109                         sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
3110                         ap = rbuf;
3111                         }
3112                     else if (!strcmp ("SIM_MESSAGE", gbuf)) {
3113                         sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
3114                         ap = rbuf;
3115                         }
3116                     else if (!strcmp ("HOSTID", gbuf)) {
3117 #if defined( HAVE_UNISTD ) && !defined ( __HAIKU__ ) && !defined ( __ANDROID__ ) && !defined ( __serenity__  )
3118                         sprintf (rbuf, "%ld", (long)gethostid());
3119 #else
3120                         sprintf (rbuf, "00000000");
3121 #endif /* if defined( HAVE_UNISTD ) && !defined ( __HAIKU__ ) && !defined ( __ANDROID__ ) && !defined ( __serenity__ ) */
3122                         ap = rbuf;
3123                         }
3124                     else if (!strcmp ("UID", gbuf)) {
3125 #ifdef HAVE_UNISTD
3126                         sprintf (rbuf, "%ld", (long)getuid());
3127 #else
3128                         sprintf (rbuf, "0");
3129 #endif /* ifdef HAVE_UNISTD */
3130                         ap = rbuf;
3131                         }
3132                     else if (!strcmp ("GID", gbuf)) {
3133 #ifdef HAVE_UNISTD
3134                         sprintf (rbuf, "%ld", (long)getgid());
3135 #else
3136                         sprintf (rbuf, "0");
3137 #endif /* ifdef HAVE_UNISTD */
3138                         ap = rbuf;
3139                         }
3140                     else if (!strcmp ("EUID", gbuf)) {
3141 #ifdef HAVE_UNISTD
3142                         sprintf (rbuf, "%ld", (long)geteuid());
3143 #else
3144                         sprintf (rbuf, "0");
3145 #endif /* ifdef HAVE_UNISTD */
3146                         ap = rbuf;
3147                         }
3148                     else if (!strcmp ("EGID", gbuf)) {
3149 #ifdef HAVE_UNISTD
3150                         sprintf (rbuf, "%ld", (long)getegid());
3151 #else
3152                         sprintf (rbuf, "0");
3153 #endif /* ifdef HAVE_UNISTD */
3154                         ap = rbuf;
3155                         }
3156                     else if (!strcmp ("PID", gbuf)) {
3157 #ifdef HAVE_UNISTD
3158                         sprintf (rbuf, "%ld", (long)getpid());
3159 #else
3160                         sprintf (rbuf, "0");
3161 #endif /* ifdef HAVE_UNISTD */
3162                         ap = rbuf;
3163                         }
3164                     else if (!strcmp ("PPID", gbuf)) {
3165 #ifdef HAVE_UNISTD
3166                         sprintf (rbuf, "%ld", (long)getppid());
3167 #else
3168                         sprintf (rbuf, "0");
3169 #endif /* ifdef HAVE_UNISTD */
3170                         ap = rbuf;
3171                         }
3172                     else if (!strcmp ("PGID", gbuf)) {
3173 #ifdef HAVE_UNISTD
3174                         sprintf (rbuf, "%ld", (long)getpgid(getpid()));
3175 #else
3176                         sprintf (rbuf, "0");
3177 #endif /* ifdef HAVE_UNISTD */
3178                         ap = rbuf;
3179                         }
3180                     else if (!strcmp ("SID", gbuf)) {
3181 #ifdef HAVE_UNISTD
3182                         sprintf (rbuf, "%ld", (long)getsid(getpid()));
3183 #else
3184                         sprintf (rbuf, "0");
3185 #endif /* ifdef HAVE_UNISTD */
3186                         ap = rbuf;
3187                         }
3188                     else if (!strcmp ("ENDIAN", gbuf)) {
3189 #if ( defined(DECLITEND) && DECLITEND == 1 )
3190                         sprintf (rbuf, "LITTLE");
3191 #elif ( defined(DECLITEND) && DECLITEND == 0 )
3192                         sprintf (rbuf, "BIG");
3193 #else
3194                         sprintf (rbuf, "UNKNOWN");
3195 #endif /* if ( defined(DECLITEND) && DECLITEND == 1 ) */
3196                         ap = rbuf;
3197                         }
3198                     else if (!strcmp("SIM_NAME", gbuf)) {
3199                         sprintf (rbuf, "%s", sim_name);
3200                         ap = rbuf;
3201                         }
3202                     else if (!strcmp("SIM_VERSION", gbuf)) {
3203 #if defined(VER_H_GIT_VERSION)
3204                         sprintf (rbuf, "%s", VER_H_GIT_VERSION);
3205 #else
3206                         sprintf (rbuf, "UNKNOWN");
3207 #endif /* if defined(VER_H_GIT_VERSION) */
3208                         ap = rbuf;
3209                         }
3210                     else if (!strcmp("SIM_HASH", gbuf)) {
3211 #if defined(VER_H_GIT_HASH)
3212                         sprintf (rbuf, "%s", VER_H_GIT_HASH);
3213 #else
3214                         sprintf (rbuf, "0000000000000000000000000000000000000000");
3215 #endif /* if defined(VER_H_GIT_HASH) */
3216                         ap = rbuf;
3217                         }
3218                     else if (!strcmp("SIM_RELT", gbuf)) {
3219 #if defined(VER_H_GIT_RELT)
3220                         sprintf (rbuf, "%s", VER_H_GIT_RELT);
3221 #else
3222                         sprintf (rbuf, "X");
3223 #endif /* if defined(VER_H_GIT_RELT) */
3224                         ap = rbuf;
3225                         }
3226                     else if (!strcmp("SIM_DATE", gbuf)) {
3227 #if defined(VER_H_GIT_DATE)
3228                         sprintf (rbuf, "%s", VER_H_GIT_DATE);
3229 #else
3230                         sprintf (rbuf, "UNKNOWN");
3231 #endif /* if defined(VER_H_GIT_DATE) */
3232                         ap = rbuf;
3233                         }
3234                     else if ( (!strcmp("CPUS", gbuf)) \
3235                       || (!strcmp("PROCESSORS", gbuf) ) ) {
3236 #if defined(LINUX_OS) && !defined(__ANDROID__)
3237                         sprintf(rbuf, "%ld", (long)get_nprocs());
3238 #elif defined ( __HAIKU__ )
3239                         system_info hinfo;
3240                         get_system_info(&hinfo);
3241                         sprintf (rbuf, "%llu",
3242                             (long long unsigned int)hinfo.cpu_count);
3243 #else
3244                         sprintf(rbuf, "1");
3245 #endif /* if defined(LINUX_OS) && !defined(__ANDROID__) */
3246                         ap = rbuf;
3247                         }
3248                     }
3249                 }
3250             if (ap) {                                   /* non-null arg? */
3251                 while (*ap && (op < oend))              /* copy the argument */
3252                     *op++ = *ap++;
3253                 }
3254             }
3255         else
3256             *op++ = *ip++;
3257     }
3258 *op = 0;                                                /* term buffer */
3259 strcpy (instr, tmpbuf);
3260 FREE (tmpbuf);
3261 return;
3262 }
3263 
3264 static
3265 int sim_cmp_string (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
3266 {
3267 long int v1, v2;
3268 char *ep1, *ep2;
3269 
3270 v1 = strtol(s1+1, &ep1, 0);
3271 v2 = strtol(s2+1, &ep2, 0);
3272 if ((ep1 != s1 + strlen (s1) - 1) ||
3273     (ep2 != s2 + strlen (s2) - 1))
3274     return strcmp (s1, s2);
3275 if (v1 == v2)
3276     return 0;
3277 if (v1 < v2)
3278     return -1;
3279 return 1;
3280 }
3281 
3282 /* Assert command */
3283 
3284 t_stat assert_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3285 {
3286 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3287 CONST char *tptr, *gptr;
3288 REG *rptr;
3289 uint32 idx = 0;
3290 t_value val;
3291 t_stat r;
3292 t_bool Not = FALSE;
3293 t_bool result;
3294 t_addr addr = 0;
3295 t_stat reason;
3296 
3297 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3298                                                         /* get sw, default */
3299 sim_stabr.boolop = sim_staba.boolop = -1;               /* no relational op dflt */
3300 if (*cptr == 0)                                         /* must be more */
3301     return SCPE_2FARG;
3302 tptr = get_glyph (cptr, gbuf, 0);                       /* get token */
3303 if (!strcmp (gbuf, "NOT")) {                            /* Conditional Inversion? */
3304     Not = TRUE;                                         /* remember that, and */
3305     cptr = (CONST char *)tptr;
3306     }
3307 if (*cptr == '"') {                                     /* quoted string comparison? */
3308     char op[CBUFSIZE];
3309     static struct {
3310         const char *op;
3311         int aval;
3312         int bval;
3313         t_bool invert;
3314         } *optr, compare_ops[] =
3315         {
3316             { "==",   0,  0, FALSE },
3317             { "EQU",  0,  0, FALSE },
3318             { "!=",   0,  0, TRUE  },
3319             { "NEQ",  0,  0, TRUE  },
3320             { "<",   -1, -1, FALSE },
3321             { "LSS", -1, -1, FALSE },
3322             { "<=",   0, -1, FALSE },
3323             { "LEQ",  0, -1, FALSE },
3324             { ">",    1,  1, FALSE },
3325             { "GTR",  1,  1, FALSE },
3326             { ">=",   0,  1, FALSE },
3327             { "GEQ",  0,  1, FALSE },
3328             { NULL }
3329         };
3330 
3331     tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3332                                                     /* get first string */
3333     if (!*tptr)
3334         return SCPE_2FARG;
3335     cptr += strlen (gbuf);
3336     while (sim_isspace (*cptr))                     /* skip spaces */
3337         ++cptr;
3338     (void)get_glyph (cptr, op, '"');
3339     for (optr = compare_ops; optr->op; optr++)
3340         if (0 == strcmp (op, optr->op))
3341             break;
3342     if (!optr->op)
3343         return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3344     cptr += strlen (op);
3345     while (sim_isspace (*cptr))                         /* skip spaces */
3346         ++cptr;
3347     cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3348                                                         /* get second string */
3349     if (*cptr) {                                        /* more? */
3350         if (flag)                                       /* ASSERT has no more args */
3351             return SCPE_2MARG;
3352         }
3353     else {
3354         if (!flag)
3355             return SCPE_2FARG;                          /* IF needs actions! */
3356         }
3357     result = sim_cmp_string (gbuf, gbuf2);
3358     result = ((result == optr->aval) || (result == optr->bval));
3359     if (optr->invert)
3360         result = !result;
3361     }
3362 else {
3363     cptr = get_glyph (cptr, gbuf, 0);                   /* get register */
3364     rptr = find_reg (gbuf, &gptr, sim_dfdev);           /* parse register */
3365     if (rptr) {                                         /* got register? */
3366         if (*gptr == '[') {                             /* subscript? */
3367             if (rptr->depth <= 1)                       /* array register? */
3368                 return SCPE_ARG;
3369             idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3370             if ((gptr == tptr) || (*tptr++ != ']'))
3371                 return SCPE_ARG;
3372             gptr = tptr;                                /* update */
3373             }
3374         else idx = 0;                                   /* not array */
3375         if (idx >= rptr->depth)                         /* validate subscript */
3376             return SCPE_SUB;
3377         }
3378     else {                                              /* not reg, check for memory */
3379         if (sim_dfdev && sim_vm_parse_addr)             /* get addr */
3380             addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3381         else
3382             addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix);
3383         if (gbuf == gptr)                               /* error? */
3384             return SCPE_NXREG;
3385         }
3386     if (*gptr != 0)                                     /* more? must be search */
3387         (void)get_glyph (gptr, gbuf, 0);
3388     else {
3389         if (*cptr == 0)                                 /* must be more */
3390             return SCPE_2FARG;
3391         cptr = get_glyph (cptr, gbuf, 0);               /* get search cond */
3392         }
3393     if (*cptr) {                                        /* more? */
3394         if (flag)                                       /* ASSERT has no more args */
3395             return SCPE_2MARG;
3396         }
3397     else {
3398         if (!flag)
3399             return SCPE_2FARG;                          /* IF needs actions! */
3400         }
3401     if (rptr) {                                         /* Handle register case */
3402         if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) ||  /* parse condition */
3403             (sim_stabr.boolop == -1))                   /* relational op reqd */
3404             return SCPE_MISVAL;
3405         val = get_rval (rptr, idx);                     /* get register value */
3406         result = test_search (&val, &sim_stabr);        /* test condition */
3407         }
3408     else {                                              /* Handle memory case */
3409         if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) ||  /* parse condition */
3410             (sim_staba.boolop == -1))                    /* relational op reqd */
3411             return SCPE_MISVAL;
3412         reason = get_aval (addr, sim_dfdev, sim_dfunit);/* get data */
3413         if (reason != SCPE_OK)                          /* return if error */
3414             return reason;
3415         result = test_search (sim_eval, &sim_staba);    /* test condition */
3416         }
3417     }
3418 if (Not ^ result) {
3419     if (!flag)
3420         sim_brk_setact (cptr);                          /* set up IF actions */
3421     }
3422 else
3423     if (flag)
3424         return SCPE_AFAIL;                              /* return assert status */
3425 return SCPE_OK;
3426 }
3427 
3428 /* Send command */
3429 
3430 t_stat send_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3431 {
3432 char gbuf[CBUFSIZE];
3433 CONST char *tptr;
3434 uint8 dbuf[CBUFSIZE];
3435 uint32 dsize = 0;
3436 uint32 delay = 0;
3437 uint32 after = 0;
3438 t_stat r;
3439 SEND *snd = NULL;
3440 
3441 GET_SWITCHES (cptr);                                    /* get switches */
3442 tptr = get_glyph (cptr, gbuf, ',');
3443 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3444     r = tmxr_locate_line_send (gbuf, &snd);
3445     if (r != SCPE_OK)
3446       return r;
3447     cptr = tptr;
3448     tptr = get_glyph (tptr, gbuf, ',');
3449     }
3450 else
3451     snd = sim_cons_get_send ();
3452 
3453 while (*cptr) {
3454     if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3455         delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3456         if (r != SCPE_OK)
3457             return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3458         cptr = tptr;
3459         tptr = get_glyph (cptr, gbuf, ',');
3460         continue;
3461         }
3462     if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3463         after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3464         if (r != SCPE_OK)
3465             return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3466         cptr = tptr;
3467         tptr = get_glyph (cptr, gbuf, ',');
3468         continue;
3469         }
3470     if ((*cptr == '"') || (*cptr == '\''))
3471         break;
3472     return SCPE_ARG;
3473     }
3474 if (*cptr) {
3475     if ((*cptr != '"') && (*cptr != '\'')) //-V560
3476         return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3477     cptr = get_glyph_quoted (cptr, gbuf, 0);
3478     if (*cptr != '\0')
3479         return SCPE_2MARG;                  /* No more arguments */
3480 
3481     if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3482         return sim_messagef (SCPE_ARG, "Invalid String\n");
3483     }
3484 if ((dsize == 0) && (delay == 0) && (after == 0))
3485     return SCPE_2FARG;
3486 return sim_send_input (snd, dbuf, dsize, after, delay);
3487 }
3488 
3489 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3490 {
3491 char gbuf[CBUFSIZE];
3492 CONST char *tptr;
3493 t_stat r;
3494 SEND *snd = NULL;
3495 
3496 tptr = get_glyph (cptr, gbuf, ',');
3497 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3498     r = tmxr_locate_line_send (gbuf, &snd);
3499     if (r != SCPE_OK)
3500       return r;
3501     cptr = tptr;
3502     }
3503 else
3504     snd = sim_cons_get_send ();
3505 if (*cptr)
3506     return SCPE_2MARG;
3507 return sim_show_send_input (st, snd);
3508 }
3509 
3510 t_stat expect_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3511 {
3512 char gbuf[CBUFSIZE];
3513 CONST char *tptr;
3514 EXPECT *exp = NULL;
3515 
3516 GET_SWITCHES (cptr);                                    /* get switches */
3517 tptr = get_glyph (cptr, gbuf, ',');
3518 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3519     cptr = tptr;
3520     }
3521 else
3522     exp = sim_cons_get_expect ();
3523 if (flag)
3524     return sim_set_expect (exp, cptr);
3525 else
3526     return sim_set_noexpect (exp, cptr);
3527 }
3528 
3529 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3530 {
3531 char gbuf[CBUFSIZE];
3532 CONST char *tptr;
3533 EXPECT *exp = NULL;
3534 t_stat r;
3535 
3536 tptr = get_glyph (cptr, gbuf, ',');
3537 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3538     r = tmxr_locate_line_expect (gbuf, &exp);
3539     if (r != SCPE_OK)
3540         return r;
3541     cptr = tptr;
3542     }
3543 else
3544     exp = sim_cons_get_expect ();
3545 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3546     return SCPE_ARG;            /* String must be quote delimited */
3547 tptr = get_glyph_quoted (cptr, gbuf, 0);
3548 if (*tptr != '\0')
3549     return SCPE_2MARG;          /* No more arguments */
3550 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3551     return SCPE_ARG;            /* String must be quote delimited */
3552 return sim_exp_show (st, exp, gbuf);
3553 }
3554 
3555 /* Goto command */
3556 
3557 t_stat goto_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3558 {
3559 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3560 const char *cptr;
3561 long fpos;
3562 int32 saved_do_echo = sim_do_echo;
3563 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3564 
3565 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3566 (void)get_glyph (fcptr, gbuf1, 0);
3567 if ('\0' == gbuf1[0]) return SCPE_ARG;                  /* unspecified goto target */
3568 fpos = ftell(sim_gotofile);                             /* Save start position */
3569 rewind(sim_gotofile);                                   /* start search for label */
3570 sim_goto_line[sim_do_depth] = 0;                        /* reset line number */
3571 sim_do_echo = 0;                                        /* Don't echo while searching for label */
3572 while (1) {
3573     cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3574     if (cptr == NULL) break;                            /* exit on eof */
3575     sim_goto_line[sim_do_depth] += 1;                   /* record line number */
3576     if (*cptr == 0) continue;                           /* ignore blank */
3577     if (*cptr != ':') continue;                         /* ignore non-labels */
3578     ++cptr;                                             /* skip : */
3579     while (sim_isspace (*cptr)) ++cptr;                 /* skip blanks */
3580     cptr = get_glyph (cptr, gbuf, 0);                   /* get label glyph */
3581     if (0 == strcmp(gbuf, gbuf1)) {
3582         sim_brk_clract ();                              /* goto defangs current actions */
3583         sim_do_echo = saved_do_echo;                    /* restore echo mode */
3584         if (sim_do_echo)                                /* echo if -v */
3585             sim_printf("%s> %s\n", do_position(), cbuf);
3586         return SCPE_OK;
3587         }
3588     }
3589 sim_do_echo = saved_do_echo;                       /* restore echo mode         */
3590 fseek(sim_gotofile, fpos, SEEK_SET);               /* restore start position    */
3591 sim_goto_line[sim_do_depth] = saved_goto_line;     /* restore start line number */
3592 return SCPE_ARG;
3593 }
3594 
3595 /* Return command */
3596 
3597 /* The return command is invalid unless encountered in a do_cmd context,    */
3598 /* and in that context, it is handled as a special case inside of do_cmd()  */
3599 /* and not dispatched here, so if we get here a return has been issued from */
3600 /* interactive input */
3601 
3602 t_stat return_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3603 {
3604 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3605 }
3606 
3607 /* Shift command */
3608 
3609 /* The shift command is invalid unless encountered in a do_cmd context,    */
3610 /* and in that context, it is handled as a special case inside of do_cmd() */
3611 /* and not dispatched here, so if we get here a shift has been issued from */
3612 /* interactive input (it is not valid interactively since it would have to */
3613 /* mess with the program's argv which is owned by the C runtime library    */
3614 
3615 t_stat shift_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3616 {
3617 return SCPE_UNK;                                 /* only valid inside of do_cmd */
3618 }
3619 
3620 /* Call command */
3621 
3622 /* The call command is invalid unless encountered in a do_cmd context,     */
3623 /* and in that context, it is handled as a special case inside of do_cmd() */
3624 /* and not dispatched here, so if we get here a call has been issued from  */
3625 /* interactive input                                                       */
3626 
3627 t_stat call_cmd (int32 flag, CONST char *fcptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3628 {
3629 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3630 const char *cptr;
3631 
3632 if (NULL == sim_gotofile) return SCPE_UNK;              /* only valid inside of do_cmd */
3633 cptr = get_glyph (fcptr, gbuf, 0);
3634 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified goto target */
3635 snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3636 sim_switches |= SWMASK ('O');                           /* inherit ON state and actions */
3637 return do_cmd_label (flag, cbuf, gbuf);
3638 }
3639 
3640 /* On command */
3641 
3642 t_stat on_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3643 {
3644 char gbuf[CBUFSIZE];
3645 t_stat cond;
3646 
3647 cptr = get_glyph (cptr, gbuf, 0);
3648 if ('\0' == gbuf[0]) return SCPE_ARG;                   /* unspecified condition */
3649 if (0 == strcmp("ERROR", gbuf))
3650     cond = 0;
3651 else
3652     if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3653         return SCPE_ARG;
3654 if ((NULL == cptr) || ('\0' == *cptr)) {                /* Empty Action */
3655     FREE(sim_on_actions[sim_do_depth][cond]);           /* Clear existing condition */
3656     sim_on_actions[sim_do_depth][cond] = NULL; }
3657 else {
3658     sim_on_actions[sim_do_depth][cond] =
3659         (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3660     if (!sim_on_actions[sim_do_depth][cond])
3661       {
3662         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
3663                  __func__, __FILE__, __LINE__);
3664 #if defined(USE_BACKTRACE)
3665 # ifdef SIGUSR2
3666         (void)raise(SIGUSR2);
3667         /*NOTREACHED*/ /* unreachable */
3668 # endif /* ifdef SIGUSR2 */
3669 #endif /* if defined(USE_BACKTRACE) */
3670         abort();
3671       }
3672     strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3673     }
3674 return SCPE_OK;
3675 }
3676 
3677 /* noop command */
3678 
3679 t_stat noop_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3680 {
3681 if (cptr && (*cptr != 0))                               /* now eol? */
3682     return SCPE_2MARG;
3683 return SCPE_OK;                                         /* we're happy doing nothing */
3684 }
3685 
3686 /* Set on/noon routine */
3687 
3688 t_stat set_on (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3689 {
3690 if ((flag) && (cptr) && (*cptr)) {                      /* Set ON with arg */
3691     char gbuf[CBUFSIZE];
3692 
3693     cptr = get_glyph (cptr, gbuf, 0);                   /* get command glyph */
3694     if (((MATCH_CMD(gbuf,"INHERIT")) &&
3695          (MATCH_CMD(gbuf,"NOINHERIT"))) || //-V600
3696         (*cptr))
3697         return SCPE_2MARG;
3698     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT"))) //-V560
3699         sim_on_inherit = 1;
3700     if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT"))) //-V560
3701         sim_on_inherit = 0;
3702     return SCPE_OK;
3703     }
3704 if (cptr && (*cptr != 0))                               /* now eol? */
3705     return SCPE_2MARG;
3706 sim_on_check[sim_do_depth] = flag;
3707 if ((sim_do_depth != 0) &&
3708     (NULL == sim_on_actions[sim_do_depth][0])) {        /* default handler set? */
3709     sim_on_actions[sim_do_depth][0] =                   /* No, so make "RETURN" */
3710         (char *)malloc(1+strlen("RETURN"));             /* be the default action */
3711     strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
3712     }
3713 if ((sim_do_depth != 0) &&
3714     (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
3715     sim_on_actions[sim_do_depth][SCPE_AFAIL] =          /* No, so make "RETURN" */
3716         (char *)malloc(1+strlen("RETURN"));             /* be the action */
3717     strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
3718     }
3719 return SCPE_OK;
3720 }
3721 
3722 /* Set verify/noverify routine */
3723 
3724 t_stat set_verify (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3725 {
3726 if (cptr && (*cptr != 0))                               /* now eol? */
3727     return SCPE_2MARG;
3728 if (flag == sim_do_echo)                                /* already set correctly? */
3729     return SCPE_OK;
3730 sim_do_echo = flag;
3731 return SCPE_OK;
3732 }
3733 
3734 /* Set message/nomessage routine */
3735 
3736 t_stat set_message (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3737 {
3738 if (cptr && (*cptr != 0))                               /* now eol? */
3739     return SCPE_2MARG;
3740 if (flag == sim_show_message)                           /* already set correctly? */
3741     return SCPE_OK;
3742 sim_show_message = flag;
3743 return SCPE_OK;
3744 }
3745 
3746 /* Set localopc/nolocalopc routine */
3747 
3748 t_stat set_localopc (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3749 {
3750 if (cptr && (*cptr != 0))                               /* now eol? */
3751     return SCPE_2MARG;
3752 if (flag == sim_localopc)                               /* already set correctly? */
3753     return SCPE_OK;
3754 sim_localopc = flag;
3755 return SCPE_OK;
3756 }
3757 /* Set quiet/noquiet routine */
3758 
3759 t_stat set_quiet (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3760 {
3761 if (cptr && (*cptr != 0))                               /* now eol? */
3762     return SCPE_2MARG;
3763 if (flag == sim_quiet)                                  /* already set correctly? */
3764     return SCPE_OK;
3765 sim_quiet = flag;
3766 return SCPE_OK;
3767 }
3768 
3769 /* Set environment routine */
3770 
3771 t_stat sim_set_environment (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3772 {
3773 char varname[CBUFSIZE];
3774 
3775 if ((!cptr) || (*cptr == 0))                            /* now eol? */
3776     return SCPE_2FARG;
3777 cptr = get_glyph (cptr, varname, '=');                  /* get environment variable name */
3778 setenv(varname, cptr, 1);
3779 return SCPE_OK;
3780 }
3781 
3782 /* Set command */
3783 
3784 t_stat set_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3785 {
3786 uint32 lvl = 0;
3787 t_stat r;
3788 char gbuf[CBUFSIZE], *cvptr;
3789 CONST char *svptr;
3790 DEVICE *dptr;
3791 UNIT *uptr;
3792 MTAB *mptr;
3793 CTAB *gcmdp;
3794 C1TAB *ctbr = NULL, *glbr;
3795 
3796 GET_SWITCHES (cptr);                                    /* get switches */
3797 if (*cptr == 0)                                         /* must be more */
3798     return SCPE_2FARG;
3799 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get glob/dev/unit */
3800 
3801 if ((dptr = find_dev (gbuf))) {                         /* device match? */
3802     uptr = dptr->units;                                 /* first unit */
3803     ctbr = set_dev_tab;                                 /* global table */
3804     lvl = MTAB_VDV;                                     /* device match */
3805     GET_SWITCHES (cptr);                                /* get more switches */
3806     }
3807 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
3808     if (uptr == NULL)                                   /* invalid unit */
3809         return SCPE_NXUN;
3810     ctbr = set_unit_tab;                                /* global table */
3811     lvl = MTAB_VUN;                                     /* unit match */
3812     GET_SWITCHES (cptr);                                /* get more switches */
3813     }
3814 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) {    /* global? */
3815     GET_SWITCHES (cptr);                                /* get more switches */
3816     return gcmdp->action (gcmdp->arg, cptr);            /* do the rest */
3817     }
3818 else {
3819     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3820         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
3821             *cvptr++ = 0;
3822         for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
3823             if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
3824                 dptr = sim_dflt_dev;
3825                 cptr = svptr;
3826                 while (sim_isspace(*cptr))
3827                     ++cptr;
3828                 break;
3829                 }
3830             }
3831         }
3832     if (!dptr)
3833         return SCPE_NXDEV;                              /* no match */
3834     lvl = MTAB_VDV;                                     /* device match */
3835     uptr = dptr->units;                                 /* first unit */
3836     }
3837 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
3838     return SCPE_2FARG;
3839 GET_SWITCHES (cptr);                                    /* get more switches */
3840 
3841 while (*cptr != 0) {                                    /* do all mods */
3842     cptr = get_glyph (svptr = cptr, gbuf, ',');         /* get modifier */
3843     if (0 == strcmp (gbuf, ";"))
3844         break;
3845     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
3846         *cvptr++ = 0;
3847     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3848         if ((mptr->mstring) &&                          /* match string */
3849             (MATCH_CMD (gbuf, mptr->mstring) == 0)) {   /* matches option? */
3850             if (mptr->mask & MTAB_XTD) {                /* extended? */
3851                 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
3852                     return SCPE_ARG;
3853                 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
3854                     return SCPE_UDIS;                   /* unit disabled? */
3855                 if (mptr->valid) {                      /* validation rtn? */
3856                     if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
3857                         svptr = get_glyph_quoted (svptr, gbuf, ',');
3858                         if ((cvptr = strchr (gbuf, '='))) {
3859                             *cvptr++ = 0;
3860                             cptr = svptr;
3861                             }
3862                         }
3863                     else {
3864                         if (cvptr && MODMASK(mptr,MTAB_NC)) {
3865                             (void)get_glyph_nc (svptr, gbuf, ',');
3866                             if ((cvptr = strchr (gbuf, '=')))
3867                                 *cvptr++ = 0;
3868                             }
3869                         }
3870                     r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
3871                     if (r != SCPE_OK)
3872                         return r;
3873                     }
3874                 else if (!mptr->desc)                   /* value desc? */
3875                     break;
3876                 else if (cvptr)                         /* = value? */
3877                     return SCPE_ARG;
3878                 else *((int32 *) mptr->desc) = mptr->match;
3879                 }                                       /* end if xtd */
3880             else {                                      /* old style */
3881                 if (cvptr)                              /* = value? */
3882                     return SCPE_ARG;
3883                 if (uptr->flags & UNIT_DIS)             /* disabled? */
3884                      return SCPE_UDIS;
3885                 if ((mptr->valid) &&                    /* invalid? */
3886                     ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
3887                     return r;
3888                 uptr->flags = (uptr->flags & ~(mptr->mask)) |
3889                     (mptr->match & mptr->mask);         /* set new value */
3890                 }                                       /* end else xtd */
3891             break;                                      /* terminate for */
3892             }                                           /* end if match */
3893         }                                               /* end for */
3894     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
3895         if ((glbr = find_c1tab (ctbr, gbuf))) {         /* global match? */
3896             r = glbr->action (dptr, uptr, glbr->arg, cvptr);    /* do global */
3897             if (r != SCPE_OK)
3898                 return r;
3899             }
3900         else if (!dptr->modifiers)                      /* no modifiers? */
3901             return SCPE_NOPARAM;
3902         else return SCPE_NXPAR;
3903         }                                               /* end if no mat */
3904     }                                                   /* end while */
3905 return SCPE_OK;                                         /* done all */
3906 }
3907 
3908 /* Match CTAB/CTAB1 name */
3909 
3910 CTAB *find_ctab (CTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
3911 {
3912 if (!tab)
3913     return NULL;
3914 for (; tab->name != NULL; tab++) {
3915     if (MATCH_CMD (gbuf, tab->name) == 0)
3916         return tab;
3917     }
3918 return NULL;
3919 }
3920 
3921 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
3922 {
3923 if (!tab)
3924     return NULL;
3925 for (; tab->name != NULL; tab++) {
3926     if (MATCH_CMD (gbuf, tab->name) == 0)
3927         return tab;
3928     }
3929 return NULL;
3930 }
3931 
3932 /* Set device data radix routine */
3933 
3934 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3935 {
3936 if (cptr)
3937     return SCPE_ARG;
3938 dptr->dradix = flag & 037;
3939 return SCPE_OK;
3940 }
3941 
3942 /* Set device enabled/disabled routine */
3943 
3944 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3945 {
3946 UNIT *up;
3947 uint32 i;
3948 
3949 if (cptr)
3950     return SCPE_ARG;
3951 if ((dptr->flags & DEV_DISABLE) == 0)                   /* allowed? */
3952     return SCPE_NOFNC;
3953 if (flag) {                                             /* enable? */
3954     if ((dptr->flags & DEV_DIS) == 0)                   /* already enb? ok */
3955         return SCPE_OK;
3956     dptr->flags = dptr->flags & ~DEV_DIS;               /* no, enable */
3957     }
3958 else {
3959     if (dptr->flags & DEV_DIS)                          /* already dsb? ok */
3960         return SCPE_OK;
3961     for (i = 0; i < dptr->numunits; i++) {              /* check units */
3962         up = (dptr->units) + i;                         /* att or active? */
3963         if ((up->flags & UNIT_ATT) || sim_is_active (up))
3964             return SCPE_NOFNC;                          /* can't do it */
3965         }
3966     dptr->flags = dptr->flags | DEV_DIS;                /* disable */
3967     }
3968 if (dptr->reset)                                        /* reset device */
3969     return dptr->reset (dptr);
3970 else return SCPE_OK;
3971 }
3972 
3973 /* Set unit enabled/disabled routine */
3974 
3975 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3976 {
3977 if (cptr)
3978     return SCPE_ARG;
3979 if (!(uptr->flags & UNIT_DISABLE))                      /* allowed? */
3980     return SCPE_NOFNC;
3981 if (flag)                                               /* enb? enable */
3982     uptr->flags = uptr->flags & ~UNIT_DIS;
3983 else {
3984     if ((uptr->flags & UNIT_ATT) ||                     /* dsb */
3985         sim_is_active (uptr))                           /* more tests */
3986         return SCPE_NOFNC;
3987     uptr->flags = uptr->flags | UNIT_DIS;               /* disable */
3988     }
3989 return SCPE_OK;
3990 }
3991 
3992 /* Set device debug enabled/disabled routine */
3993 
3994 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
3995 {
3996 char gbuf[CBUFSIZE];
3997 DEBTAB *dep;
3998 
3999 if ((dptr->flags & DEV_DEBUG) == 0)
4000     return SCPE_NOFNC;
4001 if (cptr == NULL) {                                     /* no arguments? */
4002     dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
4003     if (flag && dptr->debflags) {                       /* enable with table? */
4004         for (dep = dptr->debflags; dep->name != NULL; dep++)
4005             dptr->dctrl = dptr->dctrl | dep->mask;      /* set all */
4006         }
4007     return SCPE_OK;
4008     }
4009 if (dptr->debflags == NULL)                             /* must have table */
4010     return SCPE_ARG;
4011 while (*cptr) {
4012     cptr = get_glyph (cptr, gbuf, ';');                 /* get debug flag */
4013     for (dep = dptr->debflags; dep->name != NULL; dep++) {
4014         if (strcmp (dep->name, gbuf) == 0) {            /* match? */
4015             if (flag)
4016                 dptr->dctrl = dptr->dctrl | dep->mask;
4017             else dptr->dctrl = dptr->dctrl & ~dep->mask;
4018             break;
4019             }
4020         }                                               /* end for */
4021     if (dep->mask == 0)                                 /* no match? */
4022         return SCPE_ARG;
4023     }                                                   /* end while */
4024 return SCPE_OK;
4025 }
4026 
4027 /* Show command */
4028 
4029 t_stat show_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4030 {
4031 t_stat r = SCPE_IERR;
4032 
4033 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
4034                                                         /* get sw, ofile */
4035 if (!cptr)                                              /* error? */
4036     return r;
4037 if (sim_ofile) {                                        /* output file? */
4038     r = show_cmd_fi (sim_ofile, flag, cptr);            /* do show */
4039     fclose (sim_ofile);
4040     }
4041 else {
4042     r = show_cmd_fi (stdout, flag, cptr);               /* no, stdout, log */
4043     if (sim_log && (sim_log != stdout))
4044         show_cmd_fi (sim_log, flag, cptr);
4045     if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
4046         show_cmd_fi (sim_deb, flag, cptr);
4047     }
4048 return r;
4049 }
4050 
4051 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4052 {
4053 uint32 lvl = 0xFFFFFFFF;
4054 char gbuf[CBUFSIZE], *cvptr;
4055 CONST char *svptr;
4056 DEVICE *dptr;
4057 UNIT *uptr;
4058 MTAB *mptr;
4059 SHTAB *shtb = NULL, *shptr;
4060 
4061 GET_SWITCHES (cptr);                                    /* get switches */
4062 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#'))   /* must be more */
4063     return SCPE_2FARG;
4064 cptr = get_glyph (svptr = cptr, gbuf, 0);               /* get next glyph */
4065 
4066 if ((dptr = find_dev (gbuf))) {                         /* device match? */
4067     uptr = dptr->units;                                 /* first unit */
4068     shtb = show_dev_tab;                                /* global table */
4069     lvl = MTAB_VDV;                                     /* device match */
4070     GET_SWITCHES (cptr);                                /* get more switches */
4071     }
4072 else if ((dptr = find_unit (gbuf, &uptr))) {            /* unit match? */
4073     if (uptr == NULL)                                   /* invalid unit */
4074         return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
4075     if (uptr->flags & UNIT_DIS)                         /* disabled? */
4076         return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
4077     shtb = show_unit_tab;                               /* global table */
4078     lvl = MTAB_VUN;                                     /* unit match */
4079     GET_SWITCHES (cptr);                                /* get more switches */
4080     }
4081 else if ((shptr = find_shtab (show_glob_tab, gbuf))) {  /* global? */
4082     GET_SWITCHES (cptr);                                /* get more switches */
4083     return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
4084     }
4085 else {
4086     if (sim_dflt_dev && sim_dflt_dev->modifiers) {
4087         if ((cvptr = strchr (gbuf, '=')))               /* = value? */
4088             *cvptr++ = 0;
4089         for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
4090             if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
4091                  (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) || //-V600
4092                 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
4093                 dptr = sim_dflt_dev;
4094                 lvl = MTAB_VDV;                         /* device match */
4095                 cptr = svptr;
4096                 while (sim_isspace(*cptr))
4097                     ++cptr;
4098                 break;
4099                 }
4100             }
4101         }
4102     if (!dptr) {
4103         if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf)))  /* global match? */
4104             return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
4105         else
4106             return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
4107         }
4108     }
4109 
4110 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
4111     return (lvl == MTAB_VDV)?
4112         show_device (ofile, dptr, 0):
4113         show_unit (ofile, dptr, uptr, -1);
4114     }
4115 GET_SWITCHES (cptr);                                    /* get more switches */
4116 
4117 while (*cptr != 0) {                                    /* do all mods */
4118     cptr = get_glyph (cptr, gbuf, ',');                 /* get modifier */
4119     if ((cvptr = strchr (gbuf, '=')))                   /* = value? */
4120         *cvptr++ = 0;
4121     for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
4122         if (((mptr->mask & MTAB_XTD)?                   /* right level? */
4123             ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
4124             ((mptr->disp && mptr->pstring &&            /* named disp? */
4125             (MATCH_CMD (gbuf, mptr->pstring) == 0))
4126             )) {
4127             if (cvptr && !MODMASK(mptr,MTAB_SHP))
4128                 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
4129             show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
4130             break;
4131             }                                           /* end if */
4132         }                                               /* end for */
4133     if (!mptr || (mptr->mask == 0)) {                   /* no match? */
4134         if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
4135             t_stat r;
4136 
4137             r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
4138             if (r != SCPE_OK)
4139                 return r;
4140             }
4141         else {
4142             if (!dptr->modifiers)                       /* no modifiers? */
4143                 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
4144             else
4145                 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
4146             }
4147         }                                               /* end if */
4148     }                                                   /* end while */
4149 return SCPE_OK;
4150 }
4151 
4152 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
4153 {
4154 if (!tab)
4155     return NULL;
4156 for (; tab->name != NULL; tab++) {
4157     if (MATCH_CMD (gbuf, tab->name) == 0)
4158         return tab;
4159     }
4160 return NULL;
4161 }
4162 
4163 /* Show device and unit */
4164 
4165 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4166 {
4167 uint32 j, udbl, ucnt;
4168 UNIT *uptr;
4169 int32 toks = 0;
4170 
4171 if (strcmp(sim_dname (dptr),"SYS") != 0) {
4172 fprintf (st, "%s", sim_dname (dptr));                   /* print dev name */
4173 if ((flag == 2) && dptr->description) {
4174     fprintf (st, "\t%s\n", dptr->description(dptr));
4175     }
4176 else {
4177     if ((sim_switches & SWMASK ('D')) && dptr->description)
4178         fprintf (st, "\t%s\n", dptr->description(dptr));
4179     }
4180 if (qdisable (dptr)) {                                  /* disabled? */
4181     fprintf (st, "\tdisabled\n");
4182     return SCPE_OK;
4183     }
4184 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) {    /* count units */
4185     uptr = dptr->units + j;
4186     if (!(uptr->flags & UNIT_DIS))                      /* count enabled units */
4187         ucnt++;
4188     else if (uptr->flags & UNIT_DISABLE)
4189         udbl++;                                         /* count user-disabled */
4190     }
4191 //show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
4192 if (dptr->numunits == 0) {
4193     if (toks) //-V547
4194         fprintf (st, "\n");
4195     }
4196 else {
4197     if (ucnt == 0) {
4198         fprint_sep (st, &toks);
4199         fprintf (st, "all units disabled\n");
4200         }
4201     else if ((ucnt + udbl) == 1) {
4202         fprint_sep (st, &toks);
4203         fprintf (st, " 1 unit\n");
4204         }
4205     else if ((ucnt > 1) || (udbl > 0)) {
4206         fprint_sep (st, &toks);
4207         fprintf (st, "%2.d units\n", ucnt + udbl);
4208         }
4209     else
4210         if ((flag != 2) || !dptr->description || toks)
4211             fprintf (st, "\n");
4212     toks = 0;
4213     }
4214 if (flag)                                               /* dev only? */
4215     return SCPE_OK;
4216 for (j = 0; j < dptr->numunits; j++) {                  /* loop thru units */
4217     uptr = dptr->units + j;
4218     if ((uptr->flags & UNIT_DIS) == 0)
4219         show_unit (st, dptr, uptr, ucnt + udbl);
4220     }
4221 }
4222 return SCPE_OK;
4223 }
4224 
4225 void fprint_sep (FILE *st, int32 *tokens)
     /* [previous][next][first][last][top][bottom][index][help] */
4226 {
4227 fprintf (st, "%s", (*tokens > 0) ? "" : "\t");
4228 *tokens += 1;
4229 }
4230 
4231 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
4232 {
4233 int32 u = (int32)(uptr - dptr->units);
4234 int32 toks = 0;
4235 
4236 if (flag > 1)
4237     fprintf (st, "   %s%d \n", sim_dname (dptr), u);
4238 else if (flag < 0)
4239     fprintf (st, " %s%d ", sim_dname (dptr), u);
4240 if (uptr->flags & UNIT_ATT) {
4241     fprint_sep (st, &toks);
4242     fprintf (st, "status   : attached to %s", uptr->filename);
4243     if (uptr->flags & UNIT_RO)
4244         fprintf (st, ", read only");
4245     }
4246 else {
4247     if (uptr->flags & UNIT_ATTABLE) {
4248         fprint_sep (st, &toks);
4249         fprintf (st, "status   : not attached");
4250         }
4251     }
4252 if ((uptr->capac > 0) && (uptr->flags & UNIT_FIX)) {
4253     fprint_sep (st, &toks);
4254     fprint_capac (st, dptr, uptr);
4255     }
4256 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks);        /* show unit mods */
4257 if (toks || (flag < 0) || (flag > 1))
4258     fprintf (st, "\n");
4259 return SCPE_OK;
4260 }
4261 
4262 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4263 {
4264 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4265 t_offset kval = (t_offset)((uptr->flags & UNIT_BINK) ? 1024: 1000);
4266 t_offset mval;
4267 t_offset psize = (t_offset)uptr->capac;
4268 char *scale, *width;
4269 
4270 if (sim_switches & SWMASK ('B'))
4271     kval = 1024;
4272 mval = kval * kval;
4273 if (dptr->flags & DEV_SECTORS) {
4274     kval = kval / 512;
4275     mval = mval / 512;
4276     }
4277 if ((dptr->dwidth / dptr->aincr) > 8)
4278     width = "W";
4279 else
4280     width = "B";
4281 if ((psize < (kval * 10)) &&
4282     (0 != (psize % kval))) {
4283     scale = "";
4284     }
4285 else if ((psize < (mval * 10)) &&
4286          (0 != (psize % mval))){
4287     scale = "K";
4288     psize = psize / kval;
4289     }
4290 else {
4291     scale = "M";
4292     psize = psize / mval;
4293     }
4294 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4295 sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4296 return capac_buf;
4297 }
4298 
4299 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4300 {
4301 fprintf (st, " %s", sprint_capac (dptr, uptr));
4302 }
4303 
4304 /* Show <global name> processors  */
4305 
4306 extern void print_default_base_system_script (void);
4307 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] */
4308 {
4309 #ifndef PERF_STRIP
4310   print_default_base_system_script();
4311 #endif /* ifndef PERF_STRIP */
4312   return 0;
4313 }
4314 
4315 static void printp (unsigned char * PROM, char * label, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4316   sim_printf (" %s ", label);
4317   sim_printf ("   %2d     %3o(8)     '", length, offset);
4318   for (int l = 0; l < length; l ++)
4319     {
4320       unsigned int byte = PROM[offset + l];
4321       if (byte == 255)
4322         {
4323           byte = ' ';
4324         }
4325       sim_printf (isprint (byte) ? "%c" : "\\%03o", byte);
4326     }
4327   sim_printf ("'\r\n");
4328 }
4329 
4330 static void strip_spaces(char* str) {
     /* [previous][next][first][last][top][bottom][index][help] */
4331   int i, x;
4332   for (i=x=0; str[i]; ++i)
4333     {
4334       if (!isspace(str[i]) || (i > 0 && !isspace(str[i-1]))) //-V781
4335         {
4336           str[x++] = str[i];
4337         }
4338     }
4339   str[x] = '\0';
4340   i = -1;
4341   x = 0;
4342   while (str[x] != '\0')
4343     {
4344       if (str[x] != ' ' && str[x] != '\t' && str[x] != '\n')
4345         {
4346           i=x;
4347         }
4348       x++;
4349     }
4350   str[i+1] = '\0';
4351 }
4352 
4353 static void printpq (unsigned char * PROM, FILE * st, int offset, int length) {
     /* [previous][next][first][last][top][bottom][index][help] */
4354   char sx[1024];
4355   sx[1023] = '\0';
4356   unsigned int lastbyte = 0;
4357   for (int l = 0; l < length; l ++)
4358     {
4359       unsigned int byte = PROM[offset + l];
4360       if (byte == 255)
4361         {
4362           byte = 20;
4363         }
4364       if ((lastbyte != 20) && (byte != 20))
4365         {
4366           sprintf(&sx[l], isprint (byte) ? "%c" : " ", byte);
4367         }
4368       lastbyte = byte;
4369     }
4370   strip_spaces(sx);
4371   fprintf (st, "%s", sx);
4372 }
4373 
4374 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4375 {
4376   unsigned char PROM[1024];
4377   setupPROM (0, PROM);
4378 
4379   sim_printf (" PROM size: %llu bytes\r\n",
4380               (long long unsigned)sizeof(PROM));
4381   sim_printf (" PROM initialization data:\r\n\r\n");
4382 
4383   sim_printf ("     Field Description      Length   Offset              Contents\r\n");
4384   sim_printf (" ========================= ======== ======== ==================================\r\n");
4385   sim_printf ("\r\n");
4386 
4387   //                      Field                 Offset       Length
4388   //             -------------------------    ----------   ----------
4389   printp (PROM, "CPU Model                ",       0,          11);
4390   printp (PROM, "CPU Serial               ",      11,          11);
4391   printp (PROM, "Ship Date                ",      22,           6);
4392   printp (PROM, "PROM Layout Version      ",      60,           1);
4393   printp (PROM, "Release Git Commit Date  ",      70,          10);
4394   printp (PROM, "Release Major            ",      80,           3);
4395   printp (PROM, "Release Minor            ",      83,           3);
4396   printp (PROM, "Release Patch            ",      86,           3);
4397   printp (PROM, "Release Iteration        ",      89,           3);
4398   printp (PROM, "Release Build Number     ",      92,           8);  /* Reserved */
4399   printp (PROM, "Release Type             ",     100,           1);
4400   printp (PROM, "Release Version Text     ",     101,          29);
4401   printp (PROM, "Build Architecture       ",     130,          20);
4402   printp (PROM, "Build Operating System   ",     150,          20);
4403   printp (PROM, "Target Architecture      ",     170,          20);
4404   printp (PROM, "Target Operating System  ",     190,          20);
4405 
4406   sim_printf("\r\n");
4407   return 0;
4408 }
4409 
4410 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4411 {
4412     fprintf (st, "\r\n\r Build Information:\n");
4413 #if defined(BUILDINFO_scp) && defined(SYSDEFS_USED)
4414     fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4415 # ifndef __OPEN64__
4416     fprintf (st, "\r\n  Relevant definitions: %s\n", SYSDEFS_USED );
4417 # endif
4418 #elif defined(BUILDINFO_scp)
4419     fprintf (st, "\r\n      Compilation info: %s\n", BUILDINFO_scp );
4420 #else
4421     fprintf (st, "\r\n      Compilation info: Not available\n" );
4422 #endif
4423 #if defined (UV_VERSION_MAJOR) && \
4424     defined (UV_VERSION_MINOR) && \
4425     defined (UV_VERSION_PATCH)
4426 # ifdef UV_VERSION_MAJOR
4427 #  ifndef UV_VERSION_MINOR
4428 #   ifndef UV_VERSION_PATCH
4429 #    ifndef UV_VERSION_SUFFIX
4430     fprintf (st, "\r\n    Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4431 #    endif /* ifndef UV_VERSION_SUFFIX */
4432 #   endif /* ifndef UV_VERSION_PATCH */
4433 #  endif /* ifndef UV_VERSION_MINOR */
4434 #  ifdef UV_VERSION_MINOR
4435 #   ifndef UV_VERSION_PATCH
4436 #    ifndef UV_VERSION_SUFFIX
4437     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4438             UV_VERSION_MINOR);
4439 #    endif /* ifndef UV_VERSION_SUFFIX */
4440 #   endif /* ifndef UV_VERSION_PATCH */
4441 #   ifdef UV_VERSION_PATCH
4442 #    ifndef UV_VERSION_SUFFIX
4443     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4444             UV_VERSION_MINOR, UV_VERSION_PATCH);
4445 #    endif /* ifndef UV_VERSION_SUFFIX */
4446 #    ifdef UV_VERSION_SUFFIX
4447     fprintf (st, "\r\n    Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4448             UV_VERSION_MINOR, UV_VERSION_PATCH);
4449 #     ifdef UV_VERSION_IS_RELEASE
4450 #      if UV_VERSION_IS_RELEASE == 1
4451 #       define UV_RELEASE_TYPE " (release)"
4452 #      endif /* if UV_VERSION_IS_RELEASE == 1 */
4453 #      if UV_VERSION_IS_RELEASE == 0
4454 #       define UV_RELEASE_TYPE "-dev"
4455 #      endif /* if UV_VERSION_IS_RELEASE == 0 */
4456 #      ifndef UV_RELEASE_TYPE
4457 #       define UV_RELEASE_TYPE ""
4458 #      endif /* ifndef UV_RELEASE_TYPE */
4459 #      ifdef UV_RELEASE_TYPE
4460     fprintf (st, "%s", UV_RELEASE_TYPE);
4461 #      endif /* ifdef UV_RELEASE_TYPE */
4462 #     endif /* ifdef UV_VERSION_IS_RELEASE */
4463 #    endif /* ifdef UV_VERSION_SUFFIX */
4464 #   endif /* ifdef UV_VERSION_PATCH */
4465 #  endif /* ifdef UV_VERSION_MINOR */
4466     unsigned int CurrentUvVersion = uv_version();
4467     if (CurrentUvVersion > 0)
4468         if (uv_version_string() != NULL)
4469             fprintf (st, "; %s in use", uv_version_string());
4470 # endif /* ifdef UV_VERSION_MAJOR */
4471 #else
4472     fprintf (st, "\r\n    Event loop library: Using libuv (or compatible) library, unknown version");
4473 #endif /* if defined(UV_VERSION_MAJOR) &&  \
4474         *    defined(UV_VERSION_MINOR) &&  \
4475         *    defined(UV_VERSION_PATCH)     \
4476         */
4477 #ifdef DECNUMBERLOC
4478 # ifdef DECVERSION
4479 #  ifdef DECVERSEXT
4480     fprintf (st, "\r\n          Math library: %s-%s", DECVERSION, DECVERSEXT);
4481 #  else
4482 #   ifdef DECNLAUTHOR
4483     fprintf (st, "\r\n          Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4484 #   else
4485     fprintf (st, "\r\n          Math library: %s", DECVERSION);
4486 #   endif /* ifdef DECNLAUTHOR */
4487 #  endif /* ifdef DECVERSEXT */
4488 # else
4489     fprintf (st, "\r\n          Math library: decNumber, unknown version");
4490 # endif /* ifdef DECVERSION */
4491 #endif /* ifdef DECNUMBERLOC */
4492 #ifdef LOCKLESS
4493     fprintf (st, "\r\n     Atomic operations: ");
4494 # if defined(AIX_ATOMICS)
4495     fprintf (st, "IBM AIX-style");
4496 # elif defined(BSD_ATOMICS)
4497     fprintf (st, "FreeBSD-style");
4498 # elif defined(GNU_ATOMICS)
4499     fprintf (st, "GNU-style");
4500 # elif defined(SYNC_ATOMICS)
4501     fprintf (st, "GNU sync-style");
4502 # elif defined(ISO_ATOMICS)
4503     fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4504 # elif defined(NT_ATOMICS)
4505     fprintf (st, "Windows NT interlocked operations");
4506 # endif
4507 #endif /* ifdef LOCKLESS */
4508     fprintf (st, "\r\n          File locking: ");
4509 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4510     fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4511 #endif
4512 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4513     fprintf (st, "POSIX-style fcntl() locking");
4514 #endif
4515 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4516     fprintf (st, "BSD-style flock() locking");
4517 #endif
4518 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4519     fprintf (st, "No file locking available");
4520 #endif
4521 #if defined(USE_BACKTRACE)
4522     fprintf (st, "\r\n     Backtrace support: ");
4523     fprintf (st, "libbacktrace");
4524 #endif /* if defined(USE_BACKTRACE) */
4525     fprintf (st, "\r\n");
4526     return 0;
4527 }
4528 
4529 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
4530 {
4531 const char *arch = "";
4532 char *whydirty = " ";
4533 int dirty = 0;
4534 
4535 if (cptr && (*cptr != 0))
4536     return SCPE_2MARG;
4537 if (flag) {
4538         fprintf (st, " %s Simulator:", sim_name);
4539 #if defined(USE_DUMA)
4540 # undef NO_SUPPORT_VERSION
4541 # define NO_SUPPORT_VERSION 1
4542         nodist++;
4543 #endif /* if defined(USE_DUMA) */
4544 #if defined(NO_SUPPORT_VERSION) ||  \
4545     defined(WITH_SOCKET_DEV)    ||  \
4546     defined(WITH_ABSI_DEV)      ||  \
4547     defined(WITH_MGP_DEV)       ||  \
4548     defined(MACOSXPPC)          ||  \
4549     defined(TESTING)            ||  \
4550     defined(ISOLTS)             ||  \
4551     defined(USE_DUMA)
4552 # ifndef NO_SUPPORT_VERSION
4553 #  define NO_SUPPORT_VERSION 1
4554 # endif
4555 #endif
4556 #if defined(NO_SUPPORT_VERSION)
4557         dirty++;
4558 #endif
4559 #if defined(GENERATED_MAKE_VER_H)
4560 # if defined(VER_H_GIT_VERSION)
4561 
4562         /* Dirty if git source is dirty */
4563         if (strstr(VER_H_GIT_VERSION, "*"))
4564           {
4565                 dirty++;
4566           }
4567 
4568         /* Dirty if version contains "X", "D", "A", or "B" */
4569         if ((strstr(VER_H_GIT_VERSION, "X")) ||  \
4570             (strstr(VER_H_GIT_VERSION, "D")) ||  \
4571             (strstr(VER_H_GIT_VERSION, "A")) ||  \
4572             (strstr(VER_H_GIT_VERSION, "B")))
4573           {
4574                 dirty++;
4575           }
4576 
4577         /* Why? */
4578         if (dirty) //-V547
4579           {
4580             if ((strstr(VER_H_GIT_VERSION, "X")))
4581               {
4582                     whydirty = " ";
4583               }
4584             else if ((strstr(VER_H_GIT_VERSION, "D")))
4585               {
4586                     whydirty = " DEV ";
4587               }
4588             else if ((strstr(VER_H_GIT_VERSION, "A")))
4589               {
4590                     whydirty = " ALPHA ";
4591               }
4592             else if ((strstr(VER_H_GIT_VERSION, "B")))
4593               {
4594                     whydirty = " BETA ";
4595               }
4596           }
4597 
4598 #  if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4599 #   if defined(VER_H_GIT_HASH)
4600 #    if VER_H_GIT_PATCH_INT < 1
4601     fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4602              VER_H_GIT_VERSION,
4603              (long)(CHAR_BIT*sizeof(void *)),
4604              VER_H_GIT_HASH);
4605 #    else
4606 #     define NO_SUPPORT_VERSION 1
4607     fprintf (st, "\n   Version: %s+%s (%ld-bit)\n    Commit: %s",
4608              VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4609              (long)(CHAR_BIT*sizeof(void *)),
4610              VER_H_GIT_HASH);
4611 #    endif
4612 #   else
4613 #    if VER_H_GIT_PATCH_INT < 1
4614         fprintf (st, "\n   Version: %s (%ld-bit)",
4615                  VER_H_GIT_VERSION,
4616                  (long)(CHAR_BIT*sizeof(void *)));
4617 #    else
4618 #     define NO_SUPPORT_VERSION 1
4619         fprintf (st, "\n   Version: %s+%s (%ld-bit)",
4620                  VER_H_GIT_VERSION, VER_H_GIT_PATCH,
4621                  (long)(CHAR_BIT*sizeof(void *)));
4622 #    endif
4623 #   endif
4624 #  else
4625 #   if defined(VER_H_GIT_HASH)
4626         fprintf (st, "\n   Version: %s (%ld-bit)\n    Commit: %s",
4627                  VER_H_GIT_VERSION,
4628                  (long)(CHAR_BIT*sizeof(void *)),
4629                  VER_H_GIT_HASH);
4630 #   else
4631         fprintf (st, "\n   Version: %s (%ld-bit)",
4632                  VER_H_GIT_VERSION,
4633                  (long)(CHAR_BIT*sizeof(void *)));
4634 #   endif
4635 #  endif
4636 # endif
4637 #endif
4638 
4639 /* TESTING */
4640 #ifdef TESTING
4641     fprintf (st, "\n   Options: ");
4642 # ifndef HAVE_DPSOPT
4643 #  define HAVE_DPSOPT 1
4644 # endif
4645     fprintf (st, "TESTING");
4646 #endif /* ifdef TESTING */
4647 
4648 /* ISOLTS */
4649 #ifdef ISOLTS
4650 # ifdef HAVE_DPSOPT
4651     fprintf (st, ", ");
4652 # else
4653     fprintf (st, "\n   Options: ");
4654 # endif
4655 # ifndef HAVE_DPSOPT
4656 #  define HAVE_DPSOPT 1
4657 # endif
4658     fprintf (st, "ISOLTS");
4659 #endif /* ifdef ISOLTS */
4660 
4661 /* NO_UCACHE */
4662 #ifdef NO_UCACHE
4663 # ifdef HAVE_DPSOPT
4664     fprintf (st, ", ");
4665 # else
4666     fprintf (st, "\n   Options: ");
4667 # endif
4668 # ifndef HAVE_DPSOPT
4669 #  define HAVE_DPSOPT 1
4670 # endif
4671     fprintf (st, "NO_UCACHE");
4672 #endif /* ifdef NO_UCACHE */
4673 
4674 /* NEED_128 */
4675 #ifdef NEED_128
4676 # ifdef HAVE_DPSOPT
4677     fprintf (st, ", ");
4678 # else
4679     fprintf (st, "\n   Options: ");
4680 # endif
4681 # ifndef HAVE_DPSOPT
4682 #  define HAVE_DPSOPT 1
4683 # endif
4684     fprintf (st, "NEED_128");
4685 #endif /* ifdef NEED_128 */
4686 
4687 /* WAM */
4688 #ifdef WAM
4689 # ifdef HAVE_DPSOPT
4690     fprintf (st, ", ");
4691 # else
4692     fprintf (st, "\n   Options: ");
4693 # endif
4694 # ifndef HAVE_DPSOPT
4695 #  define HAVE_DPSOPT 1
4696 # endif
4697     fprintf (st, "WAM");
4698 #endif /* ifdef WAM */
4699 
4700 /* ROUND_ROBIN */
4701 #ifdef ROUND_ROBIN
4702 # ifdef HAVE_DPSOPT
4703     fprintf (st, ", ");
4704 # else
4705     fprintf (st, "\n   Options: ");
4706 # endif
4707 # ifndef HAVE_DPSOPT
4708 #  define HAVE_DPSOPT 1
4709 # endif
4710     fprintf (st, "ROUND_ROBIN");
4711 #endif /* ifdef ROUND_ROBIN */
4712 
4713 /* NO_LOCKLESS */
4714 #ifndef LOCKLESS
4715 # ifdef HAVE_DPSOPT
4716     fprintf (st, ", ");
4717 # else
4718     fprintf (st, "\n   Options: ");
4719 # endif
4720 # ifndef HAVE_DPSOPT
4721 #  define HAVE_DPSOPT 1
4722 # endif
4723     fprintf (st, "NO_LOCKLESS");
4724 #endif /* ifndef LOCKLESS */
4725 
4726 /* ABSI */  /* XXX: Change to NO_ABSI once code is non-experimental */
4727 #ifdef WITH_ABSI_DEV
4728 # ifdef HAVE_DPSOPT
4729     fprintf (st, ", ");
4730 # else
4731     fprintf (st, "\n   Options: ");
4732 # endif
4733 # ifndef HAVE_DPSOPT
4734 #  define HAVE_DPSOPT 1
4735 # endif
4736     fprintf (st, "ABSI");
4737 #endif /* ifdef WITH_ABSI_DEV */
4738 
4739 /* SOCKET */  /* XXX: Change to NO_SOCKET once code is non-experimental */
4740 #ifdef WITH_SOCKET_DEV
4741 # ifdef HAVE_DPSOPT
4742     fprintf (st, ", ");
4743 # else
4744     fprintf (st, "\n   Options: ");
4745 # endif
4746 # ifndef HAVE_DPSOPT
4747 #  define HAVE_DPSOPT 1
4748 # endif
4749     fprintf (st, "SOCKET");
4750 #endif /* ifdef WITH_SOCKET_DEV */
4751 
4752 /* CHAOSNET */  /* XXX: Change to NO_CHAOSNET once code is non-experimental */
4753 #ifdef WITH_MGP_DEV
4754 # ifdef HAVE_DPSOPT
4755     fprintf (st, ", ");
4756 # else
4757     fprintf (st, "\n   Options: ");
4758 # endif
4759 # ifndef HAVE_DPSOPT
4760 #  define HAVE_DPSOPT 1
4761 # endif
4762     fprintf (st, "CHAOSNET");
4763 # if USE_SOCKET_DEV_APPROACH
4764     fprintf (st, "*");
4765 # endif /* if USE_SOCKET_DEV_APPROACH */
4766 #endif /* ifdef WITH_MGP_DEV */
4767 
4768 /* DUMA */
4769 #ifdef USE_DUMA
4770 # ifdef HAVE_DPSOPT
4771     fprintf (st, ", ");
4772 # else
4773     fprintf (st, "\n   Options: ");
4774 # endif
4775 # ifndef HAVE_DPSOPT
4776 #  define HAVE_DPSOPT 1
4777 # endif
4778     fprintf (st, "DUMA");
4779 #endif /* ifdef USE_DUMA */
4780 /* DUMA */
4781 
4782 /* BACKTRACE */
4783 #ifdef USE_BACKTRACE
4784 # ifdef HAVE_DPSOPT
4785     fprintf (st, ", ");
4786 # else
4787     fprintf (st, "\n   Options: ");
4788 # endif
4789 # ifndef HAVE_DPSOPT
4790 #  define HAVE_DPSOPT 1
4791 # endif
4792     fprintf (st, "BACKTRACE");
4793 #endif /* ifdef USE_BACKTRACE */
4794 
4795 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
4796 # if defined(NO_SUPPORT_VERSION)
4797     fprintf (st, "\n  Modified: %s", VER_H_GIT_DATE);
4798 # else
4799     fprintf (st, "\n  Released: %s", VER_H_GIT_DATE);
4800 # endif
4801 #endif
4802 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
4803     fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
4804 #endif
4805 #ifdef VER_CURRENT_TIME
4806     fprintf (st, "\n  Compiled: %s", VER_CURRENT_TIME);
4807 #endif
4808     if (dirty) //-V547
4809       {
4810         fprintf (st, "\r\n\r\n ****** THIS%sBUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******", whydirty);
4811       }
4812     fprintf (st, "\r\n\r\n Build Information:");
4813 #if defined (BUILD_PROM_OSV_TEXT) && defined (BUILD_PROM_OSA_TEXT)
4814     char build_os_version_raw[255];
4815     char build_os_arch_raw[255];
4816     sprintf(build_os_version_raw, "%.254s", BUILD_PROM_OSV_TEXT);
4817     sprintf(build_os_arch_raw, "%.254s", BUILD_PROM_OSA_TEXT);
4818     char *build_os_version = strdup(build_os_version_raw);
4819     if (!build_os_version)
4820       {
4821         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
4822                  __func__, __FILE__, __LINE__);
4823 # if defined(USE_BACKTRACE)
4824 #  ifdef SIGUSR2
4825         (void)raise(SIGUSR2);
4826         /*NOTREACHED*/ /* unreachable */
4827 #  endif /* ifdef SIGUSR2 */
4828 # endif /* if defined(USE_BACKTRACE) */
4829         abort();
4830       }
4831     char *build_os_arch = strdup(build_os_arch_raw);
4832     if (!build_os_arch)
4833       {
4834         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
4835                  __func__, __FILE__, __LINE__);
4836 # if defined(USE_BACKTRACE)
4837 #  ifdef SIGUSR2
4838         (void)raise(SIGUSR2);
4839         /*NOTREACHED*/ /* unreachable */
4840 #  endif /* ifdef SIGUSR2 */
4841 # endif /* if defined(USE_BACKTRACE) */
4842         abort();
4843       }
4844     unsigned char SPROM[1024];
4845     setupPROM (0, SPROM);
4846     fprintf (st, "\n    Target: ");
4847     printpq (SPROM, st, 190, 20);
4848     if (SPROM[170] != 20)
4849       {
4850         if (SPROM[170] != 255)
4851           {
4852             fprintf (st, " on ");
4853             printpq (SPROM, st, 170, 20);
4854           }
4855       }
4856     strtrimspace(build_os_version, build_os_version_raw);
4857     strtrimspace(build_os_arch, build_os_arch_raw);
4858     fprintf (st, "\n  Build OS: %s %s", build_os_version, build_os_arch);
4859     FREE(build_os_version);
4860     FREE(build_os_arch);
4861 #endif
4862 #if defined (__VERSION__)
4863     char gnumver[2];
4864     char postver[1024];
4865     sprintf(gnumver, "%.1s", __VERSION__);
4866     sprintf(postver, "%.1023s", __VERSION__);
4867     strremove(postver, "(TM)");
4868     strremove(postver, "(R)");
4869     strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
4870     strremove(postver, "https://github.com/OpenIndiana/oi-userland.git ");
4871     strremove(postver, " gcc 4.9 mode");
4872     strremove(postver, "4.2.1 Compatible ");
4873     strremove(postver, "git@github.com:llvm/llvm-project.git ");
4874     strremove(postver, "https://github.com/llvm/llvm-project.git ");
4875     strremove(postver, " (https://github.com/yrnkrn/zapcc)");
4876     strremove(postver, "https://github.com/yrnkrn/zapcc ");
4877     strremove(postver, "(experimental) ");
4878     strremove(postver, ".module+el8.7.0+20823+214a699d");
4879     strremove(postver, "17.1.1 (5725-C72, 5765-J20), version ");
4880     strremove(postver, "17.1.1 (5725-C72, 5765-J18), version ");
4881     strremove(postver, " Clang 15.0.0 (build 760095e)");
4882     strremove(postver, " Clang 15.0.0 (build 6af5742)");
4883     strremove(postver, " Clang 15.0.0 (build ca7115e)");
4884 #endif
4885 #if ( defined (__GNUC__) && defined (__VERSION__) ) && !defined (__EDG__)
4886 # ifndef __clang_version__
4887     if (isdigit((unsigned char)gnumver[0])) {
4888         fprintf (st, "\n  Compiler: GCC %s", postver);
4889     } else {
4890         fprintf (st, "\n  Compiler: %s", postver);
4891     }
4892 # endif
4893 # if defined (__clang_analyzer__ )
4894     fprintf (st, "\n  Compiler: Clang C/C++ Static Analyzer");
4895 # elif defined (__clang_version__) && defined (__VERSION__)
4896     char clangllvmver[1024];
4897     sprintf(clangllvmver, "%.1023s", __clang_version__);
4898     strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
4899     strremove(clangllvmver, "https://github.com/OpenIndiana/oi-userland.git ");
4900     if (gnumver[0] == 'c' || gnumver[0] == 'C') {
4901         fprintf (st, "\n  Compiler: Clang %s", clangllvmver);
4902     } else {
4903         fprintf (st, "\n  Compiler: %s", postver);
4904     }
4905 # elif defined (__clang_version__)
4906     fprintf (st, "\n  Compiler: %s", postver);
4907 # endif
4908 #elif defined (__PGI) && !defined(__NVCOMPILER)
4909     fprintf (st, "\n  Compiler: Portland Group, Inc. (PGI) C Compiler ");
4910 # ifdef __PGIC__
4911     fprintf (st, "%d", __PGIC__);
4912 #  ifdef __PGIC_MINOR__
4913     fprintf (st, ".%d", __PGIC_MINOR__);
4914 #   ifdef __PGIC_PATCHLEVEL__
4915     fprintf (st, ".%d", __PGIC_PATCHLEVEL__);
4916 #   endif
4917 #  endif
4918 # endif
4919 #elif defined(__NVCOMPILER)
4920     fprintf (st, "\n  Compiler: NVIDIA HPC SDK C Compiler ");
4921 # ifdef __NVCOMPILER_MAJOR__
4922     fprintf (st, "%d", __NVCOMPILER_MAJOR__);
4923 #  ifdef __NVCOMPILER_MINOR__
4924     fprintf (st, ".%d", __NVCOMPILER_MINOR__);
4925 #   ifdef __NVCOMPILER_PATCHLEVEL__
4926     fprintf (st, ".%d", __NVCOMPILER_PATCHLEVEL__);
4927 #   endif
4928 #  endif
4929 # endif
4930 #elif defined (_MSC_FULL_VER) && defined (_MSC_BUILD)
4931     fprintf (st, "\n  Compiler: Microsoft C %d.%02d.%05d.%02d",
4932              _MSC_FULL_VER/10000000,
4933             (_MSC_FULL_VER/100000)%100,
4934              _MSC_FULL_VER%100000,
4935              _MSC_BUILD);
4936 #elif ( defined (__xlc__) && !defined(__clang_version__) )
4937 # if defined (_AIX) && defined (PASE)
4938     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
4939 # endif
4940 # if defined (_AIX) && !defined (PASE)
4941     fprintf (st, "\n  Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
4942 # endif
4943 # if defined (__linux__) && ( !defined(_AIX) || !defined(PASE) )
4944     fprintf (st, "\n  Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
4945 # endif
4946 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(PASE) && !defined(__linux__) && defined(__xlc__) )
4947 #  if defined(__PPC__) && defined(__APPLE__)
4948     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s for Mac OS X", __xlc__);
4949 #  else
4950     fprintf (st, "\n  Compiler: IBM XL C/C++ V%s", __xlc__);
4951 #  endif
4952 # endif
4953 #elif defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__SUNPRO_CC_COMPAT)
4954     fprintf (st, "\n  Compiler: Oracle Developer Studio C/C++");
4955 #elif defined (__DMC__)
4956     fprintf (st, "\n  Compiler: Digital Mars C/C++");
4957 #elif defined (__PCC__)
4958     fprintf (st, "\n  Compiler: Portable C Compiler");
4959 #elif defined (KENC) || defined (KENCC) || defined (__KENC__) || defined (__KENCC__)
4960     fprintf (st, "\n  Compiler: Plan 9 Compiler Suite");
4961 #elif defined (__ACK__)
4962     fprintf (st, "\n  Compiler: Amsterdam Compiler Kit");
4963 #elif defined (__COMO__)
4964     fprintf (st, "\n  Compiler: Comeau C++");
4965 #elif defined (__COMPCERT__)
4966     fprintf (st, "\n  Compiler: CompCert C");
4967 #elif defined (__COVERITY__)
4968     fprintf (st, "\n  Compiler: Coverity C/C++ Static Analyzer");
4969 #elif defined (__LCC__)
4970     fprintf (st, "\n  Compiler: Local C Compiler (lcc)");
4971 #elif defined (sgi) || defined (__sgi) || defined (_sgi) || defined (_SGI_COMPILER_VERSION)
4972     fprintf (st, "\n  Compiler: SGI MIPSpro");
4973 #elif defined (__OPEN64__)
4974     fprintf (st, "\n  Compiler: Open64 %s", __OPEN64__);
4975 #elif defined (__PGI) || defined (__PGIC__)
4976     fprintf (st, "\n  Compiler: Portland Group/PGI C/C++");
4977 #elif defined (__VBCC__)
4978     fprintf (st, "\n  Compiler: Volker Barthelmann C Compiler (vbcc)");
4979 #elif defined (__WATCOMC__)
4980     fprintf (st, "\n  Compiler: Watcom C/C++ %d.%d",
4981             __WATCOMC__ / 100,
4982             __WATCOMC__ % 100);
4983 #elif defined (__xlC__)
4984     fprintf (st, "\n  Compiler: IBM XL C/C++");
4985 #elif defined (__INTEL_COMPILER) || defined (__ICC)
4986 # if defined (__INTEL_COMPILER_UPDATE)
4987 #  if defined (__INTEL_COMPILER_BUILD_DATE)
4988     fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d (%d)",
4989             __INTEL_COMPILER, __INTEL_COMPILER_UPDATE,
4990             __INTEL_COMPILER_BUILD_DATE);
4991 #  else
4992     fprintf (st, "\n  Compiler: Intel C++ Compiler %d.%d",
4993             __INTEL_COMPILER, __INTEL_COMPILER_UPDATE);
4994 #  endif
4995 # else
4996     fprintf (st, "\n  Compiler: Intel C++ Compiler %d",
4997             __INTEL_COMPILER);
4998 # endif
4999 #elif defined (SIM_COMPILER)
5000 # define S_xstr(a) S_str(a)
5001 # define S_str(a) #a
5002     fprintf (st, "\n  Compiler: %s", S_xstr(SIM_COMPILER));
5003 # undef S_str
5004 # undef S_xstr
5005 #else
5006     fprintf (st, "\n  Compiler: Unknown");
5007 #endif
5008 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
5009     arch = " x86_64";
5010 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
5011     arch = " x86";
5012 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
5013     arch = " arm64";
5014 #elif defined(_M_ARM) || defined(__arm__)
5015     arch = " arm";
5016 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
5017     arch = " ia64";
5018 #elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
5019     arch = " powerpc64";
5020 #elif defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_M_PPC) || defined(__PPC) || defined(__ppc32__) || defined(__PPC32__) || defined(__powerpc32__) || defined(__POWERPC32__) || defined(_M_PPC32) || defined(__PPC32)
5021     arch = " powerpc";
5022 #elif defined(__s390x__)
5023     arch = " s390x";
5024 #elif defined(__s390__)
5025     arch = " s390";
5026 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
5027     arch = " j2";
5028 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
5029     arch = " sh4";
5030 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
5031     arch = " sh2";
5032 #elif defined(__alpha__)
5033     arch = " alpha";
5034 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
5035     arch = " hppa";
5036 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
5037     arch = " ice9";
5038 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
5039     arch = " mips64";
5040 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
5041     arch = " mips";
5042 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
5043     arch = " openrisc";
5044 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
5045     arch = " sparc64";
5046 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
5047     arch = " sparc";
5048 #elif defined(__riscv) || defined(__riscv__)
5049     arch = " riscv";
5050 #elif defined(__myriad2__)
5051     arch = " myriad2";
5052 #elif defined(__loongarch64) || defined(__loongarch__)
5053     arch = " loongarch";
5054 #elif defined(_m68851) || defined(__m68k__) || defined(__m68000__) || defined(__M68K)
5055     arch = " m68k";
5056 #elif defined(__m88k__) || defined(__m88000__) || defined(__M88K)
5057     arch = " m88k";
5058 #elif defined(__VAX__) || defined(__vax__)
5059     arch = " vax";
5060 #elif defined(__NIOS2__) || defined(__nios2__)
5061     arch = " nios2";
5062 #elif defined(__MICROBLAZE__) || defined(__microblaze__)
5063     arch = " microblaze";
5064 #else
5065     arch = " ";
5066 #endif
5067     fprintf (st, "%s", arch);
5068 #if defined(BUILD_BY_USER)
5069         fprintf (st, "\n  Built by: %s", BUILD_BY_USER);
5070 #else
5071 # if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
5072         fprintf (st, "\n  Built by: %s", VER_H_PREP_USER);
5073 # endif
5074 #endif
5075                 fprintf (st, "\n\n Host System Information:");
5076 #if defined(_WIN32)
5077     if (1) {
5078         char *arch = getenv ("PROCESSOR_ARCHITECTURE");
5079         char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
5080         char osversion[PATH_MAX+1] = "";
5081         FILE *f;
5082 
5083         if ((f = _popen ("ver", "r"))) {
5084             memset (osversion, 0, sizeof(osversion));
5085             do {
5086                 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
5087                     break;
5088                 sim_trim_endspc (osversion);
5089                 } while (osversion[0] == '\0');
5090             _pclose (f);
5091             }
5092         fprintf (st, "\n   Host OS: %s", osversion);
5093         fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264  : "");
5094         }
5095 #else
5096     if (1) {
5097         char osversion[2*PATH_MAX+1] = "";
5098         FILE *f;
5099 # ifndef _AIX
5100         if ((f = popen ("uname -mrs 2> /dev/null", "r"))) {
5101 # else
5102         if ((f = popen                                              \
5103           ("sh -c 'command -p env uname -svM   2> /dev/null'        \
5104                                                2> /dev/null    ||   \
5105               /bin/sh -c 'command -p env uname -svM                 \
5106                                                2> /dev/null'        \
5107                                                2> /dev/null    ||   \
5108                   uname -svM                   2> /dev/null    ||   \
5109                     sh -c 'command -p env uname -svp                \
5110                                                2> /dev/null'        \
5111                                                2> /dev/null    ||   \
5112                         /bin/sh -c 'command -p env uname -svp       \
5113                                                2> /dev/null'        \
5114                                                2> /dev/null    ||   \
5115                             uname -svp         2> /dev/null         \
5116                               ", "r")))                             \
5117          {
5118 # endif /* ifndef _AIX */
5119             memset (osversion, 0, sizeof(osversion));
5120             do {
5121               if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
5122                     break;
5123               }
5124             sim_trim_endspc (osversion);
5125             } while (osversion[0] == '\0');
5126             pclose (f);
5127             strremove(osversion, "0000000000000000 ");
5128             strremove(osversion, " 0000000000000000");
5129             strremove(osversion, "000000000000 ");
5130             strremove(osversion, " 000000000000");
5131             strremove(osversion, "IBM ");
5132             strremove(osversion, " (emulated by qemu)");
5133             strremove(osversion, " (emulated by QEMU)");
5134         }
5135 # ifndef _AIX
5136             fprintf (st, "\n   Host OS: %s", osversion);
5137 # else
5138             strremove(osversion, "AIX ");
5139             fprintf (st, "\n   Host OS: IBM AIX %s", osversion);
5140 # endif /* ifndef _AIX */
5141     } else {
5142 # ifndef _AIX
5143         fprintf (st, "\n   Host OS: Unknown");
5144 # else
5145         fprintf (st, "\n   Host OS: IBM AIX");
5146 # endif /* ifndef _AIX */
5147     }
5148 #endif
5149 #if defined(__APPLE__)
5150     int isRosetta = processIsTranslated();
5151     if (isRosetta == 1) {
5152         sim_printf ("\n\n  ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
5153     }
5154 #endif
5155     if (nodist)
5156       {
5157         sim_printf ("\n\n ********* LICENSE RESTRICTED BUILD *** NOT FOR REDISTRIBUTION *********\n");
5158       }
5159     else
5160       {
5161         fprintf (st, "\n");
5162         fprintf (st, "\n This software is made available under the terms of the ICU License,");
5163         fprintf (st, "\n version 1.8.1 or later.  For complete details, see the \"LICENSE.md\"");
5164         fprintf (st, "\n included or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
5165       }
5166         fprintf (st, "\n");
5167     }
5168 return SCPE_OK;
5169 }
5170 
5171 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5172 {
5173 int32 i;
5174 DEVICE *dptr;
5175 t_bool only_enabled = (sim_switches & SWMASK ('E'));
5176 
5177 if (cptr && (*cptr != 0))
5178     return SCPE_2MARG;
5179 fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
5180 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5181     if (!only_enabled || !qdisable (dptr))
5182         show_device (st, dptr, flag);
5183 return SCPE_OK;
5184 }
5185 
5186 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5187 {
5188 int32 i;
5189 DEVICE *dptr;
5190 
5191 if (cptr && (*cptr != 0))
5192     return SCPE_2MARG;
5193 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5194     show_dev_logicals (st, dptr, NULL, 1, cptr);
5195 return SCPE_OK;
5196 }
5197 
5198 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5199 {
5200 if (dptr->lname)
5201     fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
5202 else if (!flag)
5203     fputs ("no logical name assigned\n", st);
5204 return SCPE_OK;
5205 }
5206 
5207 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5208 {
5209 DEVICE *dptr;
5210 UNIT *uptr;
5211 int32 accum;
5212 
5213 if (cptr && (*cptr != 0))
5214     return SCPE_2MARG;
5215 if (sim_clock_queue == QUEUE_LIST_END)
5216     fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructions/sec\n",
5217              sim_name, sim_time, sim_timer_inst_per_sec ());
5218 else {
5219     const char *tim;
5220 
5221     fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
5222              sim_name, sim_time, sim_timer_inst_per_sec ());
5223     accum = 0;
5224     for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
5225         if (uptr == &sim_step_unit)
5226             fprintf (st, "  Step timer");
5227         else
5228             if (uptr == &sim_expect_unit)
5229                 fprintf (st, "  Expect fired");
5230             else
5231                 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
5232                     fprintf (st, "  %s", sim_dname (dptr));
5233                     if (dptr->numunits > 1)
5234                         fprintf (st, " unit %d", (int32) (uptr - dptr->units));
5235                     }
5236                 else
5237                     fprintf (st, "  Unknown");
5238         tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
5239         fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
5240                                         (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
5241                                         (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
5242         accum = accum + uptr->time;
5243         }
5244     }
5245 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
5246 return SCPE_OK;
5247 }
5248 
5249 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5250 {
5251 if (cptr && (*cptr != 0))
5252     return SCPE_2MARG;
5253 fprintf (st, "Time:\t%.0f\n", sim_gtime());
5254 return SCPE_OK;
5255 }
5256 
5257 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5258 {
5259 t_stat r;
5260 
5261 if (cptr && (*cptr != 0))
5262     r = ssh_break (st, cptr, 1);  /* more? */
5263 else
5264     r = sim_brk_showall (st, sim_switches);
5265 return r;
5266 }
5267 
5268 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5269 {
5270 fprintf (st, "Radix=%d\n", dptr->dradix);
5271 return SCPE_OK;
5272 }
5273 
5274 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5275 {
5276 int32 any = 0;
5277 DEBTAB *dep;
5278 
5279 if (dptr->flags & DEV_DEBUG) {
5280     if (dptr->dctrl == 0)
5281         fputs ("Debugging disabled", st);
5282     else if (dptr->debflags == NULL)
5283         fputs ("Debugging enabled", st);
5284     else {
5285         uint32 dctrl = dptr->dctrl;
5286 
5287         fputs ("Debug=", st);
5288         for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
5289             if ((dctrl & dep->mask) == dep->mask) {
5290                 dctrl &= ~dep->mask;
5291                 if (any)
5292                     fputc (';', st);
5293                 fputs (dep->name, st);
5294                 any = 1;
5295                 }
5296             }
5297         }
5298     fputc ('\n', st);
5299     return SCPE_OK;
5300     }
5301 else return SCPE_NOFNC;
5302 }
5303 
5304 /* Show On actions */
5305 
5306 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5307 {
5308 int32 lvl, i;
5309 
5310 if (cptr && (*cptr != 0)) return SCPE_2MARG;            /* now eol? */
5311 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
5312     if (lvl > 0)
5313         fprintf(st, "On Processing at Do Nest Level: %d", lvl);
5314     else
5315         fprintf(st, "On Processing for input commands");
5316     fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
5317     for (i=1; i<SCPE_BASE; ++i) {
5318         if (sim_on_actions[lvl][i])
5319             fprintf(st, "    on %5d    %s\n", i, sim_on_actions[lvl][i]); }
5320     for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
5321         if (sim_on_actions[lvl][i])
5322             fprintf(st, "    on %-5s    %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
5323     if (sim_on_actions[lvl][0])
5324         fprintf(st, "    on ERROR    %s\n", sim_on_actions[lvl][0]);
5325     fprintf(st, "\n");
5326     }
5327 if (sim_on_inherit)
5328     fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
5329 return SCPE_OK;
5330 }
5331 
5332 /* Show modifiers */
5333 
5334 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5335 {
5336 int32 i;
5337 DEVICE *dptr;
5338 
5339 if (cptr && (*cptr != 0))                               /* now eol? */
5340     return SCPE_2MARG;
5341 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5342     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5343 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5344     show_dev_modifiers (st, dptr, NULL, flag, cptr);
5345 return SCPE_OK;
5346 }
5347 
5348 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5349 {
5350 fprint_set_help (st, dptr);
5351 return SCPE_OK;
5352 }
5353 
5354 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
     /* [previous][next][first][last][top][bottom][index][help] */
5355 {
5356 MTAB *mptr;
5357 t_stat r = SCPE_OK;
5358 
5359 if (dptr->modifiers == NULL)
5360     return SCPE_OK;
5361 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
5362     if (mptr->pstring &&
5363         ((mptr->mask & MTAB_XTD)?
5364             (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
5365             ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
5366         if (*toks > 0) {
5367             fprintf (st, "\n");
5368             *toks = 0;
5369             }
5370         if (r == SCPE_OK)
5371             fprint_sep (st, toks);
5372         r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
5373         }
5374     }
5375 return SCPE_OK;
5376 }
5377 
5378 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
     /* [previous][next][first][last][top][bottom][index][help] */
5379     CONST char *cptr, int32 flag)
5380 {
5381 t_stat r = SCPE_OK;
5382 
5383 if (mptr->disp)
5384     r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
5385 else
5386     fputs (mptr->pstring, st);
5387 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
5388     fputc ('\n', st);
5389 return r;
5390 }
5391 
5392 /* Show show commands */
5393 
5394 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5395 {
5396 int32 i;
5397 DEVICE *dptr;
5398 
5399 if (cptr && (*cptr != 0))                               /* now eol? */
5400     return SCPE_2MARG;
5401 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
5402     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5403 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
5404     show_dev_show_commands (st, dptr, NULL, flag, cptr);
5405 return SCPE_OK;
5406 }
5407 
5408 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] */
5409 {
5410 fprint_show_help (st, dptr);
5411 return SCPE_OK;
5412 }
5413 
5414 /* Breakpoint commands */
5415 
5416 t_stat brk_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5417 {
5418 GET_SWITCHES (cptr);                                    /* get switches */
5419 return ssh_break (NULL, cptr, flg);                     /* call common code */
5420 }
5421 
5422 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
     /* [previous][next][first][last][top][bottom][index][help] */
5423 {
5424 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
5425 CONST char *tptr, *t1ptr;
5426 DEVICE *dptr = sim_dflt_dev;
5427 UNIT *uptr;
5428 t_stat r;
5429 t_addr lo, hi, max;
5430 int32 cnt;
5431 
5432 if (sim_brk_types == 0)
5433     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5434 if (dptr == NULL)
5435     return SCPE_IERR;
5436 uptr = dptr->units;
5437 if (uptr == NULL)
5438     return SCPE_IERR;
5439 max = uptr->capac - 1;
5440 abuf[sizeof(abuf)-1] = '\0';
5441 strncpy (abuf, cptr, sizeof(abuf)-1);
5442 cptr = abuf;
5443 if ((aptr = strchr (abuf, ';'))) {                      /* ;action? */
5444     if (flg != SSH_ST)                                  /* only on SET */
5445         return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
5446     *aptr++ = 0;                                        /* separate strings */
5447     }
5448 if (*cptr == 0) {                                       /* no argument? */
5449     lo = (t_addr) get_rval (sim_PC, 0);                 /* use PC */
5450     return ssh_break_one (st, flg, lo, 0, aptr);
5451     }
5452 while (*cptr) {
5453     cptr = get_glyph (cptr, gbuf, ',');
5454     tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
5455     if (tptr == NULL)
5456         return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
5457     if (*tptr == '[') {
5458         cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
5459         if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
5460             return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
5461         tptr = t1ptr + 1;
5462         }
5463     else cnt = 0;
5464     if (*tptr != 0)
5465         return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
5466     if ((lo == 0) && (hi == max)) {
5467         if (flg == SSH_CL)
5468             sim_brk_clrall (sim_switches);
5469         else
5470             if (flg == SSH_SH)
5471                 sim_brk_showall (st, sim_switches);
5472             else
5473                 return SCPE_ARG;
5474         }
5475     else {
5476         for ( ; lo <= hi; lo = lo + 1) {
5477             r = ssh_break_one (st, flg, lo, cnt, aptr);
5478             if (r != SCPE_OK)
5479                 return r;
5480             }
5481         }
5482     }
5483 return SCPE_OK;
5484 }
5485 
5486 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] */
5487 {
5488 if (!sim_brk_types)
5489     return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
5490 switch (flg) {
5491 
5492     case SSH_ST:
5493         return sim_brk_set (lo, sim_switches, cnt, aptr);
5494         /*NOTREACHED*/ /* unreachable */
5495         break;
5496 
5497     case SSH_CL:
5498         return sim_brk_clr (lo, sim_switches);
5499         /*NOTREACHED*/ /* unreachable */
5500         break;
5501 
5502     case SSH_SH:
5503         return sim_brk_show (st, lo, sim_switches);
5504         /*NOTREACHED*/ /* unreachable */
5505         break;
5506 
5507     default:
5508         return SCPE_ARG;
5509     }
5510 }
5511 
5512 /* Reset command and routines */
5513 
5514 static t_bool run_cmd_did_reset = FALSE;
5515 
5516 t_stat reset_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5517 {
5518 char gbuf[CBUFSIZE];
5519 DEVICE *dptr;
5520 
5521 GET_SWITCHES (cptr);                                    /* get switches */
5522 run_cmd_did_reset = FALSE;
5523 if (*cptr == 0)                                         /* reset(cr) */
5524     return (reset_all (0));
5525 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5526 if (*cptr != 0)                                         /* now eol? */
5527     return SCPE_2MARG;
5528 if (strcmp (gbuf, "ALL") == 0)
5529     return (reset_all (0));
5530 dptr = find_dev (gbuf);                                 /* locate device */
5531 if (dptr == NULL)                                       /* found it? */
5532     return SCPE_NXDEV;
5533 if (dptr->reset != NULL)
5534     return dptr->reset (dptr);
5535 else return SCPE_OK;
5536 }
5537 
5538 /* Reset devices start..end
5539 
5540    Inputs:
5541         start   =       number of starting device
5542    Outputs:
5543         status  =       error status
5544 */
5545 
5546 t_stat reset_all (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5547 {
5548 DEVICE *dptr;
5549 uint32 i;
5550 t_stat reason;
5551 
5552 for (i = 0; i < start; i++) {
5553     if (sim_devices[i] == NULL)
5554         return SCPE_IERR;
5555     }
5556 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
5557     if (dptr->reset != NULL) {
5558         reason = dptr->reset (dptr);
5559         if (reason != SCPE_OK)
5560             return reason;
5561         }
5562     }
5563 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
5564     if (dptr->reset != NULL) {
5565         reason = dptr->reset (dptr);
5566         if (reason != SCPE_OK)
5567             return reason;
5568         }
5569     }
5570 return SCPE_OK;
5571 }
5572 
5573 /* Reset to powerup state
5574 
5575    Inputs:
5576         start   =       number of starting device
5577    Outputs:
5578         status  =       error status
5579 */
5580 
5581 t_stat reset_all_p (uint32 start)
     /* [previous][next][first][last][top][bottom][index][help] */
5582 {
5583 t_stat r;
5584 int32 old_sw = sim_switches;
5585 
5586 sim_switches = SWMASK ('P');
5587 r = reset_all (start);
5588 sim_switches = old_sw;
5589 return r;
5590 }
5591 
5592 /* Attach command */
5593 
5594 t_stat attach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5595 {
5596 char gbuf[4*CBUFSIZE];
5597 DEVICE *dptr;
5598 UNIT *uptr;
5599 t_stat r;
5600 
5601 GET_SWITCHES (cptr);                                    /* get switches */
5602 if (*cptr == 0)                                         /* must be more */
5603     return SCPE_2FARG;
5604 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5605 GET_SWITCHES (cptr);                                    /* get switches */
5606 if (*cptr == 0)                                         /* now eol? */
5607     return SCPE_2FARG;
5608 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5609 if (dptr == NULL)                                       /* found dev? */
5610     return SCPE_NXDEV;
5611 if (uptr == NULL)                                       /* valid unit? */
5612     return SCPE_NXUN;
5613 if (uptr->flags & UNIT_ATT) {                           /* already attached? */
5614     if (!(uptr->dynflags & UNIT_ATTMULT) &&             /* and only single attachable */
5615         !(dptr->flags & DEV_DONTAUTO)) {                /* and auto detachable */
5616         r = scp_detach_unit (dptr, uptr);               /* detach it */
5617         if (r != SCPE_OK)                               /* error? */
5618             return r; }
5619     else {
5620         if (!(uptr->dynflags & UNIT_ATTMULT))
5621             return SCPE_ALATT;                          /* Already attached */
5622         }
5623     }
5624 gbuf[sizeof(gbuf)-1] = '\0';
5625 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5626 sim_trim_endspc (gbuf);                                 /* trim trailing spc */
5627 return scp_attach_unit (dptr, uptr, gbuf);              /* attach */
5628 }
5629 
5630 /* Call device-specific or file-oriented attach unit routine */
5631 
5632 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5633 {
5634 if (dptr->attach != NULL)                               /* device routine? */
5635     return dptr->attach (uptr, (CONST char *)cptr);     /* call it */
5636 return attach_unit (uptr, (CONST char *)cptr);          /* no, std routine */
5637 }
5638 
5639 /* Attach unit to file */
5640 
5641 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5642 {
5643 DEVICE *dptr;
5644 
5645 if (uptr->flags & UNIT_DIS)                             /* disabled? */
5646     return SCPE_UDIS;
5647 if (!(uptr->flags & UNIT_ATTABLE))                      /* not attachable? */
5648     return SCPE_NOATT;
5649 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5650     return SCPE_NOATT;
5651 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
5652 if (uptr->filename == NULL)
5653     return SCPE_MEM;
5654 strncpy (uptr->filename, cptr, CBUFSIZE-1);             /* save name */
5655 if ((sim_switches & SWMASK ('R')) ||                    /* read only? */
5656     ((uptr->flags & UNIT_RO) != 0)) {
5657     if (((uptr->flags & UNIT_ROABLE) == 0) &&           /* allowed? */
5658         ((uptr->flags & UNIT_RO) == 0))
5659         return attach_err (uptr, SCPE_NORO);            /* no, error */
5660     uptr->fileref = sim_fopen (cptr, "rb");             /* open rd only */
5661     if (uptr->fileref == NULL)                          /* open fail? */
5662         return attach_err (uptr, SCPE_OPENERR);         /* yes, error */
5663     uptr->flags = uptr->flags | UNIT_RO;                /* set rd only */
5664     if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5665         sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
5666         }
5667     }
5668 else {
5669     if (sim_switches & SWMASK ('N')) {                  /* new file only? */
5670         uptr->fileref = sim_fopen (cptr, "wb+");        /* open new file */
5671         if (uptr->fileref == NULL)                      /* open fail? */
5672             return attach_err (uptr, SCPE_OPENERR);     /* yes, error */
5673         if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5674             sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
5675             }
5676         }
5677     else {                                              /* normal */
5678         uptr->fileref = sim_fopen (cptr, "rb+");        /* open r/w */
5679         if (uptr->fileref == NULL) {                    /* open fail? */
5680 #if defined (EWOULDBLOCK)
5681             if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
5682 #else
5683             if ((errno == EAGAIN))
5684 #endif
5685                 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5686 
5687 #if defined(EPERM)
5688             if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
5689 #else
5690             if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
5691 #endif
5692                 if ((uptr->flags & UNIT_ROABLE) == 0)   /* allowed? */
5693                     return attach_err (uptr, SCPE_NORO);/* no error */
5694                 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
5695                 if (uptr->fileref == NULL)              /* open fail? */
5696                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5697                 uptr->flags = uptr->flags | UNIT_RO;    /* set rd only */
5698                 if (!sim_quiet) {
5699                     sim_printf ("%s: unit is read only (%s)\n", sim_dname (dptr), cptr);
5700                     }
5701                 }
5702             else {                                      /* doesn't exist */
5703                 if (sim_switches & SWMASK ('E'))        /* must exist? */
5704                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5705                 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
5706                 if (uptr->fileref == NULL)              /* open fail? */
5707                     return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5708                 if (!sim_quiet) {
5709                     sim_printf ("%s: creating new file (%s)\n", sim_dname (dptr), cptr);
5710                     }
5711                 }
5712             }                                           /* end if null */
5713         }                                               /* end else */
5714     }
5715 if (uptr->flags & UNIT_BUFABLE) {                       /* buffer? */
5716     uint32 cap = ((uint32) uptr->capac) / dptr->aincr;  /* effective size */
5717     if (uptr->flags & UNIT_MUSTBUF)                     /* dyn alloc? */
5718         uptr->filebuf = calloc (cap, SZ_D (dptr));      /* allocate */
5719     if (uptr->filebuf == NULL)                          /* no buffer? */
5720         return attach_err (uptr, SCPE_MEM);             /* error */
5721     if (!sim_quiet) {
5722         sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
5723         }
5724     uptr->hwmark = (uint32)sim_fread (uptr->filebuf,    /* read file */
5725         SZ_D (dptr), cap, uptr->fileref);
5726     uptr->flags = uptr->flags | UNIT_BUF;               /* set buffered */
5727     }
5728 uptr->flags = uptr->flags | UNIT_ATT;
5729 uptr->pos = 0;
5730 return SCPE_OK;
5731 }
5732 
5733 t_stat attach_err (UNIT *uptr, t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
5734 {
5735 FREE (uptr->filename);
5736 uptr->filename = NULL;
5737 return stat;
5738 }
5739 
5740 /* Detach command */
5741 
5742 t_stat detach_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5743 {
5744 char gbuf[CBUFSIZE];
5745 DEVICE *dptr;
5746 UNIT *uptr;
5747 
5748 GET_SWITCHES (cptr);                                    /* get switches */
5749 if (*cptr == 0)                                         /* must be more */
5750     return SCPE_2FARG;
5751 cptr = get_glyph (cptr, gbuf, 0);                       /* get next glyph */
5752 if (*cptr != 0)                                         /* now eol? */
5753     return SCPE_2MARG;
5754 if (strcmp (gbuf, "ALL") == 0)
5755     return (detach_all (0, FALSE));
5756 dptr = find_unit (gbuf, &uptr);                         /* locate unit */
5757 if (dptr == NULL)                                       /* found dev? */
5758     return SCPE_NXDEV;
5759 if (uptr == NULL)                                       /* valid unit? */
5760     return SCPE_NXUN;
5761 return scp_detach_unit (dptr, uptr);                    /* detach */
5762 }
5763 
5764 /* Detach devices start..end
5765 
5766    Inputs:
5767         start   =       number of starting device
5768         shutdown =      TRUE if simulator shutting down
5769    Outputs:
5770         status  =       error status
5771 
5772    Note that during shutdown, detach routines for non-attachable devices
5773    will be called.  These routines can implement simulator shutdown.  Error
5774    returns during shutdown are ignored.
5775 */
5776 
5777 t_stat detach_all (int32 start, t_bool shutdown)
     /* [previous][next][first][last][top][bottom][index][help] */
5778 {
5779 uint32 i, j;
5780 DEVICE *dptr;
5781 UNIT *uptr;
5782 t_stat r;
5783 
5784 if ((start < 0) || (start > 1))
5785     return SCPE_IERR;
5786 if (shutdown)
5787     sim_switches = sim_switches | SIM_SW_SHUT;          /* flag shutdown */
5788 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5789     for (j = 0; j < dptr->numunits; j++) {              /* loop thru units */
5790         uptr = (dptr->units) + j;
5791         if ((uptr->flags & UNIT_ATT) ||                 /* attached? */
5792             (shutdown && dptr->detach &&                /* shutdown, spec rtn, */
5793             !(uptr->flags & UNIT_ATTABLE))) {           /* !attachable? */
5794             r = scp_detach_unit (dptr, uptr);           /* detach unit */
5795 
5796             if ((r != SCPE_OK) && !shutdown)            /* error and not shutting down? */
5797                 return r;                               /* bail out now with error status */
5798             }
5799         }
5800     }
5801 return SCPE_OK;
5802 }
5803 
5804 /* Call device-specific or file-oriented detach unit routine */
5805 
5806 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5807 {
5808 if (dptr->detach != NULL)                               /* device routine? */
5809     return dptr->detach (uptr);
5810 return detach_unit (uptr);                              /* no, standard */
5811 }
5812 
5813 /* Detach unit from file */
5814 
5815 t_stat detach_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5816 {
5817 DEVICE *dptr;
5818 
5819 if (uptr == NULL)
5820     return SCPE_IERR;
5821 if (!(uptr->flags & UNIT_ATTABLE))                      /* attachable? */
5822     return SCPE_NOATT;
5823 if (!(uptr->flags & UNIT_ATT)) {                        /* not attached? */
5824     if (sim_switches & SIM_SW_REST)                     /* restoring? */
5825         return SCPE_OK;                                 /* allow detach */
5826     else
5827         return SCPE_NOTATT;                             /* complain */
5828     }
5829 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5830     return SCPE_OK;
5831 if (uptr->flags & UNIT_BUF) {
5832     uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5833     if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
5834         if (!sim_quiet) {
5835             sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
5836             }
5837         rewind (uptr->fileref);
5838         sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5839         if (ferror (uptr->fileref))
5840             sim_printf ("%s: I/O error - %s", sim_dname (dptr), strerror (errno));
5841         }
5842     if (uptr->flags & UNIT_MUSTBUF) {                   /* dyn alloc? */
5843         FREE (uptr->filebuf);                           /* free buf */
5844         uptr->filebuf = NULL;
5845         }
5846     uptr->flags = uptr->flags & ~UNIT_BUF;
5847     }
5848 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
5849 FREE (uptr->filename);
5850 uptr->filename = NULL;
5851 if (fclose (uptr->fileref) == EOF)
5852     return SCPE_IOERR;
5853 return SCPE_OK;
5854 }
5855 
5856 /* Get device display name */
5857 
5858 const char *sim_dname (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5859 {
5860 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
5861 }
5862 
5863 /* Get unit display name */
5864 
5865 const char *sim_uname (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5866 {
5867 DEVICE *d = find_dev_from_unit(uptr);
5868 static char uname[CBUFSIZE];
5869 
5870 if (!d)
5871     return "";
5872 if (d->numunits == 1)
5873     return sim_dname (d);
5874 sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
5875 return uname;
5876 }
5877 
5878 /* Run, go, boot, cont, step, next commands */
5879 
5880 t_stat run_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
5881 {
5882 char gbuf[CBUFSIZE] = "";
5883 CONST char *tptr;
5884 uint32 i, j;
5885 int32 sim_next = 0;
5886 int32 unitno;
5887 t_value pcv, orig_pcv;
5888 t_stat r;
5889 DEVICE *dptr;
5890 UNIT *uptr;
5891 
5892 GET_SWITCHES (cptr);                                    /* get switches */
5893 sim_step = 0;
5894 if ((flag == RU_RUN) || (flag == RU_GO)) {              /* run or go */
5895     orig_pcv = get_rval (sim_PC, 0);                    /* get current PC value */
5896     if (*cptr != 0) {                                   /* argument? */
5897         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5898         if (MATCH_CMD (gbuf, "UNTIL") != 0) {
5899             if (sim_dflt_dev && sim_vm_parse_addr)      /* address parser? */
5900                 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
5901             else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
5902             if ((tptr == gbuf) || (*tptr != 0) ||       /* error? */
5903                 (pcv > width_mask[sim_PC->width]))
5904                 return SCPE_ARG;
5905             put_rval (sim_PC, 0, pcv);                  /* Save in PC */
5906             }
5907         }
5908     if ((flag == RU_RUN) &&                             /* run? */
5909         ((r = sim_run_boot_prep (flag)) != SCPE_OK)) {  /* reset sim */
5910         put_rval (sim_PC, 0, orig_pcv);                 /* restore original PC */
5911         return r;
5912         }
5913     if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { //-V600 /* should be end */
5914         int32 saved_switches = sim_switches;
5915 
5916         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5917             cptr = get_glyph (cptr, gbuf, 0);           /* get next glyph */
5918         if (MATCH_CMD (gbuf, "UNTIL") != 0)
5919             return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
5920                                              (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
5921         sim_switches = 0;
5922         GET_SWITCHES (cptr);
5923         if ((*cptr == '\'') || (*cptr == '"')) {        /* Expect UNTIL condition */
5924             r = expect_cmd (1, cptr);
5925             if (r != SCPE_OK)
5926                 return r;
5927             }
5928         else {                                          /* BREAK UNTIL condition */
5929             if (sim_switches == 0)
5930                 sim_switches = sim_brk_dflt;
5931             sim_switches |= BRK_TYP_TEMP;               /* make this a one-shot breakpoint */
5932             sim_brk_types |= BRK_TYP_TEMP;
5933             r = ssh_break (NULL, cptr, SSH_ST);
5934             if (r != SCPE_OK)
5935                 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
5936             }
5937         sim_switches = saved_switches;
5938         }
5939     }
5940 
5941 else if ((flag == RU_STEP) ||
5942          ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
5943     static t_bool not_implemented_message = FALSE;
5944 
5945     if ((!not_implemented_message) && (flag == RU_NEXT)) {
5946         not_implemented_message = TRUE;
5947         flag = RU_STEP;
5948         }
5949     if (*cptr != 0) {                                   /* argument? */
5950         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5951         if (*cptr != 0)                                 /* should be end */
5952             return SCPE_2MARG;
5953         sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5954         if ((r != SCPE_OK) || (sim_step <= 0))          /* error? */
5955             return SCPE_ARG;
5956         }
5957     else sim_step = 1;
5958     if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
5959         sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
5960     }
5961 else if (flag == RU_NEXT) {                             /* next */
5962     t_addr *addrs;
5963 
5964     if (*cptr != 0) {                                   /* argument? */
5965         cptr = get_glyph (cptr, gbuf, 0);               /* get next glyph */
5966         if (*cptr != 0)                                 /* should be end */
5967             return SCPE_2MARG;
5968         sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5969         if ((r != SCPE_OK) || (sim_next <= 0))          /* error? */
5970             return SCPE_ARG;
5971         }
5972     else sim_next = 1;
5973     if (sim_vm_is_subroutine_call(&addrs)) {
5974         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
5975         for (i=0; addrs[i]; i++)
5976             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
5977         }
5978     else
5979         sim_step = 1;
5980     }
5981 else if (flag == RU_BOOT) {                             /* boot */
5982     if (*cptr == 0)                                     /* must be more */
5983         return SCPE_2FARG;
5984     cptr = get_glyph (cptr, gbuf, 0);                   /* get next glyph */
5985     if (*cptr != 0)                                     /* should be end */
5986         return SCPE_2MARG;
5987     dptr = find_unit (gbuf, &uptr);                     /* locate unit */
5988     if (dptr == NULL)                                   /* found dev? */
5989         return SCPE_NXDEV;
5990     if (uptr == NULL)                                   /* valid unit? */
5991         return SCPE_NXUN;
5992     if (dptr->boot == NULL)                             /* can it boot? */
5993         return SCPE_NOFNC;
5994     if (uptr->flags & UNIT_DIS)                         /* disabled? */
5995         return SCPE_UDIS;
5996     if ((uptr->flags & UNIT_ATTABLE) &&                 /* if attable, att? */
5997         !(uptr->flags & UNIT_ATT))
5998         return SCPE_UNATT;
5999     unitno = (int32) (uptr - dptr->units);              /* recover unit# */
6000     if ((r = sim_run_boot_prep (flag)) != SCPE_OK)      /* reset sim */
6001         return r;
6002     if ((r = dptr->boot (unitno, dptr)) != SCPE_OK)     /* boot device */
6003         return r;
6004     }
6005 
6006 else
6007     if (flag != RU_CONT)                                /* must be cont */
6008         return SCPE_IERR;
6009     else                                                /* CONTINUE command */
6010         if (*cptr != 0)                                 /* should be end (no arguments allowed) */
6011             return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
6012 
6013 if (sim_switches & SIM_SW_HIDE)                         /* Setup only for Remote Console Mode */
6014     return SCPE_OK;
6015 
6016 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* reposition all */
6017     for (j = 0; j < dptr->numunits; j++) {              /* seq devices */
6018         uptr = dptr->units + j;
6019         if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
6020             sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
6021         }
6022     }
6023 stop_cpu = 0;
6024 sim_is_running = 1;                                     /* flag running */
6025 if (sim_ttrun () != SCPE_OK) {                          /* set console mode */
6026     sim_is_running = 0;                                 /* flag idle */
6027     sim_ttcmd ();
6028     return SCPE_TTYERR;
6029     }
6030 if ((r = sim_check_console (30)) != SCPE_OK) {          /* check console, error? */
6031     sim_is_running = 0;                                 /* flag idle */
6032     sim_ttcmd ();
6033     return r;
6034     }
6035 #ifndef IS_WINDOWS
6036 # ifdef SIGINT
6037 if (signal (SIGINT, int_handler) == SIG_ERR) {          /* set WRU */
6038     sim_is_running = 0;                                 /* flag idle */
6039     sim_ttcmd ();
6040     return SCPE_SIGERR;
6041     }
6042 # endif
6043 #endif
6044 #ifndef IS_WINDOWS
6045 # ifdef SIGHUP
6046 if (signal (SIGHUP, int_handler) == SIG_ERR) {          /* set WRU */
6047     sim_is_running = 0;                                 /* flag idle */
6048     sim_ttcmd ();
6049     return SCPE_SIGERR;
6050     }
6051 # endif
6052 #endif
6053 #ifndef IS_WINDOWS
6054 # ifdef SIGTERM
6055 if (signal (SIGTERM, int_handler) == SIG_ERR) {         /* set WRU */
6056     sim_is_running = 0;                                 /* flag idle */
6057     sim_ttcmd ();
6058     return SCPE_SIGERR;
6059     }
6060 # endif
6061 #endif
6062 if (sim_step)                                           /* set step timer */
6063     sim_activate (&sim_step_unit, sim_step);
6064 fflush(stdout);                                         /* flush stdout */
6065 if (sim_log)                                            /* flush log if enabled */
6066     fflush (sim_log);
6067 sim_rtcn_init_all ();                                   /* re-init clocks */
6068 sim_start_timer_services ();                            /* enable wall clock timing */
6069 
6070 do {
6071     t_addr *addrs;
6072 
6073     while (1) {
6074         r = sim_instr();
6075         if (r != SCPE_REMOTE)
6076             break;
6077         sim_remote_process_command ();                  /* Process the command and resume processing */
6078         }
6079     if ((flag != RU_NEXT) ||                            /* done if not doing NEXT */
6080         (--sim_next <=0))
6081         break;
6082     if (sim_step == 0) {                                /* doing a NEXT? */
6083         t_addr val;
6084         BRKTAB *bp;
6085 
6086         if (SCPE_BARE_STATUS(r) >= SCPE_BASE)           /* done if an error occurred */
6087             break;
6088         if (sim_vm_pc_value)                            /* done if didn't stop at a dynamic breakpoint */
6089             val = (t_addr)(*sim_vm_pc_value)();
6090         else
6091             val = (t_addr)get_rval (sim_PC, 0);
6092         if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6093             break;
6094         sim_brk_clrall (BRK_TYP_DYN_STEPOVER);          /* cancel any step/over subroutine breakpoints */
6095         }
6096     else {
6097         if (r != SCPE_STEP)                             /* done if step didn't complete with step expired */
6098             break;
6099         }
6100     /* setup another next/step */
6101     sim_step = 0;
6102     if (sim_vm_is_subroutine_call(&addrs)) {
6103         sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6104         for (i=0; addrs[i]; i++)
6105             sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6106         }
6107     else
6108         sim_step = 1;
6109     if (sim_step)                                       /* set step timer */
6110         sim_activate (&sim_step_unit, sim_step);
6111     } while (1);
6112 
6113 sim_is_running = 0;                                     /* flag idle */
6114 sim_stop_timer_services ();                             /* disable wall clock timing */
6115 sim_ttcmd ();                                           /* restore console */
6116 sim_brk_clrall (BRK_TYP_DYN_STEPOVER);                  /* cancel any step/over subroutine breakpoints */
6117 signal (SIGINT, SIG_DFL);                               /* cancel WRU */
6118 #ifdef SIGHUP
6119 signal (SIGHUP, SIG_DFL);                               /* cancel WRU */
6120 #endif
6121 signal (SIGTERM, SIG_DFL);                              /* cancel WRU */
6122 if (sim_log)                                            /* flush console log */
6123     fflush (sim_log);
6124 if (sim_deb)                                            /* flush debug log */
6125     sim_debug_flush ();
6126 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {     /* flush attached files */
6127     for (j = 0; j < dptr->numunits; j++) {              /* if not buffered in mem */
6128         uptr = dptr->units + j;
6129         if (uptr->flags & UNIT_ATT) {                   /* attached, */
6130             if (uptr->io_flush)                         /* unit specific flush routine */
6131                 uptr->io_flush (uptr);                  /* call it */
6132             else {
6133                 if (!(uptr->flags & UNIT_BUF) &&        /* not buffered, */
6134                     (uptr->fileref) &&                  /* real file, */
6135                     !(uptr->dynflags & UNIT_NO_FIO) &&  /* is FILE *, */
6136                     !(uptr->flags & UNIT_RO))           /* not read only? */
6137                     fflush (uptr->fileref);
6138                 }
6139             }
6140         }
6141     }
6142 sim_cancel (&sim_step_unit);                            /* cancel step timer */
6143 UPDATE_SIM_TIME;                                        /* update sim time */
6144 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6145 }
6146 
6147 /* run command message handler */
6148 
6149 void
6150 run_cmd_message (const char *unechoed_cmdline, t_stat r)
     /* [previous][next][first][last][top][bottom][index][help] */
6151 {
6152 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6153     sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
6154 #ifdef WIN_STDIO
6155 (void)fflush(stderr);
6156 (void)fflush(stdout);
6157 #endif /* ifdef WIN_STDIO */
6158 fprint_stopped (stdout, r);                         /* print msg */
6159 if (sim_log && (sim_log != stdout))                 /* log if enabled */
6160     fprint_stopped (sim_log, r);
6161 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6162     fprint_stopped (sim_deb, r);
6163 #ifdef WIN_STDIO
6164 (void)fflush(stderr);
6165 (void)fflush(stdout);
6166 #endif /* ifdef WIN_STDIO */
6167 }
6168 
6169 /* Common setup for RUN or BOOT */
6170 
6171 t_stat sim_run_boot_prep (int32 flag)
     /* [previous][next][first][last][top][bottom][index][help] */
6172 {
6173 UNIT *uptr;
6174 t_stat r;
6175 
6176 sim_interval = 0;                                       /* reset queue */
6177 sim_time = sim_rtime = 0;
6178 noqueue_time = 0;
6179 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6180     sim_clock_queue = uptr->next;
6181     uptr->next = NULL;
6182     }
6183 r = reset_all (0);
6184 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6185     if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6186         sim_printf ("Resetting all devices...  This may not have been your intention.\n");
6187         sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
6188         }
6189     run_cmd_did_reset = TRUE;
6190     }
6191 return r;
6192 }
6193 
6194 /* Print stopped message
6195  * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6196  * call the indicated routine to print additional information after the message
6197  * and before the PC value is printed.  If the routine returns FALSE, skip
6198  * printing the PC and its related instruction.
6199  */
6200 
6201 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6202 {
6203 int32 i;
6204 t_stat r = 0;
6205 t_addr k;
6206 t_value pcval;
6207 
6208 fputc ('\n', st);                                       /* start on a new line */
6209 
6210 if (v >= SCPE_BASE)                                     /* SCP error? */
6211     fputs (sim_error_text (v), st);                     /* print it from the SCP list */
6212 else {                                                  /* VM error */
6213     fputs (sim_stop_messages [v], st);                  /* print the VM-specific message */
6214 
6215     if ((sim_vm_fprint_stopped != NULL) &&              /* if a VM-specific stop handler is defined */
6216         (!sim_vm_fprint_stopped (st, v)))               /*   call it; if it returned FALSE, */
6217         return;                                         /*     we're done */
6218     }
6219 
6220 fprintf (st, ", %s: ", pc->name);                       /* print the name of the PC register */
6221 
6222 pcval = get_rval (pc, 0);
6223 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr)       /* if reg wants VM-specific printer */
6224     sim_vm_fprint_addr (st, dptr, (t_addr) pcval);      /*   call it to print the PC address */
6225 else fprint_val (st, pcval, pc->radix, pc->width,       /* otherwise, print as a numeric value */
6226     pc->flags & REG_FMT);                               /*   with the radix and formatting specified */
6227 if ((dptr != NULL) && (dptr->examine != NULL)) {
6228     for (i = 0; i < sim_emax; i++)
6229         sim_eval[i] = 0;
6230     for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6231         if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6232             break;
6233         }
6234     if ((r == SCPE_OK) || (i > 0)) {
6235         fprintf (st, " (");
6236         if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6237             fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6238         fprintf (st, ")");
6239         }
6240     }
6241 fprintf (st, "\n");
6242 return;
6243 }
6244 
6245 void fprint_stopped (FILE *st, t_stat v)
     /* [previous][next][first][last][top][bottom][index][help] */
6246 {
6247 #ifdef WIN_STDIO
6248 (void)fflush(stderr);
6249 (void)fflush(stdout);
6250 #endif /* ifdef WIN_STDIO */
6251 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6252 return;
6253 }
6254 
6255 /* Unit service for step timeout, originally scheduled by STEP n command
6256    Return step timeout SCP code, will cause simulation to stop */
6257 
6258 t_stat step_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6259 {
6260 return SCPE_STEP;
6261 }
6262 
6263 /* Unit service to facilitate expect matching to stop simulation.
6264    Return expect SCP code, will cause simulation to stop */
6265 
6266 t_stat expect_svc (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6267 {
6268 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6269 }
6270 
6271 /* Signal handler for ^C signal - set stop simulation flag */
6272 
6273 void int_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help] */
6274 {
6275 stop_cpu = 1;
6276 return;
6277 }
6278 
6279 /* Examine/deposit commands */
6280 
6281 t_stat exdep_cmd (int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6282 {
6283 char gbuf[CBUFSIZE];
6284 CONST char *gptr;
6285 CONST char *tptr = NULL;
6286 int32 opt;
6287 t_addr low, high;
6288 t_stat reason = SCPE_IERR;
6289 DEVICE *tdptr;
6290 REG *lowr, *highr;
6291 FILE *ofile;
6292 
6293 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT;               /* options for all */
6294 if (flag == EX_E)                                       /* extra for EX */
6295     opt = opt | CMD_OPT_OF;
6296 cptr = get_sim_opt (opt, cptr, &reason);                /* get cmd options */
6297 if (!cptr)                                              /* error? */
6298     return reason;
6299 if (*cptr == 0)                                         /* must be more */
6300     return SCPE_2FARG;
6301 if (sim_dfunit == NULL)                                 /* got a unit? */
6302     return SCPE_NXUN;
6303 cptr = get_glyph (cptr, gbuf, 0);                       /* get list */
6304 if ((flag == EX_D) && (*cptr == 0))                     /* deposit needs more */
6305 
6306     return SCPE_2FARG;
6307 ofile = sim_ofile? sim_ofile: stdout;                   /* no ofile? use stdout */
6308 
6309 for (gptr = gbuf, reason = SCPE_OK;
6310     (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6311     tdptr = sim_dfdev;                                  /* working dptr */
6312     if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6313         tptr = gptr + strlen ("STATE");
6314         if (*tptr && (*tptr++ != ','))
6315             return SCPE_ARG;
6316         if ((lowr = sim_dfdev->registers) == NULL)
6317             return SCPE_NXREG;
6318         for (highr = lowr; highr->name != NULL; highr++) ;
6319         sim_switches = sim_switches | SIM_SW_HIDE;
6320         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6321             lowr, --highr, 0, 0);
6322         continue;
6323         }
6324 
6325     /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
6326     if ((lowr = find_reg (gptr, &tptr, tdptr)) ||       /* local reg or */
6327         (!(sim_opt_out & CMD_OPT_DFT) &&                /* no dflt, global? */
6328         (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6329         low = high = 0;
6330         if ((*tptr == '-') || (*tptr == ':')) {
6331             highr = find_reg (tptr + 1, &tptr, tdptr);
6332             if (highr == NULL)
6333                 return SCPE_NXREG;
6334             }
6335         else {
6336             highr = lowr;
6337             if (*tptr == '[') {
6338                 if (lowr->depth <= 1)
6339                     return SCPE_ARG;
6340                 tptr = get_range (NULL, tptr + 1, &low, &high,
6341                     10, lowr->depth - 1, ']');
6342                 if (tptr == NULL)
6343                     return SCPE_ARG;
6344                 }
6345             }
6346         if (*tptr && (*tptr++ != ','))
6347             return SCPE_ARG;
6348         reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6349             lowr, highr, (uint32) low, (uint32) high);
6350         continue;
6351         }
6352 
6353     tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6354         (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6355         sim_dfunit->capac - sim_dfdev->aincr), 0);
6356     if (tptr == NULL)
6357         return SCPE_ARG;
6358     if (*tptr && (*tptr++ != ','))
6359         return SCPE_ARG;
6360     reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6361         sim_dfdev, sim_dfunit);
6362     }                                                   /* end for */
6363 if (sim_ofile)                                          /* close output file */
6364     fclose (sim_ofile);
6365 return reason;
6366 }
6367 
6368 /* Loop controllers for examine/deposit
6369 
6370    exdep_reg_loop       examine/deposit range of registers
6371    exdep_addr_loop      examine/deposit range of addresses
6372 */
6373 
6374 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6375     REG *lowr, REG *highr, uint32 lows, uint32 highs)
6376 {
6377 t_stat reason;
6378 uint32 idx, val_start=lows;
6379 t_value val, last_val;
6380 REG *rptr;
6381 
6382 if ((lowr == NULL) || (highr == NULL))
6383     return SCPE_IERR;
6384 if (lowr > highr)
6385     return SCPE_ARG;
6386 for (rptr = lowr; rptr <= highr; rptr++) {
6387     if ((sim_switches & SIM_SW_HIDE) &&
6388         (rptr->flags & REG_HIDDEN))
6389         continue;
6390     val = last_val = 0;
6391     for (idx = lows; idx <= highs; idx++) {
6392         if (idx >= rptr->depth)
6393             return SCPE_SUB;
6394         val = get_rval (rptr, idx);
6395         if (schptr && !test_search (&val, schptr))
6396             continue;
6397         if (flag == EX_E) {
6398             if ((idx > lows) && (val == last_val))
6399                 continue;
6400             if (idx > val_start+1) {
6401                 if (idx-1 == val_start+1) {
6402                     reason = ex_reg (ofile, val, flag, rptr, idx-1);
6403                     if (reason != SCPE_OK)
6404                         return reason;
6405                     if (sim_log && (ofile == stdout))
6406                         ex_reg (sim_log, val, flag, rptr, idx-1);
6407                     }
6408                 else {
6409                     if (val_start+1 != idx-1) {
6410                         Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6411                         if (sim_log && (ofile == stdout))
6412                             Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6413                         }
6414                     else {
6415                         Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6416                         if (sim_log && (ofile == stdout))
6417                             Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6418                         }
6419                     }
6420                 }
6421             sim_last_val = last_val = val;
6422             val_start = idx;
6423             reason = ex_reg (ofile, val, flag, rptr, idx);
6424             if (reason != SCPE_OK)
6425                 return reason;
6426             if (sim_log && (ofile == stdout))
6427                 ex_reg (sim_log, val, flag, rptr, idx);
6428             }
6429         if (flag != EX_E) {
6430             reason = dep_reg (flag, cptr, rptr, idx);
6431             if (reason != SCPE_OK)
6432                 return reason;
6433             }
6434         }
6435     if ((flag == EX_E) && (val_start != highs)) {
6436         if (highs == val_start+1) {
6437             reason = ex_reg (ofile, val, flag, rptr, highs);
6438             if (reason != SCPE_OK)
6439                 return reason;
6440             if (sim_log && (ofile == stdout))
6441                 ex_reg (sim_log, val, flag, rptr, highs);
6442             }
6443         else {
6444             if (val_start+1 != highs) {
6445                 Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6446                 if (sim_log && (ofile == stdout))
6447                     Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6448                 }
6449             else {
6450                 Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6451                 if (sim_log && (ofile == stdout))
6452                     Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6453                 }
6454             }
6455         }
6456     }
6457 return SCPE_OK;
6458 }
6459 
6460 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6461     t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6462 {
6463 t_addr i, mask;
6464 t_stat reason;
6465 
6466 if (uptr->flags & UNIT_DIS)                             /* disabled? */
6467     return SCPE_UDIS;
6468 mask = (t_addr) width_mask[dptr->awidth];
6469 if ((low > mask) || (high > mask) || (low > high))
6470     return SCPE_ARG;
6471 for (i = low; i <= high; ) {                            /* all paths must incr!! */
6472     reason = get_aval (i, dptr, uptr);                  /* get data */
6473     if (reason != SCPE_OK)                              /* return if error */
6474         return reason;
6475     if (schptr && !test_search (sim_eval, schptr))
6476         i = i + dptr->aincr;                            /* sch fails, incr */
6477     else {                                              /* no sch or success */
6478         if (flag != EX_D) {                             /* ex, ie, or id? */
6479             reason = ex_addr (ofile, flag, i, dptr, uptr);
6480             if (reason > SCPE_OK)
6481                 return reason;
6482             if (sim_log && (ofile == stdout))
6483                 ex_addr (sim_log, flag, i, dptr, uptr);
6484             }
6485         else reason = 1 - dptr->aincr;                  /* no, dflt incr */
6486         if (flag != EX_E) {                             /* ie, id, or d? */
6487             reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6488             if (reason > SCPE_OK)
6489                 return reason;
6490             }
6491         i = i + (1 - reason);                           /* incr */
6492         }
6493     }
6494 return SCPE_OK;
6495 }
6496 
6497 /* Examine register routine
6498 
6499    Inputs:
6500         ofile   =       output stream
6501         val     =       current register value
6502         flag    =       type of ex/mod command (ex, iex, idep)
6503         rptr    =       pointer to register descriptor
6504         idx     =       index
6505    Outputs:
6506         return  =       error status
6507 */
6508 
6509 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6510 {
6511 int32 rdx;
6512 
6513 if (rptr == NULL)
6514     return SCPE_IERR;
6515 if (rptr->depth > 1)
6516     Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6517 else Fprintf (ofile, "%s:\t", rptr->name);
6518 if (!(flag & EX_E))
6519     return SCPE_OK;
6520 GET_RADIX (rdx, rptr->radix);
6521 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6522     sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6523 else if (!(rptr->flags & REG_VMFLAGS) ||
6524     (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6525                  NULL, sim_switches | SIM_SW_REG) > 0)) {
6526         fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6527         if (rptr->fields) {
6528             Fprintf (ofile, "\t");
6529             fprint_fields (ofile, val, val, rptr->fields);
6530             }
6531         }
6532 if (flag & EX_I)
6533     Fprintf (ofile, "\t");
6534 else Fprintf (ofile, "\n");
6535 return SCPE_OK;
6536 }
6537 
6538 /* Get register value
6539 
6540    Inputs:
6541         rptr    =       pointer to register descriptor
6542         idx     =       index
6543    Outputs:
6544         return  =       register value
6545 */
6546 
6547 t_value get_rval (REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6548 {
6549 size_t sz;
6550 t_value val;
6551 uint32 *ptr;
6552 
6553 sz = SZ_R (rptr);
6554 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6555     idx = idx + rptr->qptr;
6556     if (idx >= rptr->depth) idx = idx - rptr->depth;
6557     }
6558 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6559     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6560     if (sz <= sizeof (uint32))
6561         val = *ptr;
6562     else val = *((t_uint64 *) ptr); //-V1032
6563     }
6564 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6565     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6566     if (sz <= sizeof (uint32))
6567         val = *ptr;
6568     else val = *((t_uint64 *) ptr);
6569     }
6570 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6571     (sz == sizeof (uint8)))
6572     val = *(((uint8 *) rptr->loc) + idx);
6573 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6574     (sz == sizeof (uint16)))
6575     val = *(((uint16 *) rptr->loc) + idx);
6576 else if (sz <= sizeof (uint32))
6577      val = *(((uint32 *) rptr->loc) + idx);
6578 else val = *(((t_uint64 *) rptr->loc) + idx);
6579 val = (val >> rptr->offset) & width_mask[rptr->width];
6580 return val;
6581 }
6582 
6583 /* Deposit register routine
6584 
6585    Inputs:
6586         flag    =       type of deposit (normal/interactive)
6587         cptr    =       pointer to input string
6588         rptr    =       pointer to register descriptor
6589         idx     =       index
6590    Outputs:
6591         return  =       error status
6592 */
6593 
6594 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
     /* [previous][next][first][last][top][bottom][index][help] */
6595 {
6596 t_stat r;
6597 t_value val, mask;
6598 int32 rdx;
6599 CONST char *tptr;
6600 char gbuf[CBUFSIZE];
6601 
6602 if ((cptr == NULL) || (rptr == NULL))
6603     return SCPE_IERR;
6604 if (rptr->flags & REG_RO)
6605     return SCPE_RO;
6606 if (flag & EX_I) {
6607     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6608     if (sim_log)
6609         fprintf (sim_log, "%s\n", cptr? cptr: "");
6610     if (cptr == NULL)                                   /* force exit */
6611         return 1;
6612     if (*cptr == 0)                                     /* success */
6613         return SCPE_OK;
6614     }
6615 mask = width_mask[rptr->width];
6616 GET_RADIX (rdx, rptr->radix);
6617 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) {    /* address form? */
6618     val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
6619     if ((tptr == cptr) || (*tptr != 0) || (val > mask))
6620         return SCPE_ARG;
6621     }
6622 else
6623     if (!(rptr->flags & REG_VMFLAGS) ||                 /* don't use sym? */
6624         (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
6625                     &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
6626     val = get_uint (cptr, rdx, mask, &r);
6627     if (r != SCPE_OK)
6628         return SCPE_ARG;
6629     }
6630 if ((rptr->flags & REG_NZ) && (val == 0))
6631     return SCPE_ARG;
6632 put_rval (rptr, idx, val);
6633 return SCPE_OK;
6634 }
6635 
6636 /* Put register value
6637 
6638    Inputs:
6639         rptr    =       pointer to register descriptor
6640         idx     =       index
6641         val     =       new value
6642         mask    =       mask
6643    Outputs:
6644         none
6645 */
6646 
6647 void put_rval (REG *rptr, uint32 idx, t_value val)
     /* [previous][next][first][last][top][bottom][index][help] */
6648 {
6649 size_t sz;
6650 t_value mask;
6651 uint32 *ptr;
6652 
6653 #define PUT_RVAL(sz,rp,id,v,m)            \
6654     *(((sz *) rp->loc) + id) =            \
6655         (sz)((*(((sz *) rp->loc) + id) &  \
6656             ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
6657 
6658 if (rptr == sim_PC)
6659     sim_brk_npc (0);
6660 sz = SZ_R (rptr);
6661 mask = width_mask[rptr->width];
6662 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6663     idx = idx + rptr->qptr;
6664     if (idx >= rptr->depth)
6665         idx = idx - rptr->depth;
6666     }
6667 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6668     ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6669     if (sz <= sizeof (uint32))
6670         *ptr = (*ptr &
6671         ~(((uint32) mask) << rptr->offset)) |
6672         (((uint32) val) << rptr->offset);
6673     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr) //-V1032
6674         & ~(mask << rptr->offset)) | (val << rptr->offset);
6675     }
6676 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6677     ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6678     if (sz <= sizeof (uint32))
6679         *((uint32 *) ptr) = (*((uint32 *) ptr) &
6680         ~(((uint32) mask) << rptr->offset)) |
6681         (((uint32) val) << rptr->offset);
6682     else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6683         & ~(mask << rptr->offset)) | (val << rptr->offset);
6684     }
6685 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6686     (sz == sizeof (uint8)))
6687     PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
6688 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6689     (sz == sizeof (uint16)))
6690     PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
6691 else if (sz <= sizeof (uint32))
6692     PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
6693 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
6694 return;
6695 }
6696 
6697 /* Examine address routine
6698 
6699    Inputs: (sim_eval is an implicit argument)
6700         ofile   =       output stream
6701         flag    =       type of ex/mod command (ex, iex, idep)
6702         addr    =       address to examine
6703         dptr    =       pointer to device
6704         uptr    =       pointer to unit
6705    Outputs:
6706         return  =       if > 0, error status
6707                         if <= 0,-number of extra addr units retired
6708 */
6709 
6710 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6711 {
6712 t_stat reason;
6713 int32 rdx;
6714 
6715 if (sim_vm_fprint_addr)
6716     sim_vm_fprint_addr (ofile, dptr, addr);
6717 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
6718 Fprintf (ofile, ":\t");
6719 if (!(flag & EX_E))
6720     return (1 - dptr->aincr);
6721 
6722 GET_RADIX (rdx, dptr->dradix);
6723 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
6724     fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
6725     reason = 1 - dptr->aincr;
6726     }
6727 if (flag & EX_I)
6728     Fprintf (ofile, "\t");
6729 else Fprintf (ofile, "\n");
6730 return reason;
6731 }
6732 
6733 /* Get address routine
6734 
6735    Inputs:
6736         flag    =       type of ex/mod command (ex, iex, idep)
6737         addr    =       address to examine
6738         dptr    =       pointer to device
6739         uptr    =       pointer to unit
6740    Outputs: (sim_eval is an implicit output)
6741         return  =       error status
6742 */
6743 
6744 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6745 {
6746 int32 i;
6747 t_value mask;
6748 t_addr j, loc;
6749 size_t sz;
6750 t_stat reason = SCPE_OK;
6751 
6752 if ((dptr == NULL) || (uptr == NULL))
6753     return SCPE_IERR;
6754 mask = width_mask[dptr->dwidth];
6755 for (i = 0; i < sim_emax; i++)
6756     sim_eval[i] = 0;
6757 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
6758     if (dptr->examine != NULL) {
6759         reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
6760         if (reason != SCPE_OK)
6761             break;
6762         }
6763     else {
6764         if (!(uptr->flags & UNIT_ATT))
6765             return SCPE_UNATT;
6766         if (uptr->dynflags & UNIT_NO_FIO)
6767             return SCPE_NOFNC;
6768         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
6769             reason = SCPE_NXM;
6770             break;
6771             }
6772         sz = SZ_D (dptr);
6773         loc = j / dptr->aincr;
6774         if (uptr->flags & UNIT_BUF) {
6775             SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
6776             }
6777         else {
6778             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6779             sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
6780             if ((feof (uptr->fileref)) &&
6781                !(uptr->flags & UNIT_FIX)) {
6782                 reason = SCPE_EOF;
6783                 break;
6784                 }
6785             else if (ferror (uptr->fileref)) {
6786                 clearerr (uptr->fileref);
6787                 reason = SCPE_IOERR;
6788                 break;
6789                 }
6790             }
6791         }
6792     sim_last_val = sim_eval[i] = sim_eval[i] & mask;
6793     }
6794 if ((reason != SCPE_OK) && (i == 0))
6795     return reason;
6796 return SCPE_OK;
6797 }
6798 
6799 /* Deposit address routine
6800 
6801    Inputs:
6802         flag    =       type of deposit (normal/interactive)
6803         cptr    =       pointer to input string
6804         addr    =       address to examine
6805         dptr    =       pointer to device
6806         uptr    =       pointer to unit
6807         dfltinc =       value to return on cr input
6808    Outputs:
6809         return  =       if > 0, error status
6810                         if <= 0, -number of extra address units retired
6811 */
6812 
6813 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
6814     UNIT *uptr, int32 dfltinc)
6815 {
6816 int32 i, count, rdx;
6817 t_addr j, loc;
6818 t_stat r, reason;
6819 t_value mask;
6820 size_t sz;
6821 char gbuf[CBUFSIZE];
6822 
6823 if (dptr == NULL)
6824     return SCPE_IERR;
6825 if (flag & EX_I) {
6826     cptr = read_line (gbuf, sizeof(gbuf), stdin);
6827     if (sim_log)
6828         fprintf (sim_log, "%s\n", cptr? cptr: "");
6829     if (cptr == NULL)                                   /* force exit */
6830         return 1;
6831     if (*cptr == 0)                                     /* success */
6832         return dfltinc;
6833     }
6834 if (uptr->flags & UNIT_RO)                              /* read only? */
6835     return SCPE_RO;
6836 mask = width_mask[dptr->dwidth];
6837 
6838 GET_RADIX (rdx, dptr->dradix);
6839 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
6840     sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
6841     if (reason != SCPE_OK)
6842         return reason;
6843     reason = dfltinc;
6844     }
6845 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
6846 
6847 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
6848     sim_eval[i] = sim_eval[i] & mask;
6849     if (dptr->deposit != NULL) {
6850         r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
6851         if (r != SCPE_OK)
6852             return r;
6853         }
6854     else {
6855         if (!(uptr->flags & UNIT_ATT))
6856             return SCPE_UNATT;
6857         if (uptr->dynflags & UNIT_NO_FIO)
6858             return SCPE_NOFNC;
6859         if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
6860             return SCPE_NXM;
6861         sz = SZ_D (dptr);
6862         loc = j / dptr->aincr;
6863         if (uptr->flags & UNIT_BUF) {
6864             SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
6865             if (loc >= uptr->hwmark)
6866                 uptr->hwmark = (uint32) loc + 1;
6867             }
6868         else {
6869             sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6870             sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
6871             if (ferror (uptr->fileref)) {
6872                 clearerr (uptr->fileref);
6873                 return SCPE_IOERR;
6874                 }
6875             }
6876         }
6877     }
6878 return reason;
6879 }
6880 
6881 /* Evaluate command */
6882 
6883 t_stat eval_cmd (int32 flg, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
6884 {
6885 if (!sim_dflt_dev)
6886   return SCPE_ARG;
6887 DEVICE *dptr = sim_dflt_dev;
6888 int32 i, rdx, a, lim;
6889 t_stat r;
6890 
6891 GET_SWITCHES (cptr);
6892 GET_RADIX (rdx, dptr->dradix);
6893 for (i = 0; i < sim_emax; i++)
6894 sim_eval[i] = 0;
6895 if (*cptr == 0)
6896     return SCPE_2FARG;
6897 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
6898     sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
6899     if (r != SCPE_OK)
6900         return r;
6901     }
6902 lim = 1 - r;
6903 for (i = a = 0; a < lim; ) {
6904     sim_printf ("%d:\t", a);
6905     if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6906         r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6907     if (sim_log) {
6908         if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6909             r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6910         }
6911     sim_printf ("\n");
6912     if (r < 0)
6913         a = a + 1 - r;
6914     else a = a + dptr->aincr;
6915     i = a / dptr->aincr;
6916     }
6917 return SCPE_OK;
6918 }
6919 
6920 /* String processing routines
6921 
6922    read_line            read line
6923 
6924    Inputs:
6925         cptr    =       pointer to buffer
6926         size    =       maximum size
6927         stream  =       pointer to input stream
6928    Outputs:
6929         optr    =       pointer to first non-blank character
6930                         NULL if EOF
6931 */
6932 
6933 char *read_line (char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
6934 {
6935 return read_line_p (NULL, cptr, size, stream);
6936 }
6937 
6938 /* read_line_p          read line with prompt
6939 
6940    Inputs:
6941         prompt  =       pointer to prompt string
6942         cptr    =       pointer to buffer
6943         size    =       maximum size
6944         stream  =       pointer to input stream
6945    Outputs:
6946         optr    =       pointer to first non-blank character
6947                         NULL if EOF
6948 */
6949 
6950 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
6951 {
6952 char *tptr;
6953 
6954 if (prompt) {                                           /* interactive? */
6955 #ifdef HAVE_LINEHISTORY
6956         char *tmpc = linenoise (prompt);                /* get cmd line */
6957         if (tmpc == NULL)                               /* bad result? */
6958             cptr = NULL;
6959         else {
6960             strncpy (cptr, tmpc, size-1);               /* copy result */
6961             linenoiseHistoryAdd (tmpc);                 /* add to history */
6962             FREE (tmpc);                                /* free temp */
6963             }
6964         }
6965 #else
6966         fflush (stdout);                                /* flush output */
6967         printf ("%s", prompt);                          /* display prompt */
6968         fflush (stdout);                                /* flush output */
6969         cptr = fgets (cptr, size, stream);              /* get cmd line */
6970         }
6971 #endif /* ifdef HAVE_LINEHISTORY */
6972 else cptr = fgets (cptr, size, stream);                 /* get cmd line */
6973 
6974 if (cptr == NULL) {
6975     clearerr (stream);                                  /* clear error */
6976     return NULL;                                        /* ignore EOF */
6977     }
6978 for (tptr = cptr; tptr < (cptr + size); tptr++) {       /* remove cr or nl */
6979     if ((*tptr == '\n') || (*tptr == '\r') ||
6980         (tptr == (cptr + size - 1))) {                  /* str max length? */
6981         *tptr = 0;                                      /* terminate */
6982         break;
6983         }
6984     }
6985 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3))              /* Skip/ignore UTF8_BOM */
6986     memmove (cptr, cptr + 3, strlen (cptr + 3));
6987 while (sim_isspace (*cptr))                             /* trim leading spc */
6988     cptr++;
6989 if ((*cptr == ';') || (*cptr == '#')) {                 /* ignore comment */
6990     if (sim_do_echo)                                    /* echo comments if -v */
6991         sim_printf("%s> %s\n", do_position(), cptr);
6992     *cptr = 0;
6993     }
6994 
6995 return cptr;
6996 }
6997 
6998 /* get_glyph            get next glyph (force upper case)
6999    get_glyph_nc         get next glyph (no conversion)
7000    get_glyph_quoted     get next glyph (potentially enclosed in quotes, no conversion)
7001    get_glyph_cmd        get command glyph (force upper case, extract leading !)
7002    get_glyph_gen        get next glyph (general case)
7003 
7004    Inputs:
7005         iptr        =   pointer to input string
7006         optr        =   pointer to output string
7007         mchar       =   optional end of glyph character
7008         uc          =   TRUE for convert to upper case (_gen only)
7009         quote       =   TRUE to allow quote enclosing values (_gen only)
7010         escape_char =   optional escape character within quoted strings (_gen only)
7011 
7012    Outputs
7013         result      =   pointer to next character in input string
7014 */
7015 
7016 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] */
7017 {
7018 t_bool quoting = FALSE;
7019 t_bool escaping = FALSE;
7020 char quote_char = 0;
7021 
7022 while ((*iptr != 0) &&
7023        ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
7024     if (quote) {
7025         if (quoting) {
7026             if (!escaping) {
7027                 if (*iptr == escape_char)
7028                     escaping = TRUE;
7029                 else
7030                     if (*iptr == quote_char)
7031                         quoting = FALSE;
7032                 }
7033             else
7034                 escaping = FALSE;
7035             }
7036         else {
7037             if ((*iptr == '"') || (*iptr == '\'')) {
7038                 quoting = TRUE;
7039                 quote_char = *iptr;
7040                 }
7041             }
7042         }
7043     if (sim_islower (*iptr) && uc)
7044         *optr = (char)toupper (*iptr);
7045     else *optr = *iptr;
7046     iptr++; optr++;
7047     }
7048 if (mchar && (*iptr == mchar))              /* skip input terminator */
7049     iptr++;
7050 *optr = 0;                                  /* terminate result string */
7051 while (sim_isspace (*iptr))                 /* absorb additional input spaces */
7052     iptr++;
7053 return iptr;
7054 }
7055 
7056 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7057 {
7058 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7059 }
7060 
7061 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7062 {
7063 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7064 }
7065 
7066 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
     /* [previous][next][first][last][top][bottom][index][help] */
7067 {
7068 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7069 }
7070 
7071 CONST char *get_glyph_cmd (const char *iptr, char *optr)
     /* [previous][next][first][last][top][bottom][index][help] */
7072 {
7073 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7074 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7075     strcpy (optr, "!");                     /* return ! as command glyph */
7076     return (CONST char *)(iptr + 1);        /* and skip over the leading ! */
7077     }
7078 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7079 }
7080 
7081 /* Trim trailing spaces from a string
7082 
7083     Inputs:
7084         cptr    =       pointer to string
7085     Outputs:
7086         cptr    =       pointer to string
7087 */
7088 
7089 char *sim_trim_endspc (char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7090 {
7091 char *tptr;
7092 
7093 tptr = cptr + strlen (cptr);
7094 while ((--tptr >= cptr) && sim_isspace (*tptr))
7095     *tptr = 0;
7096 return cptr;
7097 }
7098 
7099 int sim_isspace (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7100 {
7101 return (c & 0x80) ? 0 : isspace (c);
7102 }
7103 
7104 int sim_islower (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7105 {
7106 return (c & 0x80) ? 0 : islower (c);
7107 }
7108 
7109 int sim_isalpha (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7110 {
7111 return (c & 0x80) ? 0 : isalpha (c);
7112 }
7113 
7114 int sim_isprint (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7115 {
7116 return (c & 0x80) ? 0 : isprint (c);
7117 }
7118 
7119 int sim_isdigit (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7120 {
7121 return (c & 0x80) ? 0 : isdigit (c);
7122 }
7123 
7124 int sim_isgraph (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7125 {
7126 return (c & 0x80) ? 0 : isgraph (c);
7127 }
7128 
7129 int sim_isalnum (char c)
     /* [previous][next][first][last][top][bottom][index][help] */
7130 {
7131 return (c & 0x80) ? 0 : isalnum (c);
7132 }
7133 
7134 /* get_uint             unsigned number
7135 
7136    Inputs:
7137         cptr    =       pointer to input string
7138         radix   =       input radix
7139         max     =       maximum acceptable value
7140         *status =       pointer to error status
7141    Outputs:
7142         val     =       value
7143 */
7144 
7145 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
     /* [previous][next][first][last][top][bottom][index][help] */
7146 {
7147 t_value val;
7148 CONST char *tptr;
7149 
7150 *status = SCPE_OK;
7151 val = strtotv ((CONST char *)cptr, &tptr, radix);
7152 if ((cptr == tptr) || (val > max))
7153     *status = SCPE_ARG;
7154 else {
7155     while (sim_isspace (*tptr)) tptr++;
7156     if (*tptr != 0)
7157         *status = SCPE_ARG;
7158     }
7159 return val;
7160 }
7161 
7162 /* get_range            range specification
7163 
7164    Inputs:
7165         dptr    =       pointer to device (NULL if none)
7166         cptr    =       pointer to input string
7167         *lo     =       pointer to low result
7168         *hi     =       pointer to high result
7169         aradix  =       radix
7170         max     =       default high value
7171         term    =       terminating character, 0 if none
7172    Outputs:
7173         tptr    =       input pointer after processing
7174                         NULL if error
7175 */
7176 
7177 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
     /* [previous][next][first][last][top][bottom][index][help] */
7178     uint32 rdx, t_addr max, char term)
7179 {
7180 CONST char *tptr;
7181 
7182 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) {    /* ALL? */
7183     tptr = cptr + strlen ("ALL");
7184     *lo = 0;
7185     *hi = max;
7186     }
7187 else {
7188     if ((strncmp (cptr, ".", strlen (".")) == 0) &&             /* .? */
7189         ((cptr[1] == '\0') ||
7190          (cptr[1] == '-')  ||
7191          (cptr[1] == ':')  ||
7192          (cptr[1] == '/'))) {
7193         tptr = cptr + strlen (".");
7194         *lo = *hi = sim_last_addr;
7195         }
7196     else {
7197         if (strncmp (cptr, "$", strlen ("$")) == 0) {           /* $? */
7198             tptr = cptr + strlen ("$");
7199             *hi = *lo = (t_addr)sim_last_val;
7200             }
7201         else {
7202             if (dptr && sim_vm_parse_addr)                      /* get low */
7203                 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7204             else
7205                 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7206             if (cptr == tptr)                                   /* error? */
7207                     return NULL;
7208             }
7209         }
7210     if ((*tptr == '-') || (*tptr == ':')) {             /* range? */
7211         cptr = tptr + 1;
7212         if (dptr && sim_vm_parse_addr)                  /* get high */
7213             *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7214         else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7215         if (cptr == tptr)
7216             return NULL;
7217         if (*lo > *hi)
7218             return NULL;
7219         }
7220     else if (*tptr == '/') {                            /* relative? */
7221         cptr = tptr + 1;
7222         *hi = (t_addr) strtotv (cptr, &tptr, rdx);      /* get high */
7223         if ((cptr == tptr) || (*hi == 0))
7224             return NULL;
7225         *hi = *lo + *hi - 1;
7226         }
7227     else *hi = *lo;
7228     }
7229 sim_last_addr = *hi;
7230 if (term && (*tptr++ != term))
7231     return NULL;
7232 return tptr;
7233 }
7234 
7235 /* sim_decode_quoted_string
7236 
7237    Inputs:
7238         iptr        =   pointer to input string
7239         optr        =   pointer to output buffer
7240                         the output buffer must be allocated by the caller
7241                         and to avoid overrunat it must be at least as big
7242                         as the input string.
7243 
7244    Outputs
7245         result      =   status of decode SCPE_OK when good, SCPE_ARG otherwise
7246         osize       =   size of the data in the optr buffer
7247 
7248    The input string must be quoted.  Quotes may be either single or
7249    double but the opening anc closing quote characters must match.
7250    Within quotes C style character escapes are allowed.
7251 */
7252 
7253 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
     /* [previous][next][first][last][top][bottom][index][help] */
7254 {
7255 char quote_char;
7256 uint8 *ostart = optr;
7257 
7258 *osize = 0;
7259 if ((strlen(iptr) == 1) ||
7260     (iptr[0] != iptr[strlen(iptr)-1]) ||
7261     ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7262     return SCPE_ARG;            /* String must be quote delimited */
7263 quote_char = *iptr++;           /* Save quote character */
7264 while (iptr[1]) {               /* Skip trailing quote */
7265     if (*iptr != '\\') {
7266         if (*iptr == quote_char)
7267             return SCPE_ARG;    /* Embedded quotes must be escaped */
7268         *(optr++) = (uint8)(*(iptr++));
7269         continue;
7270         }
7271     ++iptr; /* Skip backslash */
7272     switch (*iptr) {
7273         case 'r':   /* ASCII Carriage Return character (Decimal value 13) */
7274             *(optr++) = 13; ++iptr;
7275             break;
7276         case 'n':   /* ASCII Linefeed character (Decimal value 10) */
7277             *(optr++) = 10; ++iptr;
7278             break;
7279         case 'f':   /* ASCII Formfeed character (Decimal value 12) */
7280             *(optr++) = 12; ++iptr;
7281             break;
7282         case 't':   /* ASCII Horizontal Tab character (Decimal value 9) */
7283             *(optr++) = 9; ++iptr;
7284             break;
7285         case 'v':   /* ASCII Vertical Tab character (Decimal value 11) */
7286             *(optr++) = 11; ++iptr;
7287             break;
7288         case 'b':   /* ASCII Backspace character (Decimal value 8) */
7289             *(optr++) = 8; ++iptr;
7290             break;
7291         case '\\':   /* ASCII Backslash character (Decimal value 92) */
7292             *(optr++) = 92; ++iptr;
7293             break;
7294         case 'e':   /* ASCII Escape character (Decimal value 27) */
7295             *(optr++) = 27; ++iptr;
7296             break;
7297         case '\'':   /* ASCII Single Quote character (Decimal value 39) */
7298             *(optr++) = 39; ++iptr;
7299             break;
7300         case '"':   /* ASCII Double Quote character (Decimal value 34) */
7301             *(optr++) = 34; ++iptr;
7302             break;
7303         case '?':   /* ASCII Question Mark character (Decimal value 63) */
7304             *(optr++) = 63; ++iptr;
7305             break;
7306         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7307             *optr = *(iptr++) - '0';
7308             if ((*iptr >= '0') && (*iptr <= '7'))
7309                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7310             if ((*iptr >= '0') && (*iptr <= '7'))
7311                 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7312             ++optr;
7313             break;
7314         case 'x':
7315             if (1) {
7316                 static const char *hex_digits = "0123456789ABCDEF";
7317                 const char *c;
7318 
7319                 ++iptr;
7320                 *optr = 0;
7321                 c = strchr (hex_digits, toupper(*iptr));
7322                 if (c) {
7323                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7324                     ++iptr;
7325                     }
7326                 c = strchr (hex_digits, toupper(*iptr));
7327                 if (c) {
7328                     *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7329                     ++iptr;
7330                     }
7331                 ++optr;
7332                 }
7333             break;
7334         default:
7335             return SCPE_ARG;    /* Invalid escape */
7336         }
7337     }
7338 *optr = '\0';
7339 *osize = (uint32)(optr-ostart);
7340 return SCPE_OK;
7341 }
7342 
7343 /* sim_encode_quoted_string
7344 
7345    Inputs:
7346         iptr        =   pointer to input buffer
7347         size        =   number of bytes of data in the buffer
7348 
7349    Outputs
7350         optr        =   pointer to output buffer
7351                         the output buffer must be freed by the caller
7352 
7353    The input data will be encoded into a simply printable form.
7354 */
7355 
7356 char *sim_encode_quoted_string (const uint8 *iptr, uint32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
7357 {
7358 uint32 i;
7359 t_bool double_quote_found = FALSE;
7360 t_bool single_quote_found = FALSE;
7361 char quote = '"';
7362 char *tptr, *optr;
7363 
7364 optr = (char *)malloc (4*size + 3);
7365 if (optr == NULL)
7366     return NULL;
7367 tptr = optr;
7368 for (i=0; i<size; i++)
7369     switch ((char)iptr[i]) {
7370         case '"':
7371             double_quote_found = TRUE;
7372             break;
7373         case '\'':
7374             single_quote_found = TRUE;
7375             break;
7376         }
7377 if (double_quote_found && (!single_quote_found))
7378     quote = '\'';
7379 *tptr++ = quote;
7380 while (size--) {
7381     switch (*iptr) {
7382         case '\r':
7383             *tptr++ = '\\'; *tptr++ = 'r'; break;
7384         case '\n':
7385             *tptr++ = '\\'; *tptr++ = 'n'; break;
7386         case '\f':
7387             *tptr++ = '\\'; *tptr++ = 'f'; break;
7388         case '\t':
7389             *tptr++ = '\\'; *tptr++ = 't'; break;
7390         case '\v':
7391             *tptr++ = '\\'; *tptr++ = 'v'; break;
7392         case '\b':
7393             *tptr++ = '\\'; *tptr++ = 'b'; break;
7394         case '\\':
7395             *tptr++ = '\\'; *tptr++ = '\\'; break;
7396         case '"':
7397         case '\'':
7398             if (quote == *iptr)
7399                 *tptr++ = '\\';
7400         /*FALLTHRU*/ /* fall through */ /* fallthrough */
7401         default:
7402             if (sim_isprint (*iptr))
7403                 *tptr++ = *iptr;
7404             else {
7405                 sprintf (tptr, "\\%03o", *iptr);
7406                 tptr += 4;
7407                 }
7408             break;
7409         }
7410     ++iptr;
7411     }
7412 *tptr++ = quote;
7413 *tptr++ = '\0';
7414 return optr;
7415 }
7416 
7417 void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
7418 {
7419 char *string;
7420 
7421 string = sim_encode_quoted_string (buf, size);
7422 fprintf (st, "%s", string);
7423 FREE (string);
7424 }
7425 
7426 /* Find_device          find device matching input string
7427 
7428    Inputs:
7429         cptr    =       pointer to input string
7430    Outputs:
7431         result  =       pointer to device
7432 */
7433 
7434 DEVICE *find_dev (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7435 {
7436 int32 i;
7437 DEVICE *dptr;
7438 
7439 if (cptr == NULL)
7440     return NULL;
7441 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7442     if ((strcmp (cptr, dptr->name) == 0) ||
7443         (dptr->lname &&
7444         (strcmp (cptr, dptr->lname) == 0)))
7445         return dptr;
7446     }
7447 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7448     if ((strcmp (cptr, dptr->name) == 0) ||
7449         (dptr->lname &&
7450         (strcmp (cptr, dptr->lname) == 0)))
7451         return dptr;
7452     }
7453 return NULL;
7454 }
7455 
7456 /* Find_unit            find unit matching input string
7457 
7458    Inputs:
7459         cptr    =       pointer to input string
7460         uptr    =       pointer to unit pointer
7461    Outputs:
7462         result  =       pointer to device (null if no dev)
7463         *iptr   =       pointer to unit (null if nx unit)
7464 
7465 */
7466 
7467 DEVICE *find_unit (const char *cptr, UNIT **uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7468 {
7469 uint32 i, u;
7470 const char *nptr;
7471 const char *tptr;
7472 t_stat r;
7473 DEVICE *dptr;
7474 
7475 if (uptr == NULL)                                       /* arg error? */
7476     return NULL;
7477 *uptr = NULL;
7478 if ((dptr = find_dev (cptr))) {                         /* exact match? */
7479     if (qdisable (dptr))                                /* disabled? */
7480         return NULL;
7481     *uptr = dptr->units;                                /* unit 0 */
7482     return dptr;
7483     }
7484 
7485 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {     /* base + unit#? */
7486     if (qdisable (dptr))                                /* device disabled? */
7487         continue;
7488     if (dptr->numunits && /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/ /* any units? */
7489         (((nptr = dptr->name) &&
7490           (strncmp (cptr, nptr, strlen (nptr)) == 0)) || /* LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7491          ((nptr = dptr->lname) &&
7492           (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7493         tptr = cptr + strlen (nptr);
7494         if (sim_isdigit (*tptr)) {
7495             if (qdisable (dptr))                        /* disabled? */
7496                 return NULL;
7497             u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7498             if (r != SCPE_OK)                           /* error? */
7499                 *uptr = NULL;
7500             else
7501                 *uptr = dptr->units + u;
7502             return dptr;
7503             }
7504         }
7505     for (u = 0; u < dptr->numunits; u++) {
7506         if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7507             *uptr = &dptr->units[u];
7508             return dptr;
7509             }
7510         }
7511     }
7512 return NULL;
7513 }
7514 
7515 /* sim_register_internal_device   Add device to internal device list
7516 
7517    Inputs:
7518         dptr    =       pointer to device
7519 */
7520 
7521 t_stat sim_register_internal_device (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7522 {
7523 uint32 i;
7524 
7525 for (i = 0; (sim_devices[i] != NULL); i++)
7526     if (sim_devices[i] == dptr)
7527         return SCPE_OK;
7528 for (i = 0; i < sim_internal_device_count; i++)
7529     if (sim_internal_devices[i] == dptr)
7530         return SCPE_OK;
7531 ++sim_internal_device_count;
7532 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7533 if (!sim_internal_devices)
7534   {
7535     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7536              __func__, __FILE__, __LINE__);
7537 #if defined(USE_BACKTRACE)
7538 # ifdef SIGUSR2
7539     (void)raise(SIGUSR2);
7540     /*NOTREACHED*/ /* unreachable */
7541 # endif /* ifdef SIGUSR2 */
7542 #endif /* if defined(USE_BACKTRACE) */
7543     abort();
7544   }
7545 sim_internal_devices[sim_internal_device_count-1] = dptr;
7546 sim_internal_devices[sim_internal_device_count] = NULL;
7547 return SCPE_OK;
7548 }
7549 
7550 /* Find_dev_from_unit   find device for unit
7551 
7552    Inputs:
7553         uptr    =       pointer to unit
7554    Outputs:
7555         result  =       pointer to device
7556 */
7557 
7558 DEVICE *find_dev_from_unit (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7559 {
7560 DEVICE *dptr;
7561 uint32 i, j;
7562 
7563 if (uptr == NULL)
7564     return NULL;
7565 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7566     for (j = 0; j < dptr->numunits; j++) {
7567         if (uptr == (dptr->units + j))
7568             return dptr;
7569         }
7570     }
7571 for (i = 0; i<sim_internal_device_count; i++) {
7572     dptr = sim_internal_devices[i];
7573     for (j = 0; j < dptr->numunits; j++) {
7574         if (uptr == (dptr->units + j))
7575             return dptr;
7576         }
7577     }
7578 return NULL;
7579 }
7580 
7581 /* Test for disabled device */
7582 
7583 t_bool qdisable (DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7584 {
7585 return (dptr->flags & DEV_DIS? TRUE: FALSE);
7586 }
7587 
7588 /* find_reg_glob        find globally unique register
7589 
7590    Inputs:
7591         cptr    =       pointer to input string
7592         optr    =       pointer to output pointer (can be null)
7593         gdptr   =       pointer to global device
7594    Outputs:
7595         result  =       pointer to register, NULL if error
7596         *optr   =       pointer to next character in input string
7597         *gdptr  =       pointer to device where found
7598 */
7599 
7600 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7601 {
7602 int32 i;
7603 DEVICE *dptr;
7604 REG *rptr, *srptr = NULL;
7605 
7606 *gdptr = NULL;
7607 for (i = 0; (dptr = sim_devices[i]) != 0; i++) {        /* all dev */
7608     if (dptr->flags & DEV_DIS)                          /* skip disabled */
7609         continue;
7610     if ((rptr = find_reg (cptr, optr, dptr))) {         /* found? */
7611         if (srptr)                                      /* ambig? err */
7612             return NULL;
7613         srptr = rptr;                                   /* save reg */
7614         *gdptr = dptr;                                  /* save unit */
7615         }
7616     }
7617 return srptr;
7618 }
7619 
7620 /* find_reg             find register matching input string
7621 
7622    Inputs:
7623         cptr    =       pointer to input string
7624         optr    =       pointer to output pointer (can be null)
7625         dptr    =       pointer to device
7626    Outputs:
7627         result  =       pointer to register, NULL if error
7628         *optr   =       pointer to next character in input string
7629 */
7630 
7631 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7632 {
7633 CONST char *tptr;
7634 REG *rptr;
7635 size_t slnt;
7636 
7637 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
7638     return NULL;
7639 tptr = cptr;
7640 do {
7641     tptr++;
7642     } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
7643 slnt = tptr - cptr;
7644 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
7645     if ((slnt == strlen (rptr->name)) &&
7646         (strncmp (cptr, rptr->name, slnt) == 0)) {
7647         if (optr != NULL)
7648             *optr = tptr;
7649         return rptr;
7650         }
7651     }
7652 return NULL;
7653 }
7654 
7655 /* get_switches         get switches from input string
7656 
7657    Inputs:
7658         cptr    =       pointer to input string
7659    Outputs:
7660         sw      =       switch bit mask
7661                         0 if no switches, -1 if error
7662 */
7663 
7664 int32 get_switches (const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7665 {
7666 int32 sw;
7667 
7668 if (*cptr != '-')
7669     return 0;
7670 sw = 0;
7671 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
7672     if (sim_isalpha (*cptr) == 0)
7673         return -1;
7674     sw = sw | SWMASK (toupper (*cptr));
7675     }
7676 return sw;
7677 }
7678 
7679 /* get_sim_sw           accumulate sim_switches
7680 
7681    Inputs:
7682         cptr    =       pointer to input string
7683    Outputs:
7684         ptr     =       pointer to first non-string glyph
7685                         NULL if error
7686 */
7687 
7688 CONST char *get_sim_sw (CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7689 {
7690 int32 lsw;
7691 char gbuf[CBUFSIZE];
7692 
7693 while (*cptr == '-') {                                  /* while switches */
7694     cptr = get_glyph (cptr, gbuf, 0);                   /* get switch glyph */
7695     lsw = get_switches (gbuf);                          /* parse */
7696     if (lsw <= 0)                                       /* invalid? */
7697         return NULL;
7698     sim_switches = sim_switches | lsw;                  /* accumulate */
7699     }
7700 return cptr;
7701 }
7702 
7703 /* get_sim_opt          get simulator command options
7704 
7705    Inputs:
7706         opt     =       command options
7707         cptr    =       pointer to input string
7708    Outputs:
7709         ptr     =       pointer to next glyph, NULL if error
7710         *stat   =       error status
7711 */
7712 
7713 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
     /* [previous][next][first][last][top][bottom][index][help] */
7714 {
7715 int32 t;
7716 char gbuf[CBUFSIZE];
7717 CONST char *svptr;
7718 DEVICE *tdptr;
7719 UNIT *tuptr;
7720 
7721 sim_switches = 0;                                       /* no switches */
7722 sim_ofile = NULL;                                       /* no output file */
7723 sim_schrptr = NULL;                                     /* no search */
7724 sim_schaptr = NULL;                                     /* no search */
7725 sim_stabr.logic = sim_staba.logic = SCH_OR;             /* default search params */
7726 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
7727 sim_stabr.count = 1;
7728 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
7729 if (!sim_stabr.mask)
7730   {
7731     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7732              __func__, __FILE__, __LINE__);
7733 #if defined(USE_BACKTRACE)
7734 # ifdef SIGUSR2
7735     (void)raise(SIGUSR2);
7736     /*NOTREACHED*/ /* unreachable */
7737 # endif /* ifdef SIGUSR2 */
7738 #endif /* if defined(USE_BACKTRACE) */
7739     abort();
7740   }
7741 memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
7742 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
7743 if (!sim_stabr.comp)
7744   {
7745     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7746              __func__, __FILE__, __LINE__);
7747 #if defined(USE_BACKTRACE)
7748 # ifdef SIGUSR2
7749     (void)raise(SIGUSR2);
7750     /*NOTREACHED*/ /* unreachable */
7751 # endif /* ifdef SIGUSR2 */
7752 #endif /* if defined(USE_BACKTRACE) */
7753     abort();
7754   }
7755 memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
7756 sim_staba.count = sim_emax;
7757 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
7758 if (!sim_staba.mask)
7759   {
7760     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7761              __func__, __FILE__, __LINE__);
7762 #if defined(USE_BACKTRACE)
7763 # ifdef SIGUSR2
7764     (void)raise(SIGUSR2);
7765     /*NOTREACHED*/ /* unreachable */
7766 # endif /* ifdef SIGUSR2 */
7767 #endif /* if defined(USE_BACKTRACE) */
7768     abort();
7769   }
7770 memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
7771 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
7772 if (!sim_staba.comp)
7773   {
7774     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
7775              __func__, __FILE__, __LINE__);
7776 #if defined(USE_BACKTRACE)
7777 # ifdef SIGUSR2
7778     (void)raise(SIGUSR2);
7779     /*NOTREACHED*/ /* unreachable */
7780 # endif /* ifdef SIGUSR2 */
7781 #endif /* if defined(USE_BACKTRACE) */
7782     abort();
7783   }
7784 memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
7785 if (! sim_dflt_dev)
7786   return NULL;
7787 sim_dfdev = sim_dflt_dev;
7788 sim_dfunit = sim_dfdev->units;
7789 sim_opt_out = 0;                                        /* no options yet */
7790 *st = SCPE_OK;
7791 while (*cptr) {                                         /* loop through modifiers */
7792     svptr = cptr;                                       /* save current position */
7793     if ((opt & CMD_OPT_OF) && (*cptr == '@')) {         /* output file spec? */
7794         if (sim_ofile) {                                /* already got one? */
7795             fclose (sim_ofile);                         /* one per customer */
7796             *st = SCPE_ARG;
7797             return NULL;
7798             }
7799         cptr = get_glyph (cptr + 1, gbuf, 0);
7800         sim_ofile = sim_fopen (gbuf, "a");              /* open for append */
7801         if (sim_ofile == NULL) {                        /* open failed? */
7802             *st = SCPE_OPENERR;
7803             return NULL;
7804             }
7805         sim_opt_out |= CMD_OPT_OF;                      /* got output file */
7806         continue;
7807         }
7808     cptr = get_glyph (cptr, gbuf, 0);
7809     if ((t = get_switches (gbuf)) != 0) {               /* try for switches */
7810         if (t < 0) {                                    /* err if bad switch */
7811             *st = SCPE_INVSW;
7812             return NULL;
7813             }
7814         sim_switches = sim_switches | t;                /* or in new switches */
7815         }
7816     else if ((opt & CMD_OPT_SCH) &&                     /* if allowed, */
7817         get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
7818         sim_schrptr = &sim_stabr;                       /* set search */
7819         sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
7820         sim_opt_out |= CMD_OPT_SCH;                     /* got search */
7821         }
7822     else if ((opt & CMD_OPT_DFT) &&                     /* default allowed? */
7823         ((sim_opt_out & CMD_OPT_DFT) == 0) &&           /* none yet? */
7824         (tdptr = find_unit (gbuf, &tuptr)) &&           /* try for default */
7825         (tuptr != NULL)) {
7826         sim_dfdev = tdptr;                              /* set as default */
7827         sim_dfunit = tuptr;
7828         sim_opt_out |= CMD_OPT_DFT;                     /* got default */
7829         }
7830     else return svptr;                                  /* not rec, break out */
7831     }
7832 return cptr;
7833 }
7834 
7835 /* put_switches         put switches into string
7836 
7837    Inputs:
7838         buf     =       pointer to string buffer
7839         bufsize =       size of string buffer
7840         sw      =       switch bit mask
7841    Outputs:
7842         buf     =       buffer with switches converted to text
7843 */
7844 
7845 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
7846 {
7847 char *optr = buf;
7848 int32 bit;
7849 
7850 memset (buf, 0, bufsize);
7851 if ((sw == 0) || (bufsize < 3))
7852     return buf;
7853 --bufsize;                          /* leave room for terminating NUL */
7854 *optr++ = '-';
7855 for (bit=0; bit <= ('Z'-'A'); bit++)
7856     if (sw & (1 << bit))
7857         if ((size_t)(optr - buf) < bufsize)
7858             *optr++ = 'A' + bit;
7859 return buf;
7860 }
7861 
7862 /* Get register search specification
7863 
7864    Inputs:
7865         cptr    =       pointer to input string
7866         radix   =       radix for numbers
7867         schptr =        pointer to search table
7868    Outputs:
7869         return =        NULL if error
7870                         schptr if valid search specification
7871 */
7872 
7873 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7874 {
7875 int32 c, logop, cmpop;
7876 t_value logval, cmpval;
7877 const char *sptr;
7878 CONST char *tptr;
7879 const char logstr[] = "|&^", cmpstr[] = "=!><";
7880 
7881 logval = cmpval = 0;
7882 if (*cptr == 0)                                         /* check for clause */
7883     return NULL;
7884 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7885 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7886     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7887         logop = (int32)(sptr - logstr);
7888         logval = strtotv (cptr, &tptr, radix);
7889         if (cptr == tptr)
7890             return NULL;
7891         cptr = tptr;
7892         }
7893     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7894         cmpop = (int32)(sptr - cmpstr);
7895         if (*cptr == '=') {
7896             cmpop = cmpop + strlen (cmpstr);
7897             cptr++;
7898             }
7899         cmpval = strtotv (cptr, &tptr, radix);
7900         if (cptr == tptr)
7901             return NULL;
7902         cptr = tptr;
7903         }
7904     else return NULL;
7905     }                                                   /* end for */
7906 if (schptr->count != 1) {
7907     FREE (schptr->mask);
7908     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7909     FREE (schptr->comp);
7910     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7911     }
7912 if (logop >= 0) {
7913     schptr->logic = logop;
7914     schptr->mask[0] = logval;
7915     }
7916 if (cmpop >= 0) {
7917     schptr->boolop = cmpop;
7918     schptr->comp[0] = cmpval;
7919     }
7920 schptr->count = 1;
7921 return schptr;
7922 }
7923 
7924 /* Get memory search specification
7925 
7926    Inputs:
7927         cptr    =       pointer to input string
7928         radix   =       radix for numbers
7929         schptr =        pointer to search table
7930    Outputs:
7931         return =        NULL if error
7932                         schptr if valid search specification
7933 */
7934 
7935 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
7936 {
7937 int32 c, logop, cmpop;
7938 t_value *logval, *cmpval;
7939 t_stat reason = SCPE_OK;
7940 CONST char *ocptr = cptr;
7941 const char *sptr;
7942 char gbuf[CBUFSIZE];
7943 const char logstr[] = "|&^", cmpstr[] = "=!><";
7944 
7945 if (*cptr == 0)                                         /* check for clause */
7946     return NULL;
7947 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
7948 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
7949 /*LINTED E_EQUALITY_NOT_ASSIGNMENT*/
7950 for (logop = cmpop = -1; (c = *cptr++); ) {             /* loop thru clauses */
7951     if ((sptr = strchr (logstr, c))) {                  /* check for mask */
7952         logop = (int32)(sptr - logstr);
7953         cptr = get_glyph (cptr, gbuf, 0);
7954         reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
7955         if (reason > 0) {
7956             FREE (logval);
7957             FREE (cmpval);
7958             return get_rsearch (ocptr, radix, schptr);
7959             }
7960         }
7961     else if ((sptr = strchr (cmpstr, c))) {             /* check for boolop */
7962         cmpop = (int32)(sptr - cmpstr);
7963         if (*cptr == '=') {
7964             cmpop = cmpop + strlen (cmpstr);
7965             cptr++;
7966             }
7967         cptr = get_glyph (cptr, gbuf, 0);
7968         reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
7969         if (reason > 0) {
7970             FREE (logval);
7971             FREE (cmpval);
7972             return get_rsearch (ocptr, radix, schptr);
7973             }
7974         }
7975     else {
7976         FREE (logval);
7977         FREE (cmpval);
7978         return NULL;
7979         }
7980     }                                                   /* end for */
7981 if (schptr->count != (uint32)(1 - reason)) {
7982     schptr->count = 1 - reason;
7983     FREE (schptr->mask);
7984     schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7985     FREE (schptr->comp);
7986     schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7987     }
7988 if (logop >= 0) {
7989     schptr->logic = logop;
7990     FREE (schptr->mask);
7991     schptr->mask = logval;
7992     }
7993 else {
7994     FREE (logval);
7995     }
7996 if (cmpop >= 0) {
7997     schptr->boolop = cmpop;
7998     FREE (schptr->comp);
7999     schptr->comp = cmpval;
8000     }
8001 else {
8002     FREE (cmpval);
8003     }
8004 return schptr;
8005 }
8006 
8007 /* Test value against search specification
8008 
8009    Inputs:
8010         val    =        value list to test
8011         schptr =        pointer to search table
8012    Outputs:
8013         return =        1 if value passes search criteria, 0 if not
8014 */
8015 
8016 int32 test_search (t_value *values, SCHTAB *schptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8017 {
8018 t_value *val = NULL;
8019 int32 i, updown;
8020 int32 ret = 0;
8021 
8022 if (schptr == NULL)
8023     return ret;
8024 
8025 val = (t_value *)malloc (schptr->count * sizeof (*values));
8026 if (!val)
8027   {
8028     fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8029             __func__, __FILE__, __LINE__);
8030 #if defined(USE_BACKTRACE)
8031 # ifdef SIGUSR2
8032     (void)raise(SIGUSR2);
8033     /*NOTREACHED*/ /* unreachable */
8034 # endif /* ifdef SIGUSR2 */
8035 #endif /* if defined(USE_BACKTRACE) */
8036     abort();
8037   }
8038 for (i=0; i<(int32)schptr->count; i++) {
8039     val[i] = values[i];
8040     switch (schptr->logic) {                            /* case on logical */
8041 
8042         case SCH_OR:
8043             val[i] = val[i] | schptr->mask[i];
8044             break;
8045 
8046         case SCH_AND:
8047             val[i] = val[i] & schptr->mask[i];
8048             break;
8049 
8050         case SCH_XOR:
8051             val[i] = val[i] ^ schptr->mask[i];
8052             break;
8053             }
8054     }
8055 
8056 ret = 1;
8057 if (1) {    /* Little Endian VM */
8058     updown = -1;
8059     i=schptr->count-1;
8060     }
8061 else {      /* Big Endian VM */
8062     updown = 1;
8063     i=0;
8064     }
8065 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
8066     switch (schptr->boolop) {                           /* case on comparison */
8067 
8068         case SCH_E: case SCH_EE:
8069             if (val[i] != schptr->comp[i])
8070                 ret = 0;
8071             break;
8072 
8073         case SCH_N: case SCH_NE:
8074             if (val[i] == schptr->comp[i])
8075                 ret = 0;
8076             break;
8077 
8078         case SCH_G:
8079             if (val[i] <= schptr->comp[i])
8080                 ret = 0;
8081             break;
8082 
8083         case SCH_GE:
8084             if (val[i] < schptr->comp[i])
8085                 ret = 0;
8086             break;
8087 
8088         case SCH_L:
8089             if (val[i] >= schptr->comp[i])
8090                 ret = 0;
8091             break;
8092 
8093         case SCH_LE:
8094             if (val[i] > schptr->comp[i])
8095                 ret = 0;
8096             break;
8097         }
8098     }
8099 FREE (val);
8100 return ret;
8101 }
8102 
8103 /* Radix independent input/output package
8104 
8105    strtotv - general radix input routine
8106 
8107    Inputs:
8108         inptr   =       string to convert
8109         endptr  =       pointer to first unconverted character
8110         radix   =       radix for input
8111    Outputs:
8112         value   =       converted value
8113 
8114    On an error, the endptr will equal the inptr.
8115 */
8116 
8117 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
     /* [previous][next][first][last][top][bottom][index][help] */
8118 {
8119 int32 nodigit;
8120 t_value val;
8121 uint32 c, digit;
8122 
8123 *endptr = inptr;                                        /* assume fails */
8124 if ((radix < 2) || (radix > 36))
8125     return 0;
8126 while (sim_isspace (*inptr))                            /* bypass white space */
8127     inptr++;
8128 val = 0;
8129 nodigit = 1;
8130 for (c = *inptr; sim_isalnum(c); c = *++inptr) {        /* loop through char */
8131     if (sim_islower (c))
8132         c = toupper (c);
8133     if (sim_isdigit (c))                                /* digit? */
8134         digit = c - (uint32) '0';
8135     else if (radix <= 10)                               /* stop if not expected */
8136         break;
8137     else digit = c + 10 - (uint32) 'A';                 /* convert letter */
8138     if (digit >= radix)                                 /* valid in radix? */
8139         return 0;
8140     val = (val * radix) + digit;                        /* add to value */
8141     nodigit = 0;
8142     }
8143 if (nodigit)                                            /* no digits? */
8144     return 0;
8145 *endptr = inptr;                                        /* result pointer */
8146 return val;
8147 }
8148 
8149 /* fprint_val - general radix printing routine
8150 
8151    Inputs:
8152         stream  =       stream designator
8153         val     =       value to print
8154         radix   =       radix to print
8155         width   =       width to print
8156         format  =       leading zeroes format
8157    Outputs:
8158         status  =       error status
8159         if stream is NULL, returns length of output that would
8160         have been generated.
8161 */
8162 
8163 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8164     uint32 width, uint32 format)
8165 {
8166 #define MAX_WIDTH ((int) ((CHAR_BIT * sizeof (t_value) * 4 + 3)/3))
8167 t_value owtest, wtest;
8168 int32 d, digit, ndigits, commas = 0;
8169 char dbuf[MAX_WIDTH + 1];
8170 
8171 for (d = 0; d < MAX_WIDTH; d++)
8172     dbuf[d] = (format == PV_RZRO)? '0': ' ';
8173 dbuf[MAX_WIDTH] = 0;
8174 d = MAX_WIDTH;
8175 do {
8176     d = d - 1;
8177     digit = (int32) (val % radix);
8178     val = val / radix;
8179     dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8180     } while ((d > 0) && (val != 0));
8181 
8182 switch (format) {
8183     case PV_LEFT:
8184         break;
8185     case PV_RCOMMA:
8186         for (digit = 0; digit < MAX_WIDTH; digit++)
8187             if (dbuf[digit] != ' ')
8188                 break;
8189         ndigits = MAX_WIDTH - digit;
8190         commas = (ndigits - 1)/3;
8191         for (digit=0; digit<ndigits-3; digit++)
8192             dbuf[MAX_WIDTH + (digit - ndigits) - (ndigits - digit - 1)/3] = dbuf[MAX_WIDTH + (digit - ndigits)];
8193         for (digit=1; digit<=commas; digit++)
8194             dbuf[MAX_WIDTH - (digit * 4)] = ',';
8195         d = d - commas;
8196         if (width > MAX_WIDTH) {
8197             if (!buffer)
8198                 return width;
8199             sprintf (buffer, "%*s", -((int)width), dbuf);
8200             return SCPE_OK;
8201             }
8202         else
8203             if (width > 0)
8204                 d = MAX_WIDTH - width;
8205         break;
8206     case PV_RZRO:
8207     case PV_RSPC:
8208         wtest = owtest = radix;
8209         ndigits = 1;
8210         while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8211             owtest = wtest;
8212             wtest = wtest * radix;
8213             ndigits = ndigits + 1;
8214             }
8215         if ((MAX_WIDTH - (ndigits + commas)) < d)
8216             d = MAX_WIDTH - (ndigits + commas);
8217         break;
8218     }
8219 if (!buffer)
8220     return strlen(dbuf+d);
8221 *buffer = '\0';
8222 if (width < strlen(dbuf+d))
8223     return SCPE_IOERR;
8224 strcpy(buffer, dbuf+d);
8225 return SCPE_OK;
8226 }
8227 
8228 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
     /* [previous][next][first][last][top][bottom][index][help] */
8229     uint32 width, uint32 format)
8230 {
8231 char dbuf[MAX_WIDTH + 1];
8232 
8233 if (!stream)
8234     return sprint_val (NULL, val, radix, width, format);
8235 if (width > MAX_WIDTH)
8236     width = MAX_WIDTH;
8237 sprint_val (dbuf, val, radix, width, format);
8238 if (Fprintf (stream, "%s", dbuf) < 0)
8239     return SCPE_IOERR;
8240 return SCPE_OK;
8241 }
8242 
8243 const char *sim_fmt_secs (double seconds)
     /* [previous][next][first][last][top][bottom][index][help] */
8244 {
8245 static char buf[60];
8246 char frac[16] = "";
8247 const char *sign = "";
8248 double val = seconds;
8249 double days, hours, mins, secs, msecs, usecs;
8250 
8251 if (val == 0.0)
8252     return "";
8253 if (val < 0.0) {
8254     sign = "-";
8255     val = -val;
8256     }
8257 days = floor (val / (24.0*60.0*60.0));
8258 val -= (days * 24.0*60.0*60.0);
8259 hours = floor (val / (60.0*60.0));
8260 val -= (hours * 60.0 * 60.0);
8261 mins = floor (val / 60.0);
8262 val -= (mins * 60.0);
8263 secs = floor (val);
8264 val -= secs;
8265 val *= 1000.0;
8266 msecs = floor (val);
8267 val -= msecs;
8268 val *= 1000.0;
8269 usecs = floor (val+0.5);
8270 if (usecs == 1000.0) {
8271     usecs = 0.0;
8272     msecs += 1;
8273     }
8274 if ((msecs > 0.0) || (usecs > 0.0)) {
8275     sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8276     while (frac[strlen (frac) - 1] == '0') //-V557
8277         frac[strlen (frac) - 1] = '\0'; //-V557
8278     if (strlen (frac) == 1)
8279         frac[0] = '\0';
8280     }
8281 if (days > 0)
8282     sprintf (buf, "%s%.0f day%s %02.0f:%02.0f:%02.0f%s hour%s", sign, days, (days != 1)? "s" : "", hours, mins, secs, frac, (days == 1) ? "s" : "");
8283 else
8284     if (hours > 0)
8285         sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour", sign, hours, mins, secs, frac);
8286     else
8287         if (mins > 0)
8288             sprintf (buf, "%s%.0f:%02.0f%s minute", sign, mins, secs, frac);
8289         else
8290             if (secs > 0)
8291                 sprintf (buf, "%s%.0f%s second", sign, secs, frac);
8292             else
8293                 if (msecs > 0) {
8294                     if (usecs > 0)
8295                         sprintf (buf, "%s%.0f.%s msec", sign, msecs, frac+4);
8296                     else
8297                         sprintf (buf, "%s%.0f msec", sign, msecs);
8298                     }
8299                 else
8300                     sprintf (buf, "%s%.0f usec", sign, usecs);
8301 if (0 != strncmp ("1 ", buf, 2))
8302     strcpy (&buf[strlen (buf)], "s");
8303 return buf;
8304 }
8305 
8306 /*
8307  * Event queue package
8308  *
8309  *      sim_activate            add entry to event queue
8310  *      sim_activate_abs        add entry to event queue even if event already scheduled
8311  *      sim_activate_after      add entry to event queue after a specified amount of wall time
8312  *      sim_cancel              remove entry from event queue
8313  *      sim_process_event       process entries on event queue
8314  *      sim_is_active           see if entry is on event queue
8315  *      sim_activate_time       return time until activation
8316  *      sim_atime               return absolute time for an entry
8317  *      sim_gtime               return global time
8318  *      sim_qcount              return event queue entry count
8319  */
8320 
8321 t_stat sim_process_event (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8322 {
8323 UNIT *uptr;
8324 t_stat reason;
8325 
8326 if (stop_cpu)                                           /* stop CPU? */
8327   {
8328 #ifdef WIN_STDIO
8329     (void)fflush(stdout);
8330     (void)fflush(stderr);
8331 #endif /* ifdef WIN_STDIO */
8332     return SCPE_STOP;
8333   }
8334 UPDATE_SIM_TIME;                                        /* update sim time */
8335 
8336 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8337     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8338     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Empty New Interval = %d\n", sim_interval);
8339     return SCPE_OK;
8340     }
8341 sim_processing_event = TRUE;
8342 do {
8343     uptr = sim_clock_queue;                             /* get first */
8344     sim_clock_queue = uptr->next;                       /* remove first */
8345     uptr->next = NULL;                                  /* hygiene */
8346     sim_interval -= uptr->time;
8347     uptr->time = 0;
8348     if (sim_clock_queue != QUEUE_LIST_END)
8349         sim_interval = sim_clock_queue->time;
8350     else
8351         sim_interval = noqueue_time = NOQUEUE_WAIT;
8352     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr));
8353     if (uptr->action != NULL)
8354         reason = uptr->action (uptr);
8355     else
8356         reason = SCPE_OK;
8357     } while ((reason == SCPE_OK) &&
8358              (sim_interval <= 0) &&
8359              (sim_clock_queue != QUEUE_LIST_END) &&
8360              (!stop_cpu));
8361 
8362 if (sim_clock_queue == QUEUE_LIST_END) {                /* queue empty? */
8363     sim_interval = noqueue_time = NOQUEUE_WAIT;         /* flag queue empty */
8364     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval);
8365     }
8366 else
8367     sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue));
8368 
8369 if ((reason == SCPE_OK) && stop_cpu)
8370   {
8371 #ifdef WIN_STDIO
8372     (void)fflush(stdout);
8373     (void)fflush(stderr);
8374 #endif /* ifdef WIN_STDIO */
8375     reason = SCPE_STOP;
8376   }
8377 sim_processing_event = FALSE;
8378 return reason;
8379 }
8380 
8381 /* sim_activate - activate (queue) event
8382 
8383    Inputs:
8384         uptr    =       pointer to unit
8385         event_time =    relative timeout
8386    Outputs:
8387         reason  =       result (SCPE_OK if ok)
8388 */
8389 
8390 t_stat sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8391 {
8392 if (uptr->dynflags & UNIT_TMR_UNIT)
8393     return sim_timer_activate (uptr, event_time);
8394 return _sim_activate (uptr, event_time);
8395 }
8396 
8397 t_stat _sim_activate (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8398 {
8399 UNIT *cptr, *prvptr;
8400 int32 accum;
8401 
8402 if (sim_is_active (uptr))                               /* already active? */
8403     return SCPE_OK;
8404 UPDATE_SIM_TIME;                                        /* update sim time */
8405 
8406 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8407 
8408 prvptr = NULL;
8409 accum = 0;
8410 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8411     if (event_time < (accum + cptr->time))
8412         break;
8413     accum = accum + cptr->time;
8414     prvptr = cptr;
8415     }
8416 if (prvptr == NULL) {                                   /* insert at head */
8417     cptr = uptr->next = sim_clock_queue;
8418     sim_clock_queue = uptr;
8419     }
8420 else {
8421     cptr = uptr->next = prvptr->next;                   /* insert at prvptr */
8422     prvptr->next = uptr;
8423     }
8424 uptr->time = event_time - accum;
8425 if (cptr != QUEUE_LIST_END)
8426     cptr->time = cptr->time - uptr->time;
8427 sim_interval = sim_clock_queue->time;
8428 return SCPE_OK;
8429 }
8430 
8431 /* sim_activate_abs - activate (queue) event even if event already scheduled
8432 
8433    Inputs:
8434         uptr    =       pointer to unit
8435         event_time =    relative timeout
8436    Outputs:
8437         reason  =       result (SCPE_OK if ok)
8438 */
8439 
8440 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
     /* [previous][next][first][last][top][bottom][index][help] */
8441 {
8442 sim_cancel (uptr);
8443 return _sim_activate (uptr, event_time);
8444 }
8445 
8446 /* sim_activate_after - activate (queue) event
8447 
8448    Inputs:
8449         uptr    =       pointer to unit
8450         usec_delay =    relative timeout (in microseconds)
8451    Outputs:
8452         reason  =       result (SCPE_OK if ok)
8453 */
8454 
8455 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8456 {
8457 return _sim_activate_after (uptr, usec_delay);
8458 }
8459 
8460 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
8461 {
8462 if (sim_is_active (uptr))                               /* already active? */
8463     return SCPE_OK;
8464 return sim_timer_activate_after (uptr, usec_delay);
8465 }
8466 
8467 /* sim_cancel - cancel (dequeue) event
8468 
8469    Inputs:
8470         uptr    =       pointer to unit
8471    Outputs:
8472         reason  =       result (SCPE_OK if ok)
8473 
8474 */
8475 
8476 t_stat sim_cancel (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8477 {
8478 UNIT *cptr, *nptr;
8479 
8480 if (sim_clock_queue == QUEUE_LIST_END)
8481     return SCPE_OK;
8482 if (!sim_is_active (uptr))
8483     return SCPE_OK;
8484 UPDATE_SIM_TIME;                                        /* update sim time */
8485 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8486 nptr = QUEUE_LIST_END;
8487 
8488 if (sim_clock_queue == uptr) {
8489     nptr = sim_clock_queue = uptr->next;
8490     uptr->next = NULL;                                  /* hygiene */
8491     }
8492 else {
8493     for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8494         if (cptr->next == uptr) {
8495             nptr = cptr->next = uptr->next;
8496             uptr->next = NULL;                          /* hygiene */
8497             break;                                      /* end queue scan */
8498             }
8499         }
8500     }
8501 if (nptr != QUEUE_LIST_END)
8502     nptr->time += (uptr->next) ? 0 : uptr->time;
8503 if (!uptr->next)
8504     uptr->time = 0;
8505 if (sim_clock_queue != QUEUE_LIST_END)
8506     sim_interval = sim_clock_queue->time;
8507 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8508 if (uptr->next) {
8509     sim_printf ("\rCancel failed for '%s'!\r\n", sim_uname(uptr));
8510     if (sim_deb)
8511         fclose(sim_deb);
8512     fprintf (stderr, "\rFATAL: Bugcheck! Aborting at %s[%s:%d]\r\n",
8513              __func__, __FILE__, __LINE__);
8514     abort ();
8515     }
8516 return SCPE_OK;
8517 }
8518 
8519 /* sim_is_active - test for entry in queue
8520 
8521    Inputs:
8522         uptr    =       pointer to unit
8523    Outputs:
8524         result =        TRUE if unit is busy, FALSE inactive
8525 */
8526 
8527 t_bool sim_is_active (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8528 {
8529 if (uptr->next == NULL)
8530   return FALSE;
8531 else
8532 return TRUE;
8533 }
8534 
8535 /* sim_activate_time - return activation time
8536 
8537    Inputs:
8538         uptr    =       pointer to unit
8539    Outputs:
8540         result =        absolute activation time + 1, 0 if inactive
8541 */
8542 
8543 int32 sim_activate_time (UNIT *uptr)
     /* [previous][next][first][last][top][bottom][index][help] */
8544 {
8545 UNIT *cptr;
8546 int32 accum = 0;
8547 
8548 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8549     if (cptr == sim_clock_queue) {
8550         if (sim_interval > 0)
8551             accum = accum + sim_interval;
8552         }
8553     else
8554         accum = accum + cptr->time;
8555     if (cptr == uptr)
8556         return accum + 1;
8557     }
8558 return 0;
8559 }
8560 
8561 /* sim_gtime - return global time
8562 
8563    Inputs: none
8564    Outputs:
8565         time    =       global time
8566 */
8567 
8568 double sim_gtime (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8569 {
8570 return sim_time;
8571 }
8572 
8573 /* sim_qcount - return queue entry count
8574 
8575    Inputs: none
8576    Outputs:
8577         count   =       number of entries on the queue
8578 */
8579 
8580 int32 sim_qcount (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8581 {
8582 int32 cnt;
8583 UNIT *uptr;
8584 
8585 cnt = 0;
8586 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
8587     cnt++;
8588 return cnt;
8589 }
8590 
8591 /* Breakpoint package.  This module replaces the VM-implemented one
8592    instruction breakpoint capability.
8593 
8594    Breakpoints are stored in table sim_brk_tab, which is ordered by address for
8595    efficient binary searching.  A breakpoint consists of a six entry structure:
8596 
8597         addr                    address of the breakpoint
8598         type                    types of breakpoints set on the address
8599                                 a bit mask representing letters A-Z
8600         cnt                     number of iterations before breakp is taken
8601         action                  pointer command string to be executed
8602                                 when break is taken
8603         next                    list of other breakpoints with the same addr specifier
8604         time_fired              array of when this breakpoint was fired for each class
8605 
8606    sim_brk_summ is a summary of the types of breakpoints that are currently set (it
8607    is the bitwise OR of all the type fields).  A simulator need only check for
8608    a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
8609 
8610    The package contains the following public routines:
8611 
8612         sim_brk_init            initialize
8613         sim_brk_set             set breakpoint
8614         sim_brk_clr             clear breakpoint
8615         sim_brk_clrall          clear all breakpoints
8616         sim_brk_show            show breakpoint
8617         sim_brk_showall         show all breakpoints
8618         sim_brk_test            test for breakpoint
8619         sim_brk_npc             PC has been changed
8620         sim_brk_getact          get next action
8621         sim_brk_clract          clear pending actions
8622 
8623    Initialize breakpoint system.
8624 */
8625 
8626 t_stat sim_brk_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
8627 {
8628 int32 i;
8629 
8630 for (i=0; i<sim_brk_lnt; i++) {
8631     BRKTAB *bp;
8632     if (sim_brk_tab)
8633       {
8634         bp = sim_brk_tab[i];
8635 
8636         while (bp)
8637           {
8638             BRKTAB *bpt = bp->next;
8639 
8640             FREE (bp->act);
8641             FREE (bp);
8642             bp = bpt;
8643           }
8644       }
8645 }
8646 if (sim_brk_tab != NULL)
8647     memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8648 sim_brk_lnt = SIM_BRK_INILNT;
8649 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
8650 if (sim_brk_tab == NULL)
8651     return SCPE_MEM;
8652 memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8653 sim_brk_ent = sim_brk_ins = 0;
8654 sim_brk_clract ();
8655 sim_brk_npc (0);
8656 return SCPE_OK;
8657 }
8658 
8659 /* Search for a breakpoint in the sorted breakpoint table */
8660 
8661 BRKTAB *sim_brk_fnd (t_addr loc)
     /* [previous][next][first][last][top][bottom][index][help] */
8662 {
8663 int32 lo, hi, p;
8664 BRKTAB *bp;
8665 
8666 if (sim_brk_ent == 0) {                                 /* table empty? */
8667     sim_brk_ins = 0;                                    /* insrt at head */
8668     return NULL;                                        /* sch fails */
8669     }
8670 lo = 0;                                                 /* initial bounds */
8671 hi = sim_brk_ent - 1;
8672 do {
8673     p = (lo + hi) >> 1;                                 /* probe */
8674     bp = sim_brk_tab[p];                                /* table addr */
8675     if (loc == bp->addr) {                              /* match? */
8676         sim_brk_ins = p;
8677         return bp;
8678         }
8679     else if (loc < bp->addr)                            /* go down? p is upper */
8680         hi = p - 1;
8681     else lo = p + 1;                                    /* go up? p is lower */
8682     } while (lo <= hi);
8683 if (loc < bp->addr)                                     /* insrt before or */
8684     sim_brk_ins = p;
8685 else sim_brk_ins = p + 1;                               /* after last sch */
8686 return NULL;
8687 }
8688 
8689 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
     /* [previous][next][first][last][top][bottom][index][help] */
8690 {
8691 BRKTAB *bp = sim_brk_fnd (loc);
8692 
8693 while (bp) {
8694     if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
8695                   (bp->typ == btyp))
8696         return bp;
8697     bp = bp->next;
8698     }
8699 return bp;
8700 }
8701 
8702 /* Insert a breakpoint */
8703 
8704 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
8705 {
8706 int32 i, t;
8707 BRKTAB *bp, **newp;
8708 
8709 if (sim_brk_ins < 0)
8710     return NULL;
8711 if (sim_brk_ent >= sim_brk_lnt) {                       /* out of space? */
8712     t = sim_brk_lnt + SIM_BRK_INILNT;                   /* new size */
8713     newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*));    /* new table */
8714     if (newp == NULL)                                   /* can't extend */
8715         return NULL;
8716     memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
8717     memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
8718     FREE (sim_brk_tab);                                 /* free old table */
8719     sim_brk_tab = newp;                                 /* new base, lnt */
8720     sim_brk_lnt = t;
8721     }
8722 if ((sim_brk_ins == sim_brk_ent) ||
8723     ((sim_brk_ins != sim_brk_ent) && //-V728
8724      (sim_brk_tab[sim_brk_ins]->addr != loc))) {        /* need to open a hole? */
8725     for (i = sim_brk_ent; i > sim_brk_ins; --i)
8726         sim_brk_tab[i] = sim_brk_tab[i - 1];
8727     sim_brk_tab[sim_brk_ins] = NULL;
8728     }
8729 bp = (BRKTAB *)calloc (1, sizeof (*bp));
8730 if (!bp)
8731   {
8732     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
8733              __func__, __FILE__, __LINE__);
8734 #if defined(USE_BACKTRACE)
8735 # ifdef SIGUSR2
8736     (void)raise(SIGUSR2);
8737     /*NOTREACHED*/ /* unreachable */
8738 # endif /* ifdef SIGUSR2 */
8739 #endif /* if defined(USE_BACKTRACE) */
8740     abort();
8741   }
8742 bp->next = sim_brk_tab[sim_brk_ins];
8743 sim_brk_tab[sim_brk_ins] = bp;
8744 if (bp->next == NULL)
8745     sim_brk_ent += 1;
8746 bp->addr = loc;
8747 bp->typ = btyp;
8748 bp->cnt = 0;
8749 bp->act = NULL;
8750 for (i = 0; i < SIM_BKPT_N_SPC; i++)
8751     bp->time_fired[i] = -1.0;
8752 return bp;
8753 }
8754 
8755 /* Set a breakpoint of type sw */
8756 
8757 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
     /* [previous][next][first][last][top][bottom][index][help] */
8758 {
8759 BRKTAB *bp;
8760 
8761 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
8762     sw |= sim_brk_dflt;
8763 if (~sim_brk_types & sw) {
8764     char gbuf[CBUFSIZE];
8765 
8766     return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
8767     }
8768 if ((sw & BRK_TYP_DYN_ALL) && act)                      /* can't specify an action with a dynamic breakpoint */
8769     return SCPE_ARG;
8770 bp = sim_brk_fnd (loc);                                 /* loc present? */
8771 if (!bp)                                                /* no, allocate */
8772     bp = sim_brk_new (loc, sw);
8773 else {
8774     while (bp && (bp->typ != (uint32)sw))
8775         bp = bp->next;
8776     if (!bp)
8777         bp = sim_brk_new (loc, sw);
8778     }
8779 if (!bp)                                                /* still no? mem err */
8780     return SCPE_MEM;
8781 bp->cnt = ncnt;                                         /* set count */
8782 if ((!(sw & BRK_TYP_DYN_ALL)) &&                        /* Not Dynamic and */
8783     (bp->act != NULL) && (act != NULL)) {               /* replace old action? */
8784     FREE (bp->act);                                     /* deallocate */
8785     bp->act = NULL;                                     /* now no action */
8786     }
8787 if ((act != NULL) && (*act != 0)) {                     /* new action? */
8788     char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
8789     if (newp == NULL)                                   /* mem err? */
8790         return SCPE_MEM;
8791     strncpy (newp, act, CBUFSIZE);                      /* copy action */
8792     bp->act = newp;                                     /* set pointer */
8793     }
8794 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
8795 return SCPE_OK;
8796 }
8797 
8798 /* Clear a breakpoint */
8799 
8800 t_stat sim_brk_clr (t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8801 {
8802 BRKTAB *bpl = NULL;
8803 BRKTAB *bp = sim_brk_fnd (loc);
8804 int32 i;
8805 
8806 if (!bp)                                                /* not there? ok */
8807     return SCPE_OK;
8808 if (sw == 0)
8809     sw = SIM_BRK_ALLTYP;
8810 
8811 #ifndef __clang_analyzer__
8812 while (bp) {
8813     if (bp->typ == (bp->typ & sw)) {
8814         FREE (bp->act);                                 /* deallocate action */
8815         if (bp == sim_brk_tab[sim_brk_ins])
8816             bpl = sim_brk_tab[sim_brk_ins] = bp->next;
8817         else
8818           {
8819             if (bpl)
8820               bpl->next = bp->next;
8821           }
8822         FREE (bp);
8823         bp = bpl;
8824         }
8825     else {
8826         bpl = bp;
8827         bp = bp->next;
8828         }
8829     }
8830 #endif /* ifndef __clang_analyzer__ */
8831 if (sim_brk_tab[sim_brk_ins] == NULL) {                 /* erased entry */
8832     sim_brk_ent = sim_brk_ent - 1;                      /* decrement count */
8833     for (i = sim_brk_ins; i < sim_brk_ent; i++)         /* shuffle remaining entries */
8834         sim_brk_tab[i] = sim_brk_tab[i+1];
8835     }
8836 sim_brk_summ = 0;                                       /* recalc summary */
8837 for (i = 0; i < sim_brk_ent; i++) {
8838     bp = sim_brk_tab[i];
8839     while (bp) {
8840         sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
8841         bp = bp->next;
8842         }
8843     }
8844 return SCPE_OK;
8845 }
8846 
8847 /* Clear all breakpoints */
8848 
8849 t_stat sim_brk_clrall (int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8850 {
8851 int32 i;
8852 
8853 if (sw == 0)
8854     sw = SIM_BRK_ALLTYP;
8855 for (i = 0; i < sim_brk_ent;) {
8856     t_addr loc = sim_brk_tab[i]->addr;
8857     sim_brk_clr (loc, sw);
8858     if ((i < sim_brk_ent) &&
8859         (loc == sim_brk_tab[i]->addr))
8860         ++i;
8861     }
8862 return SCPE_OK;
8863 }
8864 
8865 /* Show a breakpoint */
8866 
8867 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8868 {
8869 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
8870 DEVICE *dptr;
8871 int32 i, any;
8872 
8873 if ((sw == 0) || (sw == SWMASK ('C')))
8874     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8875 if (!bp || (!(bp->typ & sw)))
8876     return SCPE_OK;
8877 dptr = sim_dflt_dev;
8878 if (dptr == NULL)
8879     return SCPE_OK;
8880 if (sw & SWMASK ('C'))
8881     fprintf (st, "SET BREAK ");
8882 else {
8883     if (sim_vm_fprint_addr)
8884         sim_vm_fprint_addr (st, dptr, loc);
8885     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8886     fprintf (st, ":\t");
8887     }
8888 for (i = any = 0; i < 26; i++) {
8889     if ((bp->typ >> i) & 1) {
8890         if ((sw & SWMASK ('C')) == 0) {
8891             if (any)
8892                 fprintf (st, ", ");
8893             fputc (i + 'A', st);
8894             }
8895         else
8896             fprintf (st, "-%c", i + 'A');
8897         any = 1;
8898         }
8899     }
8900 if (sw & SWMASK ('C')) {
8901     fprintf (st, " ");
8902     if (sim_vm_fprint_addr)
8903         sim_vm_fprint_addr (st, dptr, loc);
8904     else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8905     }
8906 if (bp->cnt > 0)
8907     fprintf (st, "[%d]", bp->cnt);
8908 if (bp->act != NULL)
8909     fprintf (st, "; %s", bp->act);
8910 fprintf (st, "\n");
8911 return SCPE_OK;
8912 }
8913 
8914 /* Show all breakpoints */
8915 
8916 t_stat sim_brk_showall (FILE *st, int32 sw)
     /* [previous][next][first][last][top][bottom][index][help] */
8917 {
8918 int32 bit, mask, types;
8919 BRKTAB **bpt;
8920 
8921 if ((sw == 0) || (sw == SWMASK ('C')))
8922     sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8923 for (types=bit=0; bit <= ('Z'-'A'); bit++)
8924     if (sim_brk_types & (1 << bit))
8925         ++types;
8926 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
8927     fprintf (st, "Supported Breakpoint Types:");
8928     for (bit=0; bit <= ('Z'-'A'); bit++)
8929         if (sim_brk_types & (1 << bit))
8930             fprintf (st, " -%c", 'A' + bit);
8931     fprintf (st, "\n");
8932     }
8933 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
8934     mask = (sw & sim_brk_types);
8935     fprintf (st, "Displaying Breakpoint Types:");
8936     for (bit=0; bit <= ('Z'-'A'); bit++)
8937         if (mask & (1 << bit))
8938             fprintf (st, " -%c", 'A' + bit);
8939     fprintf (st, "\n");
8940     }
8941 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8942     BRKTAB *prev = NULL;
8943     BRKTAB *cur = *bpt;
8944     BRKTAB *next;
8945     /* First reverse the list */
8946     while (cur) {
8947         next = cur->next;
8948         cur->next = prev;
8949         prev = cur;
8950         cur = next;
8951         }
8952     /* save reversed list in the head pointer so lookups work */
8953     *bpt = prev;
8954     /* Walk the reversed list and print it in the order it was defined in */
8955     cur = prev;
8956     while (cur) {
8957         if (cur->typ & sw)
8958             sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
8959         cur = cur->next;
8960         }
8961     /* reversing the list again */
8962     cur = prev;
8963     prev = NULL;
8964     while (cur) {
8965         next = cur->next;
8966         cur->next = prev;
8967         prev = cur;
8968         cur = next;
8969         }
8970     /* restore original list */
8971     *bpt = prev;
8972     }
8973 return SCPE_OK;
8974 }
8975 
8976 /* Test for breakpoint */
8977 
8978 uint32 sim_brk_test (t_addr loc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
8979 {
8980 BRKTAB *bp;
8981 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
8982 
8983 if (sim_brk_summ & BRK_TYP_DYN_ALL)
8984     btyp |= BRK_TYP_DYN_ALL;
8985 
8986 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) {     /* in table, and type match? */
8987     double s_gtime = sim_gtime ();                      /* get time now */
8988 
8989     if (bp->time_fired[spc] == s_gtime)                 /* already taken?  */
8990         return 0;
8991     bp->time_fired[spc] = s_gtime;                      /* remember match time */
8992     if (--bp->cnt > 0)                                  /* count > 0? */
8993         return 0;
8994     bp->cnt = 0;                                        /* reset count */
8995     sim_brk_setact (bp->act);                           /* set up actions */
8996     sim_brk_match_type = btyp & bp->typ;                /* set return value */
8997     if (bp->typ & BRK_TYP_TEMP)
8998         sim_brk_clr (loc, bp->typ);                     /* delete one-shot breakpoint */
8999     sim_brk_match_addr = loc;
9000     return sim_brk_match_type;
9001     }
9002 return 0;
9003 }
9004 
9005 /* Get next pending action, if any */
9006 
9007 CONST char *sim_brk_getact (char *buf, int32 size)
     /* [previous][next][first][last][top][bottom][index][help] */
9008 {
9009 char *ep;
9010 size_t lnt;
9011 
9012 if (sim_brk_act[sim_do_depth] == NULL)                  /* any action? */
9013     return NULL;
9014 while (sim_isspace (*sim_brk_act[sim_do_depth]))        /* skip spaces */
9015     sim_brk_act[sim_do_depth]++;
9016 if (*sim_brk_act[sim_do_depth] == 0) {                  /* now empty? */
9017     return sim_brk_clract ();
9018     }
9019 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) {   /* cmd delimiter? */
9020     lnt = ep - sim_brk_act[sim_do_depth];               /* cmd length */
9021     memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1);   /* copy with ; */
9022     buf[lnt] = 0;                                       /* erase ; */
9023     sim_brk_act[sim_do_depth] += lnt + 1;               /* adv ptr */
9024     }
9025 else {
9026     strncpy (buf, sim_brk_act[sim_do_depth], size);     /* copy action */
9027     sim_brk_clract ();                                  /* no more */
9028     }
9029 return buf;
9030 }
9031 
9032 /* Clear pending actions */
9033 
9034 char *sim_brk_clract (void)
     /* [previous][next][first][last][top][bottom][index][help] */
9035 {
9036 FREE (sim_brk_act_buf[sim_do_depth]);
9037 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
9038 }
9039 
9040 /* Set up pending actions */
9041 
9042 void sim_brk_setact (const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
9043 {
9044 if (action) {
9045     sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
9046     if (!sim_brk_act_buf[sim_do_depth])
9047       {
9048         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9049                  __func__, __FILE__, __LINE__);
9050 #if defined(USE_BACKTRACE)
9051 # ifdef SIGUSR2
9052         (void)raise(SIGUSR2);
9053         /*NOTREACHED*/ /* unreachable */
9054 # endif /* ifdef SIGUSR2 */
9055 #endif /* if defined(USE_BACKTRACE) */
9056         abort();
9057       }
9058     strcpy (sim_brk_act_buf[sim_do_depth], action);
9059     sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
9060     }
9061 else
9062     sim_brk_clract ();
9063 }
9064 
9065 /* New PC */
9066 
9067 void sim_brk_npc (uint32 cnt)
     /* [previous][next][first][last][top][bottom][index][help] */
9068 {
9069 uint32 spc;
9070 BRKTAB **bpt, *bp;
9071 
9072 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
9073     cnt = SIM_BKPT_N_SPC;
9074 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9075     for (bp = *bpt; bp; bp = bp->next) {
9076         for (spc = 0; spc < cnt; spc++)
9077             bp->time_fired[spc] = -1.0;
9078         }
9079     }
9080 }
9081 
9082 /* Clear breakpoint space */
9083 
9084 void sim_brk_clrspc (uint32 spc, uint32 btyp)
     /* [previous][next][first][last][top][bottom][index][help] */
9085 {
9086 BRKTAB **bpt, *bp;
9087 
9088 if (spc < SIM_BKPT_N_SPC) {
9089     for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
9090         for (bp = *bpt; bp; bp = bp->next) {
9091             if (bp->typ & btyp)
9092                 bp->time_fired[spc] = -1.0;
9093             }
9094         }
9095     }
9096 }
9097 
9098 const char *sim_brk_message(void)
     /* [previous][next][first][last][top][bottom][index][help] */
9099 {
9100 static char msg[256];
9101 char addr[65];
9102 char buf[32];
9103 
9104 msg[0] = '\0';
9105 if (sim_dflt_dev) {
9106   if (sim_vm_sprint_addr)
9107     sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
9108   else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
9109 }
9110 if (sim_brk_type_desc) {
9111     BRKTYPTAB *brk = sim_brk_type_desc;
9112 
9113     while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
9114         if (brk->btyp == sim_brk_match_type) {
9115             sprintf (msg, "%s: %s", brk->desc, addr);
9116             break;
9117             }
9118         brk++;
9119         }
9120     }
9121 if (!msg[0])
9122     sprintf (msg, "%s Breakpoint at: %s\n", put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9123 
9124 return msg;
9125 }
9126 
9127 /* Expect package.  This code provides a mechanism to stop and control simulator
9128    execution based on traffic coming out of simulated ports and as well as a means
9129    to inject data into those ports.  It can conceptually viewed as a string
9130    breakpoint package.
9131 
9132    Expect rules are stored in tables associated with each port which can use this
9133    facility.  An expect rule consists of a five entry structure:
9134 
9135         match                   the expect match string
9136         size                    the number of bytes in the match string
9137         match_pattern           the expect match string in display format
9138         cnt                     number of iterations before match is declared
9139         action                  command string to be executed when match occurs
9140 
9141    All active expect rules are contained in an expect match context structure.
9142 
9143         rules                   the match rules
9144         size                    the count of match rules
9145         buf                     the buffer of output data which has been produced
9146         buf_ins                 the buffer insertion point for the next output data
9147         buf_size                the buffer size
9148 
9149    The package contains the following public routines:
9150 
9151         sim_set_expect          expect command parser and intializer
9152         sim_set_noexpect        noexpect command parser
9153         sim_exp_set             set or add an expect rule
9154         sim_exp_clr             clear or delete an expect rule
9155         sim_exp_clrall          clear all expect rules
9156         sim_exp_show            show an expect rule
9157         sim_exp_showall         show all expect rules
9158         sim_exp_check           test for rule match
9159 */
9160 
9161 /* Set expect */
9162 
9163 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9164 {
9165 char gbuf[CBUFSIZE];
9166 CONST char *tptr;
9167 CONST char *c1ptr;
9168 t_bool after_set = FALSE;
9169 uint32 after;
9170 int32 cnt = 0;
9171 t_stat r;
9172 
9173 if (exp == NULL)
9174     return sim_messagef (SCPE_ARG, "Null exp!\n");
9175 after = exp->after;
9176 
9177 if ((cptr == NULL) || (*cptr == 0))
9178     return SCPE_2FARG;
9179 if (*cptr == '[') {
9180     cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9181     if ((cptr == c1ptr) || (*c1ptr != ']'))
9182         return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
9183     cptr = c1ptr + 1;
9184     while (sim_isspace(*cptr))
9185         ++cptr;
9186     }
9187 tptr = get_glyph (cptr, gbuf, ',');
9188 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9189     after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9190     if (r != SCPE_OK)
9191         return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
9192     after_set = TRUE;
9193     cptr = tptr;
9194     }
9195 if ((*cptr != '"') && (*cptr != '\''))
9196     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9197 cptr = get_glyph_quoted (cptr, gbuf, 0);
9198 
9199 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9200 }
9201 
9202 /* Clear expect */
9203 
9204 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9205 {
9206 char gbuf[CBUFSIZE];
9207 
9208 if (!cptr || !*cptr)
9209     return sim_exp_clrall (exp);                    /* clear all rules */
9210 if ((*cptr != '"') && (*cptr != '\''))
9211     return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9212 cptr = get_glyph_quoted (cptr, gbuf, 0);
9213 if (*cptr != '\0')
9214     return SCPE_2MARG;                              /* No more arguments */
9215 return sim_exp_clr (exp, gbuf);                     /* clear one rule */
9216 }
9217 
9218 /* Search for an expect rule in an expect context */
9219 
9220 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, int32 start_rule)
     /* [previous][next][first][last][top][bottom][index][help] */
9221 {
9222 int32 i;
9223 
9224 if (!exp->rules)
9225     return NULL;
9226 for (i=start_rule; i<exp->size; i++)
9227     if (!strcmp (exp->rules[i].match_pattern, match))
9228         return &exp->rules[i];
9229 return NULL;
9230 }
9231 
9232 /* Clear (delete) an expect rule */
9233 
9234 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9235 {
9236 int32 i;
9237 
9238 if (!ep)                                                /* not there? ok */
9239     return SCPE_OK;
9240 FREE (ep->match);                                       /* deallocate match string */
9241 FREE (ep->match_pattern);                               /* deallocate the display format match string */
9242 FREE (ep->act);                                         /* deallocate action */
9243 exp->size -= 1;                                         /* decrement count */
9244 #ifndef __clang_analyzer__
9245 for (i=ep-exp->rules; i<exp->size; i++)                 /* shuffle up remaining rules */
9246     exp->rules[i] = exp->rules[i+1];
9247 if (exp->size == 0) {                                   /* No rules left? */
9248     FREE (exp->rules);
9249     exp->rules = NULL;
9250     }
9251 #endif /* ifndef __clang_analyzer__ */
9252 return SCPE_OK;
9253 }
9254 
9255 t_stat sim_exp_clr (EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9256 {
9257 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9258 
9259 while (ep) {
9260     sim_exp_clr_tab (exp, ep);
9261     ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9262     }
9263 return SCPE_OK;
9264 }
9265 
9266 /* Clear all expect rules */
9267 
9268 t_stat sim_exp_clrall (EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9269 {
9270 int32 i;
9271 
9272 for (i=0; i<exp->size; i++) {
9273     FREE (exp->rules[i].match);                         /* deallocate match string */
9274     FREE (exp->rules[i].match_pattern);                 /* deallocate display format match string */
9275     FREE (exp->rules[i].act);                           /* deallocate action */
9276     }
9277 FREE (exp->rules);
9278 exp->rules = NULL;
9279 exp->size = 0;
9280 FREE (exp->buf);
9281 exp->buf = NULL;
9282 exp->buf_size = 0;
9283 exp->buf_ins = 0;
9284 return SCPE_OK;
9285 }
9286 
9287 /* Set/Add an expect rule */
9288 
9289 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] */
9290 {
9291 EXPTAB *ep;
9292 uint8 *match_buf;
9293 uint32 match_size;
9294 int i;
9295 
9296 /* Validate the match string */
9297 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9298 if (!match_buf)
9299     return SCPE_MEM;
9300 if (switches & EXP_TYP_REGEX) {
9301     FREE (match_buf);
9302     return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9303     }
9304 else {
9305     if (switches & EXP_TYP_REGEX_I) {
9306         FREE (match_buf);
9307         return sim_messagef (SCPE_ARG, "Case independent matching is only valid for RegEx expect rules\n");
9308         }
9309     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9310     if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9311         FREE (match_buf);
9312         return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9313         }
9314     }
9315 FREE (match_buf);
9316 for (i=0; i<exp->size; i++) {                           /* Make sure this rule won't be occluded */
9317     if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9318         (exp->rules[i].switches & EXP_TYP_PERSIST))
9319         return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9320     }
9321 if (after && exp->size)
9322     return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9323 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9324 if (!exp->rules)
9325   {
9326     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9327              __func__, __FILE__, __LINE__);
9328 #if defined(USE_BACKTRACE)
9329 # ifdef SIGUSR2
9330     (void)raise(SIGUSR2);
9331     /*NOTREACHED*/ /* unreachable */
9332 # endif /* ifdef SIGUSR2 */
9333 #endif /* if defined(USE_BACKTRACE) */
9334     abort();
9335   }
9336 ep = &exp->rules[exp->size];
9337 exp->size += 1;
9338 exp->after = after;                                     /* set halt after value */
9339 memset (ep, 0, sizeof(*ep));
9340 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9341 if (ep->match_pattern)
9342     strcpy (ep->match_pattern, match);
9343 ep->cnt = cnt;                                          /* set proceed count */
9344 ep->switches = switches;                                /* set switches */
9345 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9346 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9347     sim_exp_clr_tab (exp, ep);                          /* clear it */
9348     FREE (match_buf);                                   /* release allocation */
9349     return SCPE_MEM;
9350     }
9351 if (switches & EXP_TYP_REGEX) {
9352     FREE (match_buf);
9353     match_buf = NULL;
9354     }
9355 else {
9356     sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9357     sim_decode_quoted_string (match, match_buf, &match_size);
9358     ep->match = match_buf;
9359     ep->size = match_size;
9360     }
9361 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9362 if (!ep->match_pattern)
9363   {
9364     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9365              __func__, __FILE__, __LINE__);
9366 #if defined(USE_BACKTRACE)
9367 # ifdef SIGUSR2
9368     (void)raise(SIGUSR2);
9369     /*NOTREACHED*/ /* unreachable */
9370 # endif /* ifdef SIGUSR2 */
9371 #endif /* if defined(USE_BACKTRACE) */
9372     abort();
9373   }
9374 strcpy (ep->match_pattern, match);
9375 if (ep->act) {                                          /* replace old action? */
9376     FREE (ep->act);                                     /* deallocate */
9377     ep->act = NULL;                                     /* now no action */
9378     }
9379 if (act) while (sim_isspace(*act)) ++act;                   /* skip leading spaces in action string */
9380 if ((act != NULL) && (*act != 0)) {                     /* new action? */
9381     char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9382     if (newp == NULL)                                   /* mem err? */
9383         return SCPE_MEM;
9384     strcpy (newp, act);                                 /* copy action */
9385     ep->act = newp;                                     /* set pointer */
9386     }
9387 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9388 for (i=0; i<exp->size; i++) {
9389     uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9390     if (compare_size >= exp->buf_size) {
9391         exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9392         exp->buf_size = compare_size + 1;
9393         }
9394     }
9395 return SCPE_OK;
9396 }
9397 
9398 /* Show an expect rule */
9399 
9400 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
     /* [previous][next][first][last][top][bottom][index][help] */
9401 {
9402 if (!ep)
9403     return SCPE_OK;
9404 fprintf (st, "EXPECT");
9405 if (ep->switches & EXP_TYP_PERSIST)
9406     fprintf (st, " -p");
9407 if (ep->switches & EXP_TYP_CLEARALL)
9408     fprintf (st, " -c");
9409 if (ep->switches & EXP_TYP_REGEX)
9410     fprintf (st, " -r");
9411 if (ep->switches & EXP_TYP_REGEX_I)
9412     fprintf (st, " -i");
9413 fprintf (st, " %s", ep->match_pattern);
9414 if (ep->cnt > 0)
9415     fprintf (st, " [%d]", ep->cnt);
9416 if (ep->act)
9417     fprintf (st, " %s", ep->act);
9418 fprintf (st, "\n");
9419 return SCPE_OK;
9420 }
9421 
9422 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
9423 {
9424 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9425 
9426 if (exp->buf_size) {
9427     char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9428 
9429     fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
9430     fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
9431     fprintf (st, "Buffer Contents: %s\n", bstr);
9432     FREE (bstr);
9433     }
9434 if (exp->after)
9435     fprintf (st, "Halt After: %d instructions\n", exp->after);
9436 if (exp->dptr && exp->dbit)
9437     fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : "");
9438 fprintf (st, "Match Rules:\n");
9439 if (!*match)
9440     return sim_exp_showall (st, exp);
9441 if (!ep) {
9442     fprintf (st, "No Rules match '%s'\n", match);
9443     return SCPE_ARG;
9444     }
9445 do {
9446     sim_exp_show_tab (st, exp, ep);
9447     ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9448     } while (ep);
9449 return SCPE_OK;
9450 }
9451 
9452 /* Show all expect rules */
9453 
9454 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
     /* [previous][next][first][last][top][bottom][index][help] */
9455 {
9456 int32 i;
9457 
9458 for (i=0; i < exp->size; i++)
9459     sim_exp_show_tab (st, exp, &exp->rules[i]);
9460 return SCPE_OK;
9461 }
9462 
9463 /* Test for expect match */
9464 
9465 t_stat sim_exp_check (EXPECT *exp, uint8 data)
     /* [previous][next][first][last][top][bottom][index][help] */
9466 {
9467 int32 i;
9468 EXPTAB *ep = NULL;
9469 char *tstr = NULL;
9470 
9471 if ((!exp) || (!exp->rules))                            /* Anything to check? */
9472     return SCPE_OK;
9473 
9474 exp->buf[exp->buf_ins++] = data;                        /* Save new data */
9475 exp->buf[exp->buf_ins] = '\0';                          /* Nul terminate for RegEx match */
9476 
9477 for (i=0; i < exp->size; i++) {
9478     ep = &exp->rules[i];
9479     if (ep == NULL)
9480         break;
9481     if (ep->switches & EXP_TYP_REGEX) {
9482         }
9483     else {
9484         if (exp->buf_ins < ep->size) {                          /* Match stradle end of buffer */
9485             /*
9486              * First compare the newly deposited data at the beginning
9487              * of buffer with the end of the match string
9488              */
9489             if (exp->buf_ins) {
9490                 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9491                     char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9492                     char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9493 
9494                     sim_debug (exp->dbit, exp->dptr, "Checking String[0:%d]: %s\n", exp->buf_ins, estr);
9495                     sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9496                     FREE (estr);
9497                     FREE (mstr);
9498                     }
9499                 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9500                     continue;
9501                 }
9502             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9503                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9504                 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9505 
9506                 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_size-(ep->size-exp->buf_ins), ep->size-exp->buf_ins, estr);
9507                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9508                 FREE (estr);
9509                 FREE (mstr);
9510                 }
9511             if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9512                 continue;
9513             break;
9514             }
9515         else {
9516             if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9517                 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9518                 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9519 
9520                 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_ins-ep->size, ep->size, estr);
9521                 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9522                 FREE (estr);
9523                 FREE (mstr);
9524                 }
9525             if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9526                 continue;
9527             break;
9528             }
9529         }
9530     }
9531 if (exp->buf_ins == exp->buf_size) {                    /* At end of match buffer? */
9532         exp->buf_ins = 0;                               /* wrap around to beginning */
9533         sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9534     }
9535 if ((ep != NULL) && (i != exp->size)) {                 /* Found? */
9536     sim_debug (exp->dbit, exp->dptr, "Matched expect pattern!\n");
9537     if (ep->cnt > 0) {
9538         ep->cnt -= 1;
9539         sim_debug (exp->dbit, exp->dptr, "Waiting for %d more match%s before stopping\n",
9540                                          ep->cnt, (ep->cnt == 1) ? "" : "es");
9541         }
9542     else {
9543         uint32 after   = exp->after;
9544         int32 switches = ep->switches;
9545         if (ep->act && *ep->act) {
9546             sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
9547             }
9548         else {
9549             sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
9550             }
9551         sim_brk_setact (ep->act);                       /* set up actions */
9552         if (ep->switches & EXP_TYP_CLEARALL)            /* Clear-all expect rule? */
9553             sim_exp_clrall (exp);                       /* delete all rules */
9554         else {
9555             if (!(ep->switches & EXP_TYP_PERSIST))      /* One shot expect rule? */
9556                 sim_exp_clr_tab (exp, ep);              /* delete it */
9557             }
9558         sim_activate (&sim_expect_unit,                 /* schedule simulation stop when indicated */
9559                       (switches & EXP_TYP_TIME) ?
9560                             (uint32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
9561                              after);
9562         }
9563     /* Matched data is no longer available for future matching */
9564     exp->buf_ins = 0;
9565     }
9566 if (tstr)  //-V547
9567   FREE (tstr);
9568 return SCPE_OK;
9569 }
9570 
9571 /* Queue input data for sending */
9572 
9573 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
     /* [previous][next][first][last][top][bottom][index][help] */
9574 {
9575 if (snd->extoff != 0) {
9576     if (snd->insoff-snd->extoff > 0)
9577         memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9578     snd->insoff -= snd->extoff;
9579     snd->extoff -= snd->extoff;
9580     }
9581 if (snd->insoff+size > snd->bufsize) {
9582     snd->bufsize = snd->insoff+size;
9583     snd->buffer  = (uint8 *)realloc(snd->buffer, snd->bufsize);
9584     if (!snd->buffer)
9585       {
9586         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
9587                  __func__, __FILE__, __LINE__);
9588 #if defined(USE_BACKTRACE)
9589 # ifdef SIGUSR2
9590         (void)raise(SIGUSR2);
9591         /*NOTREACHED*/ /* unreachable */
9592 # endif /* ifdef SIGUSR2 */
9593 #endif /* if defined(USE_BACKTRACE) */
9594         abort();
9595       }
9596     }
9597 memcpy(snd->buffer+snd->insoff, data, size);
9598 snd->insoff += size;
9599 if (delay)
9600     snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
9601 if (after)
9602     snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
9603 if (snd->after == 0)
9604     snd->after = snd->delay;
9605 snd->next_time = sim_gtime() + snd->after;
9606 return SCPE_OK;
9607 }
9608 
9609 /* Cancel Queued input data */
9610 t_stat sim_send_clear (SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
9611 {
9612 snd->insoff = 0;
9613 snd->extoff = 0;
9614 return SCPE_OK;
9615 }
9616 
9617 /* Display console Queued input data status */
9618 
9619 t_stat sim_show_send_input (FILE *st, const SEND *snd)
     /* [previous][next][first][last][top][bottom][index][help] */
9620 {
9621 if (snd->extoff < snd->insoff) {
9622     fprintf (st, "%d bytes of pending input Data:\n    ", snd->insoff-snd->extoff);
9623     fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9624     fprintf (st, "\n");
9625     }
9626 else
9627     fprintf (st, "No Pending Input Data\n");
9628 if ((snd->next_time - sim_gtime()) > 0) {
9629     if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
9630         fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
9631         (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
9632     else
9633         fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
9634     }
9635 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
9636     fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
9637 else
9638     fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay);
9639 if (snd->dptr && snd->dbit)
9640     fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : "");
9641 return SCPE_OK;
9642 }
9643 
9644 /* Poll for Queued input data */
9645 
9646 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9647 {
9648 if (snd && (snd->extoff < snd->insoff)) {               /* pending input characters available? */
9649     if (sim_gtime() < snd->next_time) {                 /* too soon? */
9650         *stat = SCPE_OK;
9651         sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
9652         }
9653     else {
9654         char dstr[8] = "";
9655         *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
9656         snd->next_time = sim_gtime() + snd->delay;
9657         if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
9658             sprintf (dstr, " '%c'", *stat & 0xFF);
9659         sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
9660         }
9661     return TRUE;
9662     }
9663 return FALSE;
9664 }
9665 
9666 /* Message Text */
9667 
9668 const char *sim_error_text (t_stat stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9669 {
9670 static char msgbuf[64];
9671 
9672 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE);        /* remove any flags */
9673 if (stat == SCPE_OK)
9674     return "No Error";
9675 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
9676     return scp_errors[stat-SCPE_BASE].message;
9677 sprintf(msgbuf, "Error %d", stat);
9678 return msgbuf;
9679 }
9680 
9681 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
     /* [previous][next][first][last][top][bottom][index][help] */
9682 {
9683 char gbuf[CBUFSIZE];
9684 int32 cond;
9685 
9686 *stat = SCPE_ARG;
9687 cptr = get_glyph (cptr, gbuf, 0);
9688 if (0 == memcmp("SCPE_", gbuf, 5))
9689     memmove (gbuf, gbuf + 5, 1 + strlen (gbuf + 5));  /* skip leading SCPE_ */
9690 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
9691     if (0 == strcmp(scp_errors[cond].code, gbuf)) {
9692         cond += SCPE_BASE;
9693         break;
9694         }
9695 if (0 == strcmp(gbuf, "OK"))
9696     cond = SCPE_OK;
9697 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) {       /* not found? */
9698     if (0 == (cond = strtol(gbuf, NULL, 0)))  /* try explicit number */
9699         return SCPE_ARG;
9700     }
9701 if (cond > SCPE_MAX_ERR)
9702     return SCPE_ARG;
9703 *stat = cond;
9704 return SCPE_OK;
9705 }
9706 
9707 /* Debug printout routines, from Dave Hittner */
9708 
9709 const char* debug_bstates = "01_^";
9710 char debug_line_prefix[256];
9711 int32 debug_unterm  = 0;
9712 
9713 /* Finds debug phrase matching bitmask from from device DEBTAB table */
9714 
9715 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9716 {
9717 static const char *debtab_none    = "DEBTAB_ISNULL";
9718 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
9719 const char *some_match = NULL;
9720 int32 offset = 0;
9721 
9722 if (dptr->debflags == 0)
9723     return debtab_none;
9724 
9725 dbits &= dptr->dctrl;                           /* Look for just the bits tha matched */
9726 
9727 /* Find matching words for bitmask */
9728 
9729 while ((offset < 32) && dptr->debflags[offset].name) {
9730     if (dptr->debflags[offset].mask == dbits)   /* All Bits Match */
9731         return dptr->debflags[offset].name;
9732     if (dptr->debflags[offset].mask & dbits)
9733         some_match = dptr->debflags[offset].name;
9734     offset++;
9735     }
9736 return some_match ? some_match : debtab_nomatch;
9737 }
9738 
9739 /* Prints standard debug prefix unless previous call unterminated */
9740 
9741 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
     /* [previous][next][first][last][top][bottom][index][help] */
9742 {
9743 const char* debug_type = get_dbg_verb (dbits, dptr);
9744 char tim_t[32] = "";
9745 char tim_a[32] = "";
9746 char  pc_s[64] = "";
9747 struct timespec time_now;
9748 
9749 #if defined(__MACH__) && defined(__APPLE__) && \
9750   ( defined(__PPC__) || defined(_ARCH_PPC) )
9751 # include <mach/clock.h>
9752 # include <mach/mach.h>
9753 # ifdef MACOSXPPC
9754 #  undef MACOSXPPC
9755 # endif /* ifdef MACOSXPPC */
9756 # define MACOSXPPC 1
9757 #endif /* if defined(__MACH__) && defined(__APPLE__) &&
9758            ( defined(__PPC__) || defined(_ARCH_PPC) ) */
9759 
9760 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
9761 #ifdef MACOSXPPC
9762     clock_serv_t cclock;
9763     mach_timespec_t mts;
9764     host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
9765     clock_get_time(cclock, &mts);
9766     mach_port_deallocate(mach_task_self(), cclock);
9767     time_now.tv_sec = mts.tv_sec;
9768     time_now.tv_nsec = mts.tv_nsec;
9769 #else
9770     clock_gettime(CLOCK_REALTIME, &time_now);
9771 #endif /* ifdef MACOSXPPC */
9772     if (sim_deb_switches & SWMASK ('R'))
9773         sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
9774     if (sim_deb_switches & SWMASK ('T')) {
9775         time_t tnow = (time_t)time_now.tv_sec;
9776         struct tm *now = gmtime(&tnow);
9777         sprintf(tim_t, "%02d:%02d:%02d.%03ld ",
9778                 (int)now->tm_hour,
9779                 (int)now->tm_min,
9780                 (int)now->tm_sec,
9781                 (long)(time_now.tv_nsec/1000000));
9782         }
9783     if (sim_deb_switches & SWMASK ('A')) {
9784         sprintf(tim_t, "%d.%03ld ",
9785                 (int)(time_now.tv_sec),
9786                 (long)(time_now.tv_nsec/1000000));
9787         }
9788     }
9789 if (sim_deb_switches & SWMASK ('P')) {
9790     t_value val;
9791 
9792     if (sim_vm_pc_value)
9793         val = (*sim_vm_pc_value)();
9794     else
9795         val = get_rval (sim_PC, 0);
9796     sprintf(pc_s, "-%s:", sim_PC->name);
9797     sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
9798     }
9799 sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s,  "", dptr->name, debug_type);
9800 return debug_line_prefix;
9801 }
9802 
9803 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
     /* [previous][next][first][last][top][bottom][index][help] */
9804 {
9805 int32 i, fields, offset;
9806 uint32 value, beforevalue, mask;
9807 
9808 for (fields=offset=0; bitdefs[fields].name; ++fields) {
9809     if (bitdefs[fields].offset == 0xffffffff)       /* fixup uninitialized offsets */
9810         bitdefs[fields].offset = offset;
9811     offset += bitdefs[fields].width;
9812     }
9813 for (i = fields-1; i >= 0; i--) {                   /* print xlation, transition */
9814     if (bitdefs[i].name[0] == '\0')
9815         continue;
9816     if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
9817         int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
9818         Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
9819         }
9820     else {
9821         const char *delta = "";
9822         mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
9823         value = (uint32)((after >> bitdefs[i].offset) & mask);
9824         beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
9825         if (value < beforevalue)
9826             delta = "_";
9827         if (value > beforevalue)
9828             delta = "^";
9829         if (bitdefs[i].valuenames)
9830             Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
9831         else
9832             if (bitdefs[i].format) {
9833                 Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
9834                 Fprintf(stream, bitdefs[i].format, value);
9835                 Fprintf(stream, " ");
9836                 }
9837             else
9838                 Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
9839         }
9840     }
9841 }
9842 
9843 /* Prints state of a register: bit translation + state (0,1,_,^)
9844    indicating the state and transition of the bit and bitfields. States:
9845    0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
9846 
9847 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
     /* [previous][next][first][last][top][bottom][index][help] */
9848     BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
9849 {
9850 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
9851     if (!debug_unterm)
9852         fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr));         /* print prefix if required */
9853     if (header)
9854         fprintf(sim_deb, "%s: ", header);
9855     fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
9856     if (terminate)
9857         fprintf(sim_deb, "\r\n");
9858     debug_unterm = terminate ? 0 : 1;                   /* set unterm for next */
9859     }
9860 }
9861 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
     /* [previous][next][first][last][top][bottom][index][help] */
9862     uint32 before, uint32 after, int terminate)
9863 {
9864 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
9865 }
9866 
9867 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9868 void sim_printf (const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9869 {
9870 char stackbuf[STACKBUFSIZE];
9871 int32 bufsize = sizeof(stackbuf);
9872 char *buf = stackbuf;
9873 int32 len;
9874 va_list arglist;
9875 
9876 while (1) {                                         /* format passed string, args */
9877     va_start (arglist, fmt);
9878     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9879     va_end (arglist);
9880 
9881 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9882 
9883     if ((len < 0) || (len >= bufsize-1)) {
9884         if (buf != stackbuf)
9885             FREE (buf);
9886         bufsize = bufsize * 2;
9887         if (bufsize < len + 2)
9888             bufsize = len + 2;
9889         buf = (char *) malloc (bufsize);
9890         if (buf == NULL)                            /* out of memory */
9891             return;
9892         buf[bufsize-1] = '\0';
9893         continue;
9894         }
9895     break;
9896     }
9897 
9898 if (sim_is_running) {
9899     char *c, *remnant = buf;
9900     while ((c = strchr(remnant, '\n'))) {
9901         if ((c != buf) && (*(c - 1) != '\r'))
9902             printf("%.*s\r\n", (int)(c-remnant), remnant);
9903         else
9904             printf("%.*s\n", (int)(c-remnant), remnant);
9905         remnant = c + 1;
9906         }
9907     printf("%s", remnant);
9908     }
9909 else
9910     printf("%s", buf);
9911 if (sim_log && (sim_log != stdout))
9912     fprintf (sim_log, "%s", buf);
9913 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
9914     fprintf (sim_deb, "%s", buf);
9915 
9916 if (buf != stackbuf)
9917     FREE (buf);
9918 }
9919 
9920 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9921 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9922 {
9923 char stackbuf[STACKBUFSIZE];
9924 int32 bufsize = sizeof(stackbuf);
9925 char *buf = stackbuf;
9926 int32 len;
9927 va_list arglist;
9928 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
9929 
9930 while (1) {                                         /* format passed string, args */
9931     va_start (arglist, fmt);
9932     len = vsnprintf (buf, bufsize-1, fmt, arglist);
9933     va_end (arglist);
9934 
9935 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9936 
9937     if ((len < 0) || (len >= bufsize-1)) {
9938         if (buf != stackbuf)
9939             FREE (buf);
9940         bufsize = bufsize * 2;
9941         if (bufsize < len + 2)
9942             bufsize = len + 2;
9943         buf = (char *) malloc (bufsize);
9944         if (buf == NULL)                            /* out of memory */
9945             return SCPE_MEM;
9946         buf[bufsize-1] = '\0';
9947         continue;
9948         }
9949     break;
9950     }
9951 
9952 if (sim_do_ocptr[sim_do_depth]) {
9953     if (!sim_do_echo && !sim_quiet && !inhibit_message)
9954         sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9955     else {
9956         if (sim_deb)                        /* Always put context in debug output */
9957             fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9958         }
9959     }
9960 if (sim_is_running && !inhibit_message) {
9961     char *c, *remnant = buf;
9962     while ((c = strchr(remnant, '\n'))) {
9963         if ((c != buf) && (*(c - 1) != '\r'))
9964             printf("%.*s\r\n", (int)(c-remnant), remnant);
9965         else
9966             printf("%.*s\n", (int)(c-remnant), remnant);
9967         remnant = c + 1;
9968         }
9969     printf("%s", remnant);
9970     }
9971 else {
9972     if (!inhibit_message)
9973         printf("%s", buf);
9974     }
9975 if (sim_log && (sim_log != stdout) && !inhibit_message)
9976     fprintf (sim_log, "%s", buf);
9977 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
9978     fprintf (sim_deb, "%s", buf);
9979 
9980 if (buf != stackbuf)
9981     FREE (buf);
9982 return stat | SCPE_NOMESSAGE;
9983 }
9984 
9985 /* Inline debugging - will print debug message if debug file is
9986    set and the bitmask matches the current device debug options.
9987    Extra returns are added for un*x systems, since the output
9988    device is set into 'raw' mode when the cpu is booted,
9989    and the extra returns don't hurt any other systems.
9990    Callers should be calling sim_debug() which is a macro
9991    defined in scp.h which evaluates the action condition before
9992    incurring call overhead. */
9993 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
9994 {
9995 DEVICE *dptr = (DEVICE *)vdptr;
9996 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
9997     char stackbuf[STACKBUFSIZE];
9998     int32 bufsize = sizeof(stackbuf);
9999     char *buf = stackbuf;
10000     va_list arglist;
10001     int32 i, j, len;
10002     const char* debug_prefix = sim_debug_prefix(dbits, dptr);   /* prefix to print if required */
10003 
10004     buf[bufsize-1] = '\0';
10005     while (1) {                                         /* format passed string, args */
10006         va_start (arglist, fmt);
10007         len = vsnprintf (buf, bufsize-1, fmt, arglist);
10008         va_end (arglist);
10009 
10010 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
10011 
10012         if ((len < 0) || (len >= bufsize-1)) {
10013             if (buf != stackbuf)
10014                 FREE (buf);
10015             bufsize = bufsize * 2;
10016             if (bufsize < len + 2)
10017                 bufsize = len + 2;
10018             buf = (char *) malloc (bufsize);
10019             if (buf == NULL)                            /* out of memory */
10020                 return;
10021             buf[bufsize-1] = '\0';
10022             continue;
10023             }
10024         break;
10025         }
10026 
10027 /* Output the formatted data expanding newlines where they exist */
10028 
10029     for (i = j = 0; i < len; ++i) {
10030         if ('\n' == buf[i]) {
10031             if (i >= j) {
10032                 if ((i != j) || (i == 0)) {
10033                     if (debug_unterm)
10034                         fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
10035                     else                                /* print prefix when required */
10036                         fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
10037                     }
10038                 debug_unterm = 0;
10039                 }
10040             j = i + 1;
10041             }
10042         }
10043     if (i > j) {
10044         if (debug_unterm)
10045             fprintf (sim_deb, "%.*s", i-j, &buf[j]);
10046         else                                        /* print prefix when required */
10047             fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
10048         }
10049 
10050 /* Set unterminated flag for next time */
10051 
10052     debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
10053     if (buf != stackbuf)
10054         FREE (buf);
10055     }
10056 return;
10057 }
10058 
10059 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] */
10060 {
10061 
10062 if (sim_deb && (dptr->dctrl & reason)) {
10063     sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
10064     if (data && len) {
10065         unsigned int i, same, group, sidx, oidx, ridx, eidx, soff;
10066         char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
10067         static char hex[] = "0123456789ABCDEF";
10068         static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
10069         static unsigned char ebcdic2ascii[] = {
10070             0000,0001,0002,0003,0234,0011,0206,0177,
10071             0227,0215,0216,0013,0014,0015,0016,0017,
10072             0020,0021,0022,0023,0235,0205,0010,0207,
10073             0030,0031,0222,0217,0034,0035,0036,0037,
10074             0200,0201,0202,0203,0204,0012,0027,0033,
10075             0210,0211,0212,0213,0214,0005,0006,0007,
10076             0220,0221,0026,0223,0224,0225,0226,0004,
10077             0230,0231,0232,0233,0024,0025,0236,0032,
10078             0040,0240,0241,0242,0243,0244,0245,0246,
10079             0247,0250,0133,0056,0074,0050,0053,0041,
10080             0046,0251,0252,0253,0254,0255,0256,0257,
10081             0260,0261,0135,0044,0052,0051,0073,0136,
10082             0055,0057,0262,0263,0264,0265,0266,0267,
10083             0270,0271,0174,0054,0045,0137,0076,0077,
10084             0272,0273,0274,0275,0276,0277,0300,0301,
10085             0302,0140,0072,0043,0100,0047,0075,0042,
10086             0303,0141,0142,0143,0144,0145,0146,0147,
10087             0150,0151,0304,0305,0306,0307,0310,0311,
10088             0312,0152,0153,0154,0155,0156,0157,0160,
10089             0161,0162,0313,0314,0315,0316,0317,0320,
10090             0321,0176,0163,0164,0165,0166,0167,0170,
10091             0171,0172,0322,0323,0324,0325,0326,0327,
10092             0330,0331,0332,0333,0334,0335,0336,0337,
10093             0340,0341,0342,0343,0344,0345,0346,0347,
10094             0173,0101,0102,0103,0104,0105,0106,0107,
10095             0110,0111,0350,0351,0352,0353,0354,0355,
10096             0175,0112,0113,0114,0115,0116,0117,0120,
10097             0121,0122,0356,0357,0360,0361,0362,0363,
10098             0134,0237,0123,0124,0125,0126,0127,0130,
10099             0131,0132,0364,0365,0366,0367,0370,0371,
10100             0060,0061,0062,0063,0064,0065,0066,0067,
10101             0070,0071,0372,0373,0374,0375,0376,0377,
10102             };
10103 
10104         for (i=same=0; i<len; i += 16) {
10105             if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
10106                 ++same;
10107                 continue;
10108                 }
10109             if (same > 0) {
10110                 sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
10111                 same = 0;
10112                 }
10113             group = (((len - i) > 16) ? 16 : (len - i));
10114             strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10115             eidx = strlen(ebcdicbuf);
10116             strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10117             ridx = strlen(rad50buf);
10118             strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10119             soff = strlen(strbuf);
10120             for (sidx=oidx=0; sidx<group; ++sidx) {
10121                 outbuf[oidx++] = ' ';
10122                 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10123                 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10124                 if (sim_isprint (data[i+sidx]))
10125                     strbuf[soff+sidx] = data[i+sidx];
10126                 else
10127                     strbuf[soff+sidx] = '.';
10128                 if (ridx && ((sidx&1) == 0)) {
10129                     uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10130 
10131                     if (word >= 64000) {
10132                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10133                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10134                         rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10135                         }
10136                     else {
10137                         rad50buf[ridx++] = rad50[word/1600];
10138                         rad50buf[ridx++] = rad50[(word/40)%40];
10139                         rad50buf[ridx++] = rad50[word%40];
10140                         }
10141                     }
10142                 if (eidx) {
10143                     if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10144                         ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10145                     else
10146                         ebcdicbuf[eidx++] = '.';
10147                     }
10148                 }
10149             outbuf[oidx] = '\0';
10150             strbuf[soff+sidx] = '\0';
10151             ebcdicbuf[eidx] = '\0';
10152             rad50buf[ridx] = '\0';
10153             sim_debug (reason, dptr, "%04X%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
10154             }
10155         if (same > 0) {
10156             sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), (unsigned int)(len-1));
10157             }
10158         }
10159     }
10160 }
10161 
10162 int Fprintf (FILE *f, const char* fmt, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
10163 {
10164 int ret = 0;
10165 va_list args;
10166 
10167 va_start (args, fmt);
10168     ret = vfprintf (f, fmt, args);
10169 va_end (args);
10170 return ret;
10171 }
10172 
10173 /* Hierarchical help presentation
10174  *
10175  * Device help can be presented hierarchically by calling
10176  *
10177  * t_stat scp_help (FILE *st, DEVICE *dptr,
10178  *                  UNIT *uptr, int flag, const char *help, char *cptr)
10179  *
10180  * or one of its three cousins from the device HELP routine.
10181  *
10182  * *help is the pointer to the structured help text to be displayed.
10183  *
10184  * The format and usage, and some helper macros can be found in scp_help.h
10185  * If you don't use the macros, it is not necessary to #include "scp_help.h".
10186  *
10187  * Actually, if you don't specify a DEVICE pointer and don't include
10188  * other device references, it can be used for non-device help.
10189  */
10190 
10191 #define blankch(x) ((x) == ' ' || (x) == '\t')
10192 
10193 typedef struct topic {
10194     uint32         level;
10195     char          *title;
10196     char          *label;
10197     struct topic  *parent;
10198     struct topic **children;
10199     uint32         kids;
10200     char          *text;
10201     size_t         len;
10202     uint32         flags;
10203     uint32         kidwid;
10204 #define HLP_MAGIC_TOPIC  1
10205     } TOPIC;
10206 
10207 static volatile struct {
10208     const char *error;
10209     const char *prox;
10210     size_t block;
10211     size_t line;
10212     } help_where = { "", NULL, 0, 0 };
10213 jmp_buf help_env;
10214 
10215 #define FAIL(why,text,here)        \
10216   {                                \
10217     help_where.error = #text;      \
10218     help_where.prox = here;        \
10219     longjmp ( help_env, (why) );   \
10220     /*LINTED E_STMT_NOT_REACHED*/  \
10221   }
10222 
10223 /*
10224  * Add to topic text.
10225  * Expands text buffer as necessary.
10226  */
10227 
10228 static void appendText (TOPIC *topic, const char *text, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
10229 {
10230 char *newt;
10231 
10232 if (!len)
10233     return;
10234 
10235 newt = (char *)realloc (topic->text, topic->len + len +1);
10236 if (!newt) {
10237 #ifndef SUNLINT
10238     FAIL (SCPE_MEM, No memory, NULL);
10239 #endif /* ifndef SUNLINT */
10240     }
10241 topic->text = newt;
10242 memcpy (newt + topic->len, text, len);
10243 topic->len +=len;
10244 newt[topic->len] = '\0';
10245 return;
10246 }
10247 
10248 /* Release memory held by a topic and its children.
10249  */
10250 static void cleanHelp (TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10251 {
10252 TOPIC *child;
10253 size_t i;
10254 
10255 FREE (topic->title);
10256 FREE (topic->text);
10257 FREE (topic->label);
10258 for (i = 0; i < topic->kids; i++) {
10259     child = topic->children[i];
10260     cleanHelp (child);
10261     FREE (child);
10262     }
10263 FREE (topic->children);
10264 return;
10265 }
10266 
10267 /* Build a help tree from a string.
10268  * Handles substitutions, formatting.
10269  */
10270 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10271                          UNIT *uptr, const char *htext, va_list ap)
10272 {
10273 char *end;
10274 size_t n, ilvl;
10275 #define VSMAX 100
10276 char *vstrings[VSMAX];
10277 size_t vsnum = 0;
10278 char * astrings[VSMAX+1];
10279 size_t asnum = 0;
10280 char *const *hblock;
10281 const char *ep;
10282 t_bool excluded = FALSE;
10283 
10284 /* variable arguments consumed table.
10285  * The scheme used allows arguments to be accessed in random
10286  * order, but for portability, all arguments must be char *.
10287  * If you try to violate this, there ARE machines that WILL break.
10288  */
10289 
10290 memset (vstrings, 0, sizeof (vstrings));
10291 memset (astrings, 0, sizeof (astrings));
10292 astrings[asnum++] = (char *) htext;
10293 
10294 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10295     help_where.block = hblock - astrings;
10296     help_where.line = 0;
10297     while (*htext) {
10298         const char *start;
10299 
10300         help_where.line++;
10301         if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10302             if (excluded) {                     /* Excluded topic text */
10303                 while (*htext && *htext != '\n')
10304                     htext++;
10305                 if (*htext)
10306                     ++htext;
10307                 continue;
10308                 }
10309             ilvl = 1;
10310             appendText (topic, "    ", 4);      /* Basic indentation */
10311             if (*htext == '+') {                /* More for each + */
10312                 while (*htext == '+') {
10313                     ilvl++;
10314                     appendText (topic, "    ", 4);
10315                     htext++;
10316                     }
10317                 }
10318             while (*htext && *htext != '\n' && sim_isspace (*htext))
10319                 htext++;
10320             if (!*htext)                        /* Empty after removing leading spaces */
10321                 break;
10322             start = htext;
10323             while (*htext) {                    /* Process line for substitutions */
10324                 if (*htext == '%') {
10325                     appendText (topic, start, htext - start); /* Flush up to escape */
10326                     switch (*++htext) {         /* Evaluate escape */
10327                         case 'U':
10328                             if (dptr) {
10329                                 char buf[129];
10330                                 n = uptr? uptr - dptr->units: 0;
10331                                 sprintf (buf, "%s%u", dptr->name, (int)n);
10332                                 appendText (topic, buf, strlen (buf));
10333                                 }
10334                             break;
10335                         case 'D':
10336                             if (dptr != NULL)
10337                                 appendText (topic, dptr->name, strlen (dptr->name));
10338                             break;
10339                         case 'S':
10340                             appendText (topic, sim_name, strlen (sim_name));
10341                             break;
10342                         case '%':
10343                             appendText (topic, "%", 1);
10344                             break;
10345                         case '+':
10346                             appendText (topic, "+", 1);
10347                             break;
10348                         default:                    /* Check for vararg # */
10349                             if (sim_isdigit (*htext)) {
10350                                 n = 0;
10351                                 while (sim_isdigit (*htext))
10352                                     n += (n * 10) + (*htext++ - '0');
10353                                 if (( *htext != 'H' && *htext != 's') ||
10354                                     n == 0 || n >= VSMAX) {
10355 #ifndef SUNLINT
10356                                     FAIL (SCPE_ARG, Invalid escape, htext);
10357 #endif /* ifndef SUNLINT */
10358                                     }
10359                                 while (n > vsnum)   /* Get arg pointer if not cached */
10360                                     vstrings[vsnum++] = va_arg (ap, char *);
10361                                 start = vstrings[n-1]; /* Insert selected string */
10362                                 if (*htext == 'H') {   /* Append as more input */
10363                                     if (asnum >= VSMAX) {
10364 #ifndef SUNLINT
10365                                         FAIL (SCPE_ARG, Too many blocks, htext);
10366 #endif /* ifndef SUNLINT */
10367                                         }
10368                                     astrings[asnum++] = (char *)start;
10369                                     break;
10370                                     }
10371                                 ep = start;
10372                                 while (*ep) {
10373                                     if (*ep == '\n') {
10374                                         ep++;       /* Segment to \n */
10375                                         appendText (topic, start, ep - start);
10376                                         if (*ep) {  /* More past \n, indent */
10377                                             size_t i;
10378                                             for (i = 0; i < ilvl; i++)
10379                                                 appendText (topic, "    ", 4);
10380                                             }
10381                                         start = ep;
10382                                         }
10383                                     else
10384                                         ep++;
10385                                     }
10386                                 appendText (topic, start, ep-start);
10387                                 break;
10388                                 }
10389 #ifndef SUNLINT
10390                             FAIL (SCPE_ARG, Invalid escape, htext);
10391 #endif /* ifndef SUNLINT */
10392                         } /* switch (escape) */
10393                     start = ++htext;
10394                     continue;                   /* Current line */
10395                     } /* if (escape) */
10396                 if (*htext == '\n') {           /* End of line, append last segment */
10397                     htext++;
10398                     appendText (topic, start, htext - start);
10399                     break;                      /* To next line */
10400                     }
10401                 htext++;                        /* Regular character */
10402                 }
10403             continue;
10404             } /* topic text line */
10405         if (sim_isdigit (*htext)) {             /* Topic heading */
10406             TOPIC **children;
10407             TOPIC *newt;
10408             char nbuf[100];
10409 
10410             n = 0;
10411             start = htext;
10412             while (sim_isdigit (*htext))
10413                 n += (n * 10) + (*htext++ - '0');
10414             if ((htext == start) || !n) {
10415 #ifndef SUNLINT
10416                 FAIL (SCPE_ARG, Invalid topic heading, htext);
10417 #endif /* ifndef SUNLINT */
10418                 }
10419             if (n <= topic->level) {            /* Find level for new topic */
10420                 while (n <= topic->level)
10421                     topic = topic->parent;
10422                 }
10423             else {
10424                 if (n > topic->level +1) {      /* Skipping down more than 1 */
10425 #ifndef SUNLINT
10426                     FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10427 #endif /* ifndef SUNLINT */
10428                     }
10429                 }
10430             while (*htext && (*htext != '\n') && sim_isspace (*htext))
10431                 htext++;
10432             if (!*htext || (*htext == '\n')) {  /* Name missing */
10433 #ifndef SUNLINT
10434                 FAIL (SCPE_ARG, Missing topic name, htext);
10435 #endif /* ifndef SUNLINT */
10436                 }
10437             start = htext;
10438             while (*htext && (*htext != '\n'))
10439                 htext++;
10440             if (start == htext) {               /* Name NULL */
10441 #ifndef SUNLINT
10442                 FAIL (SCPE_ARG, Null topic name, htext);
10443 #endif /* ifndef SUNLINT */
10444                 }
10445             excluded = FALSE;
10446             if (*start == '?') {                /* Conditional topic? */
10447                 size_t n = 0;
10448                 start++;
10449                 while (sim_isdigit (*start))    /* Get param # */
10450                     n += (n * 10) + (*start++ - '0');
10451                 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX) {
10452 #ifndef SUNLINT
10453                     FAIL (SCPE_ARG, Invalid parameter number, start);
10454 #endif /* ifndef SUNLINT */
10455                     }
10456                 while (n > vsnum)               /* Get arg pointer if not cached */
10457                     vstrings[vsnum++] = va_arg (ap, char *);
10458                 end = vstrings[n-1];            /* Check for True */
10459                 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10460                     excluded = TRUE;            /* False, skip topic this time */
10461                     if (*htext)
10462                         htext++;
10463                     continue;
10464                     }
10465                 }
10466             newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10467             if (!newt) {
10468 #ifndef SUNLINT
10469                 FAIL (SCPE_MEM, No memory, NULL);
10470 #endif /* ifndef SUNLINT */
10471                 }
10472             newt->title = (char *) malloc ((htext - start)+1);
10473             if (!newt->title) {
10474                 FREE (newt);
10475 #ifndef SUNLINT
10476                 FAIL (SCPE_MEM, No memory, NULL);
10477 #endif /* ifndef SUNLINT */
10478                 }
10479             memcpy (newt->title, start, htext - start);
10480             newt->title[htext - start] = '\0';
10481             if (*htext)
10482                 htext++;
10483 
10484             if (newt->title[0] == '$')
10485                 newt->flags |= HLP_MAGIC_TOPIC;
10486 
10487             children = (TOPIC **) realloc (topic->children,
10488                                            (topic->kids +1) * sizeof (TOPIC *));
10489             if (!children) {
10490                 FREE (newt->title);
10491                 FREE (newt);
10492 #ifndef SUNLINT
10493                 FAIL (SCPE_MEM, No memory, NULL);
10494 #endif /* ifndef SUNLINT */
10495                 }
10496             topic->children = children;
10497             topic->children[topic->kids++] = newt;
10498             newt->level = n;
10499             newt->parent = topic;
10500             n = strlen (newt->title);
10501             if (n > topic->kidwid)
10502                 topic->kidwid = n;
10503             sprintf (nbuf, ".%u", topic->kids);
10504             n = strlen (topic->label) + strlen (nbuf) + 1;
10505             newt->label = (char *) malloc (n);
10506             if (!newt->label) {
10507                 FREE (newt->title);
10508                 topic->children[topic->kids -1] = NULL;
10509                 FREE (newt);
10510 #ifndef SUNLINT
10511                 FAIL (SCPE_MEM, No memory, NULL);
10512 #endif /* ifndef SUNLINT */
10513                 }
10514             sprintf (newt->label, "%s%s", topic->label, nbuf);
10515             topic = newt;
10516             continue;
10517             } /* digits introducing a topic */
10518         if (*htext == ';') {                    /* Comment */
10519             while (*htext && *htext != '\n')
10520                 htext++;
10521             continue;
10522             }
10523 #ifndef SUNLINT
10524         FAIL (SCPE_ARG, Unknown line type, htext);     /* Unknown line */
10525 #endif /* ifndef SUNLINT */
10526         } /* htext not at end */
10527     memset (vstrings, 0, VSMAX * sizeof (char *));
10528     vsnum = 0;
10529     } /* all strings */
10530 
10531 return topic;
10532 }
10533 
10534 /*
10535  * Create prompt string - top thru current topic
10536  * Add prompt at end.
10537  */
10538 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
     /* [previous][next][first][last][top][bottom][index][help] */
10539 {
10540 char *prefix;
10541 char *newp, *newt;
10542 
10543 if (topic->level == 0) {
10544     prefix = (char *) calloc (2,1);
10545     if (!prefix) {
10546 #ifndef SUNLINT
10547         FAIL (SCPE_MEM, No memory, NULL);
10548 #endif /* ifndef SUNLINT */
10549         }
10550     prefix[0] = '\n';
10551     }
10552 else
10553     prefix = helpPrompt (topic->parent, "", oneword);
10554 
10555 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
10556                         strlen (pstring) +1);
10557 if (!newp) {
10558     FREE (prefix);
10559 #ifndef SUNLINT
10560     FAIL (SCPE_MEM, No memory, NULL);
10561 #endif /* ifndef SUNLINT */
10562     }
10563 strcpy (newp, prefix);
10564 if (topic->children) {
10565     if (topic->level != 0)
10566         strcat (newp, " ");
10567     newt = (topic->flags & HLP_MAGIC_TOPIC)?
10568             topic->title+1: topic->title;
10569     if (oneword) {
10570         char *np = newp + strlen (newp);
10571         while (*newt) {
10572             *np++ = blankch (*newt)? '_' : *newt;
10573             newt++;
10574             }
10575         *np = '\0';
10576         }
10577     else
10578         strcat (newp, newt);
10579     if (*pstring && *pstring != '?')
10580         strcat (newp, " ");
10581     }
10582 strcat (newp, pstring);
10583 FREE (prefix);
10584 return newp;
10585 }
10586 
10587 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
     /* [previous][next][first][last][top][bottom][index][help] */
10588 {
10589 char tbuf[CBUFSIZE];
10590 size_t i, skiplines;
10591 #ifdef _WIN32
10592 FILE *tmp;
10593 char *tmpnam;
10594 
10595 do {
10596     int fd;
10597     tmpnam = _tempnam (NULL, "simh");
10598     fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
10599     if (fd != -1) {
10600         tmp = _fdopen (fd, "w+");
10601         break;
10602         }
10603     } while (1);
10604 #else
10605 FILE *tmp = tmpfile();
10606 #endif
10607 
10608 if (!tmp) {
10609     fprintf (st, "Unable to create temporary file: %s\n", strerror (errno));
10610     return;
10611     }
10612 
10613 if (topic->title)
10614     fprintf (st, "%s\n", topic->title+1);
10615 
10616 skiplines = 0;
10617 if (topic->title) {
10618   if (!strcmp (topic->title+1, "Registers")) {
10619       fprint_reg_help (tmp, dptr) ;
10620       skiplines = 1;
10621       }
10622   else
10623       if (!strcmp (topic->title+1, "Set commands")) {
10624           fprint_set_help (tmp, dptr);
10625           skiplines = 3;
10626           }
10627       else
10628           if (!strcmp (topic->title+1, "Show commands")) {
10629               fprint_show_help (tmp, dptr);
10630               skiplines = 3;
10631               }
10632   }
10633 rewind (tmp);
10634 
10635 /* Discard leading blank lines/redundant titles */
10636 
10637 for (i =0; i < skiplines; i++)
10638     if (fgets (tbuf, sizeof (tbuf), tmp)) {};
10639 
10640 while (fgets (tbuf, sizeof (tbuf), tmp)) {
10641     if (tbuf[0] != '\n')
10642         fputs ("    ", st);
10643     fputs (tbuf, st);
10644     }
10645 fclose (tmp);
10646 #ifdef _WIN32
10647 remove (tmpnam);
10648 FREE (tmpnam);
10649 #endif
10650 return;
10651 }
10652 /* Flatten and display help for those who say they prefer it. */
10653 
10654 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10655                                UNIT *uptr, int32 flag,
10656                                TOPIC *topic, va_list ap )
10657 {
10658 size_t i;
10659 
10660 if (topic->flags & HLP_MAGIC_TOPIC) {
10661     fprintf (st, "\n%s ", topic->label);
10662     displayMagicTopic (st, dptr, topic);
10663     }
10664 else
10665     fprintf (st, "\n%s %s\n", topic->label, topic->title);
10666 
10667 /*
10668  * Topic text (for magic topics, follows for explanations)
10669  * It's possible/reasonable for a magic topic to have no text.
10670  */
10671 
10672 if (topic->text)
10673     fputs (topic->text, st);
10674 
10675 for (i = 0; i < topic->kids; i++)
10676     displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
10677 
10678 return SCPE_OK;
10679 }
10680 
10681 #define HLP_MATCH_AMBIGUOUS (~0u)
10682 #define HLP_MATCH_WILDCARD  (~1U)
10683 #define HLP_MATCH_NONE      0
10684 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
     /* [previous][next][first][last][top][bottom][index][help] */
10685 {
10686 size_t i, match;
10687 char cbuf[CBUFSIZE], *cptr;
10688 
10689 if (!strcmp (token, "*"))
10690     return HLP_MATCH_WILDCARD;
10691 
10692 match = 0;
10693 for (i = 0; i < topic->kids; i++) {
10694     strcpy (cbuf,topic->children[i]->title +
10695             ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10696     cptr = cbuf;
10697     while (*cptr) {
10698         if (blankch (*cptr)) {
10699             *cptr++ = '_';
10700             }
10701         else {
10702             *cptr = (char)toupper (*cptr);
10703             cptr++;
10704             }
10705         }
10706     if (!strcmp (cbuf, token))      /* Exact Match */
10707         return i+1;
10708     if (!strncmp (cbuf, token, strlen (token))) {
10709         if (match)
10710             return HLP_MATCH_AMBIGUOUS;
10711         match = i+1;
10712         }
10713     }
10714 return match;
10715 }
10716 
10717 /* Main help routine */
10718 
10719 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10720                   UNIT *uptr, int32 flag,
10721                   const char *help, const char *cptr, va_list ap)
10722 {
10723 
10724 TOPIC top;
10725 TOPIC *topic = &top;
10726 int failed;
10727 size_t match;
10728 size_t i;
10729 const char *p;
10730 t_bool flat_help = FALSE;
10731 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
10732 
10733 static const char attach_help[] = { " ATTACH" };
10734 static const char  brief_help[] = { "%s help.  Type <CR> to exit, HELP for navigation help.\n" };
10735 static const char onecmd_help[] = { "%s help.\n" };
10736 static const char   help_help[] = {
10737 
10738     /****|***********************80 column width guide********************************/
10739     "    To see more HELP information, type the listed subtopic name.  To move\n"
10740     "    up a level, just type <CR>.  To review the current subtopic, type \"?\".\n"
10741     "    To view all subtopics, type \"*\".  To exit type \"EXIT\", \"^C\", or \"^D\".\n\n"
10742     };
10743 
10744 memset (&top, 0, sizeof(top));
10745 top.parent = &top;
10746 if ((failed = setjmp (help_env)) != 0) {
10747     fprintf (stderr, "\nHELP was unable to process HELP for this device.\n"
10748                      "Error in block %u line %u: %s\n"
10749                      "%s%*.*s%s\n",
10750              (int)help_where.block, (int)help_where.line, help_where.error,
10751              help_where.prox ? "Near '" : "",
10752              help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
10753              help_where.prox ? help_where.prox : "",
10754                  help_where.prox ? "'" : "");
10755     cleanHelp (&top);
10756     return failed;
10757     }
10758 
10759 /* Compile string into navigation tree */
10760 
10761 /* Root */
10762 
10763 if (dptr) {
10764     p = dptr->name;
10765     flat_help = (dptr->flags & DEV_FLATHELP) != 0;
10766     }
10767 else
10768     p = sim_name;
10769 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
10770 if (!top.title)
10771   {
10772     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10773              __func__, __FILE__, __LINE__);
10774 #if defined(USE_BACKTRACE)
10775 # ifdef SIGUSR2
10776     (void)raise(SIGUSR2);
10777     /*NOTREACHED*/ /* unreachable */
10778 # endif /* ifdef SIGUSR2 */
10779 #endif /* if defined(USE_BACKTRACE) */
10780     abort();
10781   }
10782 for (i = 0; p[i]; i++ )
10783     top.title[i] = (char)toupper (p[i]);
10784 top.title[i] = '\0';
10785 if (flag & SCP_HELP_ATTACH)
10786     strcpy (top.title+i, attach_help);
10787 
10788 top.label = (char *) malloc (sizeof ("1"));
10789 if (!top.label)
10790   {
10791     fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
10792              __func__, __FILE__, __LINE__);
10793 #if defined(USE_BACKTRACE)
10794 # ifdef SIGUSR2
10795     (void)raise(SIGUSR2);
10796     /*NOTREACHED*/ /* unreachable */
10797 # endif /* ifdef SIGUSR2 */
10798 #endif /* if defined(USE_BACKTRACE) */
10799     abort();
10800   }
10801 strcpy (top.label, "1");
10802 
10803 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
10804 
10805 if (flat_help) {
10806     flag |= SCP_HELP_FLAT;
10807     if (sim_ttisatty())
10808         fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
10809     else
10810         fprintf (st, "%s help.\n", top.title);
10811     }
10812 else
10813     fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
10814 
10815 /* Add text and subtopics */
10816 
10817 (void) buildHelp (&top, dptr, uptr, help, ap);
10818 
10819 /* Go to initial topic if provided */
10820 
10821 while (cptr && *cptr) {
10822     cptr = get_glyph (cptr, gbuf, 0);
10823     if (!gbuf[0])
10824         break;
10825     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10826         fprintf (st, "\n");
10827         fputs (help_help, st);
10828         break;
10829         }
10830     match =  matchHelpTopicName (topic, gbuf);
10831     if (match == HLP_MATCH_WILDCARD) {
10832         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10833         cleanHelp (&top);
10834         return SCPE_OK;
10835         }
10836     if (match == HLP_MATCH_AMBIGUOUS) {
10837         fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
10838         break;
10839         }
10840     if (match == HLP_MATCH_NONE) {
10841         fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
10842         break;
10843         }
10844     topic = topic->children[match-1];
10845     }
10846 cptr = NULL;
10847 
10848 if (flat_help) {
10849     displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10850     cleanHelp (&top);
10851     return SCPE_OK;
10852     }
10853 
10854 /* Interactive loop displaying help */
10855 
10856 while (TRUE) {
10857     char *pstring;
10858     const char *prompt[2] = {"? ", "Subtopic? "};
10859 
10860     /* Some magic topic names for help from data structures */
10861 
10862     if (topic->flags & HLP_MAGIC_TOPIC) {
10863         fputc ('\n', st);
10864         displayMagicTopic (st, dptr, topic);
10865         }
10866     else
10867         fprintf (st, "\n%s\n", topic->title);
10868 
10869     /* Topic text (for magic topics, follows for explanations)
10870      * It's possible/reasonable for a magic topic to have no text.
10871      */
10872 
10873     if (topic->text)
10874         fputs (topic->text, st);
10875 
10876     if (topic->kids) {
10877         size_t w = 0;
10878         char *p;
10879         char tbuf[CBUFSIZE];
10880 
10881         fprintf (st, "\n    Additional information available:\n\n");
10882         for (i = 0; i < topic->kids; i++) {
10883             strcpy (tbuf, topic->children[i]->title +
10884                     ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10885             for (p = tbuf; *p; p++) {
10886                 if (blankch (*p))
10887                     *p = '_';
10888                 }
10889             w += 4 + topic->kidwid;
10890             if (w > 80) {
10891                 w = 4 + topic->kidwid;
10892                 fputc ('\n', st);
10893                 }
10894             fprintf (st, "    %-*s", topic->kidwid, tbuf);
10895             }
10896         fprintf (st, "\n\n");
10897         if (flag & SCP_HELP_ONECMD) {
10898             pstring = helpPrompt (topic, "", TRUE);
10899             fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
10900             FREE (pstring);
10901             break;
10902             }
10903         }
10904 
10905     if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
10906         break;
10907 
10908   reprompt:
10909     if (!cptr || !*cptr) {
10910         if (topic->kids == 0)
10911             topic = topic->parent;
10912         pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
10913 
10914         cptr = read_line_p (pstring+1, cbuf, sizeof (cbuf), stdin);
10915         FREE (pstring);
10916         if ((cptr != NULL) &&                   /* Got something? */
10917             ((0 == strcmp (cptr, "\x04")) ||    /* was it a bare ^D? */
10918              (0 == strcmp (cptr, "\x1A"))))     /* was it a bare ^Z? */
10919             cptr = NULL;                        /* These are EOF synonyms */
10920         }
10921 
10922     if (!cptr)                              /* EOF, exit help */
10923         break;
10924 
10925     cptr = get_glyph (cptr, gbuf, 0);
10926     if (!strcmp (gbuf, "*")) {              /* Wildcard */
10927         displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10928         gbuf[0] = '\0';                     /* Displayed all subtopics, go up */
10929         }
10930     if (!gbuf[0]) {                         /* Blank, up a level */
10931         if (topic->level == 0)
10932             break;
10933         topic = topic->parent;
10934         continue;
10935         }
10936     if (!strcmp (gbuf, "?"))                /* ?, repaint current topic */
10937         continue;
10938     if (!strcmp (gbuf, "HELP")) {           /* HELP (about help) */
10939         fputs (help_help, st);
10940         goto reprompt;
10941         }
10942     if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT"))   /* EXIT (help) */
10943         break;
10944 
10945     /* String - look for that topic */
10946 
10947     if (!topic->kids) {
10948         fprintf (st, "No additional help at this level.\n");
10949         cptr = NULL;
10950         goto reprompt;
10951         }
10952     match = matchHelpTopicName (topic, gbuf);
10953     if (match == HLP_MATCH_AMBIGUOUS) {
10954         fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
10955         cptr = NULL;
10956         goto reprompt;
10957         }
10958 
10959     if (match == HLP_MATCH_NONE) {
10960         fprintf (st, "Help for %s is not available\n", gbuf);
10961         cptr = NULL;
10962         goto reprompt;
10963         }
10964     /* Found, display subtopic */
10965 
10966     topic = topic->children[match-1];
10967     }
10968 
10969 /* Free structures and return */
10970 
10971 cleanHelp (&top);
10972 
10973 return SCPE_OK;
10974 }
10975 
10976 /* variable argument list shell - most commonly used */
10977 
10978 t_stat scp_help (FILE *st, DEVICE *dptr,
     /* [previous][next][first][last][top][bottom][index][help] */
10979                  UNIT *uptr, int32 flag,
10980                  const char *help, const char *cptr, ...)
10981 {
10982 t_stat r;
10983 va_list ap;
10984 
10985 va_start (ap, cptr);
10986 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
10987 va_end (ap);
10988 
10989 return r;
10990 }
10991 
10992 #if defined(_MSC_VER)
10993 # pragma warning(pop)
10994 #endif

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