1 /* scp.c: simulator control program
2
3 Copyright (c) 1993-2016 Robert M Supnik
4 Copyright (c) 2021 Jeffrey H. Johnson <trnsz@pobox.com>
5 Copyright (c) 2021 The DPS8M Development Team
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 and/or sell copies of the Software, and to permit persons to whom the
12 Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name of Robert M Supnik shall not be
25 used in advertising or otherwise to promote the sale, use or other dealings
26 in this Software without prior written authorization from Robert M Supnik.
27 */
28
29 /* Macros and data structures */
30
31 #include "sim_defs.h"
32 #include "sim_disk.h"
33 #include "sim_tape.h"
34 #include "sim_sock.h"
35 #include <signal.h>
36 #include <ctype.h>
37 #include <time.h>
38 #include <math.h>
39 #if defined(_WIN32)
40 # ifndef WIN32_LEAN_AND_MEAN
41 # define WIN32_LEAN_AND_MEAN
42 # endif
43 # if defined(_MSC_VER)
44 # pragma warning(push, 3)
45 # endif
46 # include <direct.h>
47 # include <io.h>
48 # include <fcntl.h>
49 #else
50 # include <unistd.h>
51 #endif
52 #include <sys/stat.h>
53 #include <setjmp.h>
54 #include <limits.h>
55
56 #include "linehistory.h"
57
58 #if defined(__APPLE__)
59 # include <sys/types.h>
60 # include <sys/sysctl.h>
61 #endif
62
63 #include <uv.h>
64
65 #define DBG_CTR 0
66
67 #include "../dps8/dps8.h"
68 #include "../dps8/dps8_cpu.h"
69 #include "../dps8/ver.h"
70 #include "../dps8/sysdefs.h"
71
72 #include "../decNumber/decContext.h"
73 #include "../decNumber/decNumberLocal.h"
74
75 #include "dispatch.h"
76
77 #ifndef MAX
78 # define MAX(a,b) (((a) >= (b)) ? (a) : (b))
79 #endif
80
81 /* search logical and boolean ops */
82
83 #define SCH_OR 0 /* search logicals */
84 #define SCH_AND 1
85 #define SCH_XOR 2
86 #define SCH_E 0 /* search booleans */
87 #define SCH_N 1
88 #define SCH_G 2
89 #define SCH_L 3
90 #define SCH_EE 4
91 #define SCH_NE 5
92 #define SCH_GE 6
93 #define SCH_LE 7
94
95 #define MAX_DO_NEST_LVL 20 /* DO cmd nesting level */
96 #define SRBSIZ 1024 /* save/restore buffer */
97 #define SIM_BRK_INILNT 4096 /* bpt tbl length */
98 #define SIM_BRK_ALLTYP 0xFFFFFFFB
99 #define UPDATE_SIM_TIME \
100 if (1) { \
101 int32 _x; \
102 if (sim_clock_queue == QUEUE_LIST_END) \
103 _x = noqueue_time; \
104 else \
105 _x = sim_clock_queue->time; \
106 sim_time = sim_time + (_x - sim_interval); \
107 sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \
108 if (sim_clock_queue == QUEUE_LIST_END) \
109 noqueue_time = sim_interval; \
110 else \
111 sim_clock_queue->time = sim_interval; \
112 } \
113 else \
114 (void)0
115
116 #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])
117 #define SZ_R(rp) \
118 (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])
119 #define SZ_LOAD(sz,v,mb,j) \
120 if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \
121 else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \
122 else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \
123 else v = *(((t_uint64 *) mb) + ((uint32) j));
124 #define SZ_STORE(sz,v,mb,j) \
125 if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \
126 else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \
127 else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \
128 else *(((t_uint64 *) mb) + ((uint32) j)) = v;
129 #define GET_SWITCHES(cp) \
130 if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW
131 #define GET_RADIX(val,dft) \
132 if (sim_switches & SWMASK ('O')) val = 8; \
133 else if (sim_switches & SWMASK ('D')) val = 10; \
134 else if (sim_switches & SWMASK ('H')) val = 16; \
135 else val = dft;
136
137 /* Asynch I/O support */
138 t_bool sim_asynch_enabled = FALSE;
139
140 /* The per-simulator init routine is a weak global that defaults to NULL
141 The other per-simulator pointers can be overrriden by the init routine */
142
143 extern void (*sim_vm_init) (void);
144 char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;
145 void (*sim_vm_post) (t_bool from_scp) = NULL;
146 CTAB *sim_vm_cmd = NULL;
147 void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL;
148 void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;
149 t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL;
150 t_value (*sim_vm_pc_value) (void) = NULL;
151 t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL;
152 t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL;
153
154 /* Prototypes */
155
156 /* Set and show command processors */
157
158 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
159 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
160 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
161 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
162 t_stat ssh_break (FILE *st, const char *cptr, int32 flg);
163 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr);
164 t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
165 t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
166 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
167 t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
168 t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
169 t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
170 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
171 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
172 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
173 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
174 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
175 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
176 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cprr);
177 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
178 t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
179 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
180 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
181 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
182 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
183 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
184 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
185 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks);
186 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag);
187 t_stat sim_save (FILE *sfile);
188 t_stat sim_rest (FILE *rfile);
189
190 /* Breakpoint package */
191
192 t_stat sim_brk_init (void);
193 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act);
194 t_stat sim_brk_clr (t_addr loc, int32 sw);
195 t_stat sim_brk_clrall (int32 sw);
196 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
197 t_stat sim_brk_showall (FILE *st, int32 sw);
198 CONST char *sim_brk_getact (char *buf, int32 size);
199 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp);
200 char *sim_brk_clract (void);
201
202 FILE *stdnul;
203
204 /* Command support routines */
205
206 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
207 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr);
208 int32 test_search (t_value *val, SCHTAB *schptr);
209 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char);
210 int32 get_switches (const char *cptr);
211 CONST char *get_sim_sw (CONST char *cptr);
212 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
213 t_value get_rval (REG *rptr, uint32 idx);
214 void put_rval (REG *rptr, uint32 idx, t_value val);
215 void fprint_help (FILE *st);
216 void fprint_stopped (FILE *st, t_stat r);
217 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
218 void fprint_sep (FILE *st, int32 *tokens);
219 char *read_line (char *ptr, int32 size, FILE *stream);
220 char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream);
221 REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr);
222 char *sim_trim_endspc (char *cptr);
223
224 /* Forward references */
225
226 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr);
227 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);
228 t_bool qdisable (DEVICE *dptr);
229 t_stat attach_err (UNIT *uptr, t_stat stat);
230 t_stat detach_all (int32 start_device, t_bool shutdown);
231 t_stat assign_device (DEVICE *dptr, const char *cptr);
232 t_stat deassign_device (DEVICE *dptr);
233 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr);
234 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
235 REG *lowr, REG *highr, uint32 lows, uint32 highs);
236 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);
237 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx);
238 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
239 t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
240 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
241 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
242 UNIT *uptr, int32 dfltinc);
243 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
244 t_stat step_svc (UNIT *ptr);
245 t_stat expect_svc (UNIT *ptr);
246 t_stat set_on (int32 flag, CONST char *cptr);
247 t_stat set_verify (int32 flag, CONST char *cptr);
248 t_stat set_message (int32 flag, CONST char *cptr);
249 t_stat set_quiet (int32 flag, CONST char *cptr);
250 t_stat set_asynch (int32 flag, CONST char *cptr);
251 t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
252 t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label);
253 void int_handler (int signal);
254 t_stat set_prompt (int32 flag, CONST char *cptr);
255 t_stat sim_set_asynch (int32 flag, CONST char *cptr);
256 t_stat sim_set_environment (int32 flag, CONST char *cptr);
257 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr);
258
259 /* Global data */
260
261 DEVICE *sim_dflt_dev = NULL;
262 UNIT *sim_clock_queue = QUEUE_LIST_END;
263 int32 sim_interval = 0;
264 int32 sim_switches = 0;
265 FILE *sim_ofile = NULL;
266 SCHTAB *sim_schrptr = FALSE;
267 SCHTAB *sim_schaptr = FALSE;
268 DEVICE *sim_dfdev = NULL;
269 UNIT *sim_dfunit = NULL;
270 DEVICE **sim_internal_devices = NULL;
271 uint32 sim_internal_device_count = 0;
272 int32 sim_opt_out = 0;
273 int32 sim_is_running = 0;
274 t_bool sim_processing_event = FALSE;
275 uint32 sim_brk_summ = 0;
276 uint32 sim_brk_types = 0;
277 BRKTYPTAB *sim_brk_type_desc = NULL; /* type descriptions */
278 uint32 sim_brk_dflt = 0;
279 uint32 sim_brk_match_type;
280 t_addr sim_brk_match_addr;
281 char *sim_brk_act[MAX_DO_NEST_LVL];
282 char *sim_brk_act_buf[MAX_DO_NEST_LVL];
283 BRKTAB **sim_brk_tab = NULL;
284 int32 sim_brk_ent = 0;
285 int32 sim_brk_lnt = 0;
286 int32 sim_brk_ins = 0;
287 int32 sim_iglock = 0;
288 int32 sim_nolock = 0;
289 int32 sim_quiet = 0;
290 int32 sim_randstate = 0;
291 int32 sim_randompst = 0;
292 int32 sim_step = 0;
293 static double sim_time;
294 static uint32 sim_rtime;
295 static int32 noqueue_time;
296 volatile int32 stop_cpu = 0;
297 static char **sim_argv;
298 t_value *sim_eval = NULL;
299 static t_value sim_last_val;
300 static t_addr sim_last_addr;
301 FILE *sim_log = NULL; /* log file */
302 FILEREF *sim_log_ref = NULL; /* log file file reference */
303 FILE *sim_deb = NULL; /* debug file */
304 FILEREF *sim_deb_ref = NULL; /* debug file file reference */
305 int32 sim_deb_switches = 0; /* debug switches */
306 struct timespec sim_deb_basetime; /* debug timestamp relative base time */
307 char *sim_prompt = NULL; /* prompt string */
308 static FILE *sim_gotofile; /* the currently open do file */
309 static int32 sim_goto_line[MAX_DO_NEST_LVL+1]; /* the current line number in the currently open do file */
310 static int32 sim_do_echo = 0; /* the echo status of the currently open do file */
311 static int32 sim_show_message = 1; /* the message display status of the currently open do file */
312 static int32 sim_on_inherit = 0; /* the inherit status of on state and conditions when executing do files */
313 static int32 sim_do_depth = 0;
314
315 static int32 sim_on_check[MAX_DO_NEST_LVL+1];
316 static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1];
317 static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE];
318 static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1];
319 static const char *sim_do_label[MAX_DO_NEST_LVL+1];
320
321 t_stat sim_last_cmd_stat; /* Command Status */
322
323 static SCHTAB sim_stabr; /* Register search specifier */
324 static SCHTAB sim_staba; /* Memory search specifier */
325
326 static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) };
327 static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0) };
328 static const char *sim_si64 = "64b data";
329 static const char *sim_sa64 = "32b addresses";
330 const char *sim_savename = sim_name; /* simulator Name used in SAVE/RESTORE images */
331
332 /* Tables and strings */
333
334 const char save_vercur[] = "V4.1";
335 const char save_ver40[] = "V4.0";
336 const char save_ver35[] = "V3.5";
337 const char save_ver32[] = "V3.2";
338 const char save_ver30[] = "V3.0";
339 const struct scp_error {
340 const char *code;
341 const char *message;
342 } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] =
343 {{"NXM", "Address space exceeded"},
344 {"UNATT", "Unit not attached"},
345 {"IOERR", "I/O error"},
346 {"CSUM", "Checksum error"},
347 {"FMT", "Format error"},
348 {"NOATT", "Unit not attachable"},
349 {"OPENERR", "File open error"},
350 {"MEM", "Memory exhausted"},
351 {"ARG", "Invalid argument"},
352 {"STEP", "Step expired"},
353 {"UNK", "Unknown command"},
354 {"RO", "Read only argument"},
355 {"INCOMP", "Command not completed"},
356 {"STOP", "Simulation stopped"},
357 {"EXIT", "Goodbye"},
358 {"TTIERR", "Console input I/O error"},
359 {"TTOERR", "Console output I/O error"},
360 {"EOF", "End of file"},
361 {"REL", "Relocation error"},
362 {"NOPARAM", "No settable parameters"},
363 {"ALATT", "Unit already attached"},
364 {"TIMER", "Hardware timer error"},
365 {"SIGERR", "Signal handler setup error"},
366 {"TTYERR", "Console terminal setup error"},
367 {"SUB", "Subscript out of range"},
368 {"NOFNC", "Command not allowed"},
369 {"UDIS", "Unit disabled"},
370 {"NORO", "Read only operation not allowed"},
371 {"INVSW", "Invalid switch"},
372 {"MISVAL", "Missing value"},
373 {"2FARG", "Too few arguments"},
374 {"2MARG", "Too many arguments"},
375 {"NXDEV", "Non-existent device"},
376 {"NXUN", "Non-existent unit"},
377 {"NXREG", "Non-existent register"},
378 {"NXPAR", "Non-existent parameter"},
379 {"NEST", "Nested DO command limit exceeded"},
380 {"IERR", "Internal error"},
381 {"MTRLNT", "Invalid magtape record length"},
382 {"LOST", "Console Telnet connection lost"},
383 {"TTMO", "Console Telnet connection timed out"},
384 {"STALL", "Console Telnet output stall"},
385 {"AFAIL", "Assertion failed"},
386 {"INVREM", "Invalid remote console command"},
387 {"NOTATT", "Not attached"},
388 {"EXPECT", "Expect matched"},
389 {"REMOTE", "Remote console command"},
390 };
391
392 const size_t size_map[] = { sizeof (int8),
393 sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
394 , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
395 };
396
397 const t_value width_mask[] = { 0,
398 0x1, 0x3, 0x7, 0xF,
399 0x1F, 0x3F, 0x7F, 0xFF,
400 0x1FF, 0x3FF, 0x7FF, 0xFFF,
401 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
402 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
403 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
404 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
405 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF,
406 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
407 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
408 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
409 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
410 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
411 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
412 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
413 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
414 };
415
416 static const char simh_help[] =
417 /***************** 80 character line width template *************************/
418 "1Commands\n"
419 #define HLP_RESET "*Commands Resetting Devices"
420 /***************** 80 character line width template *************************/
421 "2Resetting Devices\n"
422 " The RESET command (abbreviation RE) resets a device or the entire simulator\n"
423 " to a predefined condition. If switch -p is specified, the device is reset\n"
424 " to its power-up state:\n\n"
425 "++RESET reset all devices\n"
426 "++RESET -p powerup all devices\n"
427 "++RESET ALL reset all devices\n"
428 "++RESET <device> reset specified device\n\n"
429 " Typically, RESET stops any in-progress I/O operation, clears any interrupt\n"
430 " request, and returns the device to a quiescent state. It does not clear\n"
431 " main memory or affect I/O connections.\n"
432 #define HLP_EXAMINE "*Commands Examining_and_Changing_State"
433 #define HLP_IEXAMINE "*Commands Examining_and_Changing_State"
434 #define HLP_DEPOSIT "*Commands Examining_and_Changing_State"
435 #define HLP_IDEPOSIT "*Commands Examining_and_Changing_State"
436 /***************** 80 character line width template *************************/
437 "2Examining and Changing State\n"
438 " There are four commands to examine and change state:\n\n"
439 "++EXAMINE (abbreviated E) examines state\n"
440 "++DEPOSIT (abbreviated D) changes state\n"
441 "++IEXAMINE (interactive examine, abbreviated IE) examines state and allows\n"
442 "++++the user to interactively change it\n"
443 "++IDEPOSIT (interactive deposit, abbreviated ID) allows the user to\n"
444 "++++interactively change state\n\n"
445 " All four commands take the form\n\n"
446 "++command {modifiers} <object list>\n\n"
447 " Deposit must also include a deposit value at the end of the command.\n\n"
448 " There are four kinds of modifiers: switches, device/unit name, search\n"
449 " specifier, and for EXAMINE, output file. Switches have been described\n"
450 " previously. A device/unit name identifies the device and unit whose\n"
451 " address space is to be examined or modified. If no device is specified,\n"
452 " the CPU (main memory)is selected; if a device but no unit is specified,\n"
453 " unit 0 of the device is selected.\n\n"
454 " The search specifier provides criteria for testing addresses or registers\n"
455 " to see if they should be processed. A specifier consists of a logical\n"
456 " operator, a relational operator, or both, optionally separated by spaces.\n\n"
457 "++{<logical op> <value>} <relational op> <value>\n\n"
458 /***************** 80 character line width template *************************/
459 " where the logical operator is & (and), | (or), or ^ (exclusive or), and the\n"
460 " relational operator is = or == (equal), ! or != (not equal), >= (greater\n"
461 " than or equal), > (greater than), <= (less than or equal), or < (less than).\n"
462 " If a logical operator is specified without a relational operator, it is\n"
463 " ignored. If a relational operator is specified without a logical operator,\n"
464 " no logical operation is performed. All comparisons are unsigned.\n\n"
465 " The output file modifier redirects command output to a file instead of the\n"
466 " console. An output file modifier consists of @ followed by a valid file\n"
467 " name.\n\n"
468 " Modifiers may be specified in any order. If multiple modifiers of the\n"
469 " same type are specified, later modifiers override earlier modifiers. Note\n"
470 " that if the device/unit name comes after the search specifier, the search\n"
471 " values will interpreted in the radix of the CPU, rather than of the\n"
472 " device/unit.\n\n"
473 " The \"object list\" consists of one or more of the following, separated by\n"
474 " commas:\n\n"
475 /***************** 80 character line width template *************************/
476 "++register the specified register\n"
477 "++register[sub1-sub2] the specified register array locations,\n"
478 "++++++++ starting at location sub1 up to and\n"
479 "++++++++ including location sub2\n"
480 "++register[sub1/length] the specified register array locations,\n"
481 "++++++++ starting at location sub1 up to but\n"
482 "++++++++ not including sub1+length\n"
483 "++register[ALL] all locations in the specified register\n"
484 "++++++++ array\n"
485 "++register1-register2 all the registers starting at register1\n"
486 "++++++++ up to and including register2\n"
487 "++address the specified location\n"
488 "++address1-address2 all locations starting at address1 up to\n"
489 "++++++++ and including address2\n"
490 "++address/length all location starting at address up to\n"
491 "++++++++ but not including address+length\n"
492 "++STATE all registers in the device\n"
493 "++ALL all locations in the unit\n"
494 "++$ the last value displayed by an EXAMINE\n"
495 "++++++++ command interpreted as an address\n"
496 "3Switches\n"
497 " Switches can be used to control the format of display information:\n\n"
498 /***************** 80 character line width template *************************/
499 "++-a display as ASCII\n"
500 "++-c display as character string\n"
501 "++-m display as instruction mnemonics\n"
502 "++-o display as octal\n"
503 "++-d display as decimal\n"
504 "++-h display as hexadecimal\n\n"
505 " The simulators typically accept symbolic input (see documentation with each\n"
506 " simulator).\n\n"
507 "3Examples\n"
508 " Examples:\n\n"
509 "++ex 1000-1100 examine 1000 to 1100\n"
510 "++de PC 1040 set PC to 1040\n"
511 "++ie 40-50 interactively examine 40:50\n"
512 "++ie >1000 40-50 interactively examine the subset\n"
513 "+++++++++ of locations 40:50 that are >1000\n"
514 "++ex rx0 50060 examine 50060, RX unit 0\n"
515 "++ex rx sbuf[3-6] examine SBUF[3] to SBUF[6] in RX\n"
516 "++de all 0 set main memory to 0\n"
517 "++de &77>0 0 set all addresses whose low order\n"
518 "+++++++++ bits are non-zero to 0\n"
519 "++ex -m @memdump.txt 0-7777 dump memory to file\n\n"
520 " Note: to terminate an interactive command, simply type a bad value\n"
521 " (eg, XYZ) when input is requested.\n"
522 #define HLP_EVALUATE "*Commands Evaluating_Instructions"
523 /***************** 80 character line width template *************************/
524 "2Evaluating Instructions\n"
525 " The EVAL command evaluates a symbolic expression and returns the equivalent\n"
526 " numeric value. This is useful for obtaining numeric arguments for a search\n"
527 " command:\n\n"
528 "++EVAL <expression>\n"
529 /***************** 80 character line width template *************************/
530 "2Saving and Restoring State\n"
531 #define HLP_SAVE "*Commands Saving_and_Restoring_State SAVE"
532 "3SAVE\n"
533 " The SAVE command (abbreviation SA) save the complete state of the simulator\n"
534 " to a file. This includes the contents of main memory and all registers,\n"
535 " and the I/O connections of devices:\n\n"
536 "++SAVE <filename>\n\n"
537 #define HLP_RESTORE "*Commands Saving_and_Restoring_State RESTORE"
538 "3RESTORE\n"
539 " The RESTORE command (abbreviation REST, alternately GET) restores a\n"
540 " previously saved simulator state:\n\n"
541 "++RESTORE <filename>\n"
542 "4Switches\n"
543 " Switches can influence the output and behavior of the RESTORE command\n\n"
544 "++-Q Suppresses version warning messages\n"
545 "++-D Suppress detaching and attaching devices during a restore\n"
546 "++-F Overrides timestamp validation during restore\n"
547 "\n"
548 "4Notes:\n"
549 " 1) SAVE file format compresses zeroes to minimize file size.\n"
550 " 2) The simulator can't restore active incoming telnet sessions to\n"
551 " multiplexer devices, but the listening ports will be restored across a\n"
552 " save/restore.\n"
553 /***************** 80 character line width template *************************/
554 "2Running A Simulated Program\n"
555 #define HLP_RUN "*Commands Running_A_Simulated_Program RUN"
556 "3RUN\n"
557 " The RUN command (abbreviated RU) resets all devices, deposits its argument\n"
558 " (if given) in the PC, and starts execution. If no argument is given,\n"
559 " execution starts at the current PC.\n"
560 #define HLP_GO "*Commands Running_A_Simulated_Program GO"
561 "3GO\n"
562 " The GO command does not reset devices, deposits its argument (if given)\n"
563 " in the PC, and starts execution. If no argument is given, execution\n"
564 " starts at the current PC.\n"
565 #define HLP_CONTINUE "*Commands Running_A_Simulated_Program CONTINUE"
566 "3CONTINUE\n"
567 " The CONT command (abbreviated CO) does not reset devices and resumes\n"
568 " execution at the current PC.\n"
569 #define HLP_STEP "*Commands Running_A_Simulated_Program STEP"
570 "3STEP\n"
571 " The STEP command (abbreviated S) resumes execution at the current PC for\n"
572 " the number of instructions given by its argument. If no argument is\n"
573 " supplied, one instruction is executed.\n"
574 "4Switches\n"
575 " If the STEP command is invoked with the -T switch, the step command will\n"
576 " cause execution to run for microseconds rather than instructions.\n"
577 #define HLP_NEXT "*Commands Running_A_Simulated_Program NEXT"
578 "3NEXT\n"
579 " The NEXT command (abbreviated N) resumes execution at the current PC for\n"
580 " one instruction, attempting to execute through a subroutine calls.\n"
581 " If the next instruction to be executed is not a subroutine call,\n"
582 " one instruction is executed.\n"
583 #define HLP_BOOT "*Commands Running_A_Simulated_Program BOOT"
584 "3BOOT\n"
585 " The BOOT command (abbreviated BO) resets all devices and bootstraps the\n"
586 " device and unit given by its argument. If no unit is supplied, unit 0 is\n"
587 " bootstrapped. The specified unit must be attached.\n"
588 /***************** 80 character line width template *************************/
589 "2Stopping The Simulator\n"
590 " Programs run until the simulator detects an error or stop condition, or\n"
591 " until the user forces a stop condition.\n"
592 "3Simulator Detected Stop Conditions\n"
593 " These simulator-detected conditions stop simulation:\n\n"
594 "++- HALT instruction. If a HALT instruction is decoded, simulation stops.\n"
595 "++- Breakpoint. The simulator may support breakpoints (see below).\n"
596 "++- I/O error. If an I/O error occurs during simulation of an I/O\n"
597 "+++operation, and the device stop-on-I/O-error flag is set, simulation\n"
598 "+++usually stops.\n\n"
599 "++- Processor condition. Certain processor conditions can stop\n"
600 "+++simulation; these are described with the individual simulators.\n"
601 "3User Specified Stop Conditions\n"
602 " Typing the interrupt character stops simulation. The interrupt character\n"
603 " is defined by the WRU (where are you) console option and is initially set\n"
604 " to 005 (^E).\n\n"
605 /***************** 80 character line width template *************************/
606 #define HLP_BREAK "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
607 #define HLP_NOBREAK "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK"
608 "4Breakpoints\n"
609 " A simulator may offer breakpoint capability. A simulator may define\n"
610 " breakpoints of different types, identified by letter (for example, E for\n"
611 " execution, R for read, W for write, etc). At the moment, most simulators\n"
612 " support only E (execution) breakpoints.\n\n"
613 " Associated with a breakpoint are a count and, optionally, one or more\n"
614 " actions. Each time the breakpoint is taken, the associated count is\n"
615 " decremented. If the count is less than or equal to 0, the breakpoint\n"
616 " occurs; otherwise, it is deferred. When the breakpoint occurs, the\n"
617 " optional actions are automatically executed.\n\n"
618 " A breakpoint is set by the BREAK or the SET BREAK commands:\n\n"
619 "++BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n"
620 "++SET BREAK {-types} {<addr range>{[count]},{addr range...}}{;action;action...}\n\n"
621 " If no type is specified, the simulator-specific default breakpoint type\n"
622 " (usually E for execution) is used. If no address range is specified, the\n"
623 " current PC is used. As with EXAMINE and DEPOSIT, an address range may be a\n"
624 " single address, a range of addresses low-high, or a relative range of\n"
625 " address/length.\n"
626 /***************** 80 character line width template *************************/
627 "5Displaying Breakpoints\n"
628 " Currently set breakpoints can be displayed with the SHOW BREAK command:\n\n"
629 "++SHOW {-C} {-types} BREAK {ALL|<addr range>{,<addr range>...}}\n\n"
630 " Locations with breakpoints of the specified type are displayed.\n\n"
631 " The -C switch displays the selected breakpoint(s) formatted as commands\n"
632 " which may be subsequently used to establish the same breakpoint(s).\n\n"
633 "5Removing Breakpoints\n"
634 " Breakpoints can be cleared by the NOBREAK or the SET NOBREAK commands.\n"
635 "5Examples\n"
636 "++BREAK set E break at current PC\n"
637 "++BREAK -e 200 set E break at 200\n"
638 "++BREAK 2000/2[2] set E breaks at 2000,2001 with count = 2\n"
639 "++BREAK 100;EX AC;D MQ 0 set E break at 100 with actions EX AC and\n"
640 "+++++++++D MQ 0\n"
641 "++BREAK 100; delete action on break at 100\n\n"
642 /***************** 80 character line width template *************************/
643 "2Connecting and Disconnecting Devices\n"
644 " Except for main memory and network devices, units are simulated as\n"
645 " unstructured binary disk files in the host file system. Before using a\n"
646 " simulated unit, the user must specify the file to be accessed by that unit.\n"
647 #define HLP_ATTACH "*Commands Connecting_and_Disconnecting_Devices ATTACH"
648 "3ATTACH\n"
649 " The ATTACH (abbreviation AT) command associates a unit and a file:\n"
650 "++ATTACH <unit> <filename>\n\n"
651 " Some devices have more detailed or specific help available with:\n\n"
652 "++HELP <device> ATTACH\n\n"
653 "4Switches\n"
654 "5-n\n"
655 " If the -n switch is specified when an attach is executed, a new file is\n"
656 " created, and an appropriate message is printed.\n"
657 "5-e\n"
658 " If the file does not exist, and the -e switch was not specified, a new\n"
659 " file is created, and an appropriate message is printed. If the -e switch\n"
660 " was specified, a new file is not created, and an error message is printed.\n"
661 "5-r\n"
662 " If the -r switch is specified, or the file is write protected, ATTACH tries\n"
663 " to open the file read only. If the file does not exist, or the unit does\n"
664 " not support read only operation, an error occurs. Input-only devices, such\n"
665 " as paper-tape readers, and devices with write lock switches, such as disks\n"
666 " and tapes, support read only operation; other devices do not. If a file is\n"
667 " attached read only, its contents can be examined but not modified.\n"
668 "5-q\n"
669 " If the -q switch is specified when creating a new file (-n) or opening one\n"
670 " read only (-r), the message announcing this fact is suppressed.\n"
671 "5-f\n"
672 " For simulated magnetic tapes, the ATTACH command can specify the format of\n"
673 " the attached tape image file:\n\n"
674 "++ATTACH -f <tape_unit> <format> <filename>\n\n"
675 " The currently supported tape image file formats are:\n\n"
676 "++SIMH SIMH simulator format\n"
677 "++E11 E11 simulator format\n"
678 "++TPC TPC format\n"
679 "++P7B Pierce simulator 7-track format\n\n"
680 /***************** 80 character line width template *************************/
681 " For some simulated disk devices, the ATTACH command can specify the format\n"
682 " of the attached disk image file:\n\n"
683 "++ATTACH -f <disk_unit> <format> <filename>\n\n"
684 " The currently supported disk image file formats are:\n\n"
685 "++SIMH SIMH simulator format\n"
686 "++RAW platform specific access to physical disk or\n"
687 "++ CDROM drives\n"
688 " The disk format can also be set with the SET command prior to ATTACH:\n\n"
689 "++SET <disk_unit> FORMAT=<format>\n"
690 "++ATT <disk_unit> <filename>\n\n"
691 /***************** 80 character line width template *************************/
692 " The format of an attached tape or disk file can be displayed with the SHOW\n"
693 " command:\n"
694 "++SHOW <unit> FORMAT\n"
695 " For Telnet-based terminal emulation devices, the ATTACH command associates\n"
696 " the master unit with a TCP/IP listening port:\n\n"
697 "++ATTACH <unit> <port>\n\n"
698 " The port is a decimal number between 1 and 65535 that is not already used\n"
699 " other TCP/IP applications.\n"
700 /***************** 80 character line width template *************************/
701 #define HLP_DETACH "*Commands Connecting_and_Disconnecting_Devices DETACH"
702 "3DETACH\n"
703 " The DETACH (abbreviation DET) command breaks the association between a unit\n"
704 " and a file, port, or network device:\n\n"
705 "++DETACH ALL detach all units\n"
706 "++DETACH <unit> detach specified unit\n"
707 " The EXIT command performs an automatic DETACH ALL.\n"
708 #define HLP_SET "*Commands SET"
709 "2SET\n"
710 /***************** 80 character line width template *************************/
711 #define HLP_SET_CONSOLE "*Commands SET CONSOLE"
712 "3Console\n"
713 "+set console arg{,arg...} set console options\n"
714 "+set console WRU specify console drop to simh character\n"
715 "+set console BRK specify console Break character\n"
716 "+set console DEL specify console delete character\n"
717 "+set console PCHAR specify console printable characters\n"
718 "+set console SPEED=speed{*factor}\n"
719 "++++++++ specify console input data rate\n"
720 "+set console TELNET=port specify console telnet port\n"
721 "+set console TELNET=LOG=log_file\n"
722 "++++++++ specify console telnet logging to the\n"
723 "++++++++ specified destination {LOG,STDOUT,STDERR,\n"
724 "++++++++ DEBUG or filename)\n"
725 "+set console TELNET=NOLOG disables console telnet logging\n"
726 "+set console TELNET=BUFFERED[=bufsize]\n"
727 "++++++++ specify console telnet buffering\n"
728 "+set console TELNET=NOBUFFERED\n"
729 "++++++++ disables console telnet buffering\n"
730 "+set console TELNET=UNBUFFERED\n"
731 "++++++++ disables console telnet buffering\n"
732 "+set console NOTELNET disable console telnet\n"
733 /***************** 80 character line width template *************************/
734 #define HLP_SET_REMOTE "*Commands SET REMOTE"
735 "3Remote\n"
736 "+set remote TELNET=port specify remote console telnet port\n"
737 "+set remote NOTELNET disables remote console\n"
738 "+set remote CONNECTIONS=n specify number of concurrent remote\n"
739 "++++++++ console sessions\n"
740 "+set remote TIMEOUT=n specify number of seconds without input\n"
741 "++++++++ before automatic continue\n"
742 #define HLP_SET_LOG "*Commands SET Log"
743 "3Log\n"
744 " Interactions with the simulator session (at the \"sim>\" prompt\n"
745 " can be recorded to a log file\n\n"
746 "+set log log_file specify the log destination\n"
747 "++++++++ (STDOUT,DEBUG or filename)\n"
748 "+set nolog disables any currently active logging\n"
749 "4Switches\n"
750 " By default, log output is written at the end of the specified log file.\n"
751 " A new log file can created if the -N switch is used on the command line.\n\n"
752 " By default, log output is written in text mode. The log file can be\n"
753 " opened for binary mode writing if the -B switch is used on the command line.\n"
754 #define HLP_SET_DEBUG "*Commands SET Debug"
755 /***************** 80 character line width template *************************/
756 "3Debug\n"
757 "+set debug debug_file specify the debug destination\n"
758 "++++++++ (STDOUT,STDERR,LOG or filename)\n"
759 "+set nodebug disables any currently active debug output\n"
760 "4Switches\n"
761 " Debug message output contains a timestamp which indicates the number of\n"
762 " simulated instructions which have been executed prior to the debug event.\n\n"
763 " Debug message output can be enhanced to contain additional, potentially\n"
764 " useful information.\n"
765 "5-T\n"
766 " The -T switch causes debug output to contain a time of day displayed\n"
767 " as hh:mm:ss.msec.\n"
768 "5-A\n"
769 " The -A switch causes debug output to contain a time of day displayed\n"
770 " as seconds.msec.\n"
771 "5-R\n"
772 " The -R switch causes the time of day displayed due to the -T or -A\n"
773 " switches to be relative to the start time of debugging. If neither\n"
774 " -T or -A is explicitly specified, -T is implied.\n"
775 "5-P\n"
776 " The -P switch adds the output of the PC (Program Counter) to each debug\n"
777 " message.\n"
778 "5-N\n"
779 " The -N switch causes a new/empty file to be written to. The default\n"
780 " is to append to an existing debug log file.\n"
781 "5-D\n"
782 " The -D switch causes data blob output to also display the data as\n"
783 " RADIX-50 characters.\n"
784 "5-E\n"
785 " The -E switch causes data blob output to also display the data as\n"
786 " EBCDIC characters.\n"
787 #define HLP_SET_BREAK "*Commands SET Breakpoints"
788 "3Breakpoints\n"
789 "+set break <list> set breakpoints\n"
790 "+set nobreak <list> clear breakpoints\n"
791 /***************** 80 character line width template *************************/
792 #define HLP_SET_ENVIRON "*Commands SET Asynch"
793 "3Environment\n"
794 "+set environment name=val set environment variable\n"
795 "+set environment name clear environment variable\n"
796 #define HLP_SET_ON "*Commands SET Command_Status_Trap_Dispatching"
797 "3Command Status Trap Dispatching\n"
798 "+set on enables error checking after command\n"
799 "++++++++ execution\n"
800 "+set noon disables error checking after command\n"
801 "++++++++ execution\n"
802 "+set on inherit enables inheritance of ON state and\n"
803 "++++++++ actions into do command files\n"
804 "+set on noinherit disables inheritance of ON state and\n"
805 "++++++++ actions into do command files\n"
806 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
807 #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display"
808 "3Command Execution Display\n"
809 "+set verify re-enables display of command file\n"
810 "++++++++ processed commands\n"
811 "+set verbose re-enables display of command file\n"
812 "++++++++ processed commands\n"
813 "+set noverify disables display of command file processed\n"
814 "++++++++ commands\n"
815 "+set noverbose disables display of command file processed\n"
816 "++++++++ commands\n"
817 #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display"
818 "3Command Error Status Display\n"
819 "+set message re-enables display of command file error\n"
820 "++++++++ messages\n"
821 "+set nomessage disables display of command file error\n"
822 "++++++++ messages\n"
823 #define HLP_SET_QUIET "*Commands SET Command_Output_Display"
824 "3Command Output Display\n"
825 "+set quiet disables suppression of some output and\n"
826 "++++++++ messages\n"
827 "+set noquiet re-enables suppression of some output and\n"
828 "++++++++ messages\n"
829 #define HLP_SET_PROMPT "*Commands SET Command_Prompt"
830 "3Command Prompt\n"
831 "+set prompt \"string\" sets an alternate simulator prompt string\n"
832 "3Device and Unit\n"
833 "+set <dev> OCT|DEC|HEX set device display radix\n"
834 "+set <dev> ENABLED enable device\n"
835 "+set <dev> DISABLED disable device\n"
836 "+set <dev> DEBUG{=arg} set device debug flags\n"
837 "+set <dev> NODEBUG={arg} clear device debug flags\n"
838 "+set <dev> arg{,arg...} set device parameters (see show modifiers)\n"
839 "+set <unit> ENABLED enable unit\n"
840 "+set <unit> DISABLED disable unit\n"
841 "+set <unit> arg{,arg...} set unit parameters (see show modifiers)\n"
842 "+help <dev> set displays the device specific set commands\n"
843 "++++++++ available\n"
844 /***************** 80 character line width template *************************/
845 #define HLP_SHOW "*Commands SHOW"
846 "2SHOW\n"
847 "+sh{ow} {-c} br{eak} <list> show breakpoints\n"
848 "+sh{ow} con{figuration} show configuration\n"
849 "+sh{ow} cons{ole} {arg} show console options\n"
850 "+sh{ow} dev{ices} show devices\n"
851 "+sh{ow} fea{tures} show system devices with descriptions\n"
852 "+sh{ow} m{odifiers} show modifiers for all devices\n"
853 "+sh{ow} s{how} show SHOW commands for all devices\n"
854 "+sh{ow} n{ames} show logical names\n"
855 "+sh{ow} q{ueue} show event queue\n"
856 "+sh{ow} ti{me} show simulated time\n"
857 "+sh{ow} b{uildinfo} show build compilation information\n"
858 "+sh{ow} ve{rsion} show simulator version\n"
859 "+sh{ow} p{rom} show PROM initialization data\n"
860 "+sh{ow} <dev> RADIX show device display radix\n"
861 "+sh{ow} <dev> DEBUG show device debug flags\n"
862 "+sh{ow} <dev> MODIFIERS show device modifiers\n"
863 "+sh{ow} <dev> NAMES show device logical name\n"
864 "+sh{ow} <dev> SHOW show device SHOW commands\n"
865 "+sh{ow} <dev> {arg,...} show device parameters\n"
866 "+sh{ow} <unit> {arg,...} show unit parameters\n"
867 "+sh{ow} clocks show calibrated timers\n"
868 "+sh{ow} on show on condition actions\n"
869 "+h{elp} <dev> show displays the device specific show commands\n"
870 "++++++++ available\n"
871 #define HLP_SHOW_CONFIG "*Commands SHOW"
872 #define HLP_SHOW_DEVICES "*Commands SHOW"
873 #define HLP_SHOW_FEATURES "*Commands SHOW"
874 #define HLP_SHOW_QUEUE "*Commands SHOW"
875 #define HLP_SHOW_TIME "*Commands SHOW"
876 #define HLP_SHOW_MODIFIERS "*Commands SHOW"
877 #define HLP_SHOW_NAMES "*Commands SHOW"
878 #define HLP_SHOW_SHOW "*Commands SHOW"
879 #define HLP_SHOW_VERSION "*Commands SHOW"
880 #define HLP_SHOW_BUILDINFO "*Commands SHOW"
881 #define HLP_SHOW_PROM "*Commands SHOW"
882 #define HLP_SHOW_DEFAULT "*Commands SHOW"
883 #define HLP_SHOW_CONSOLE "*Commands SHOW"
884 #define HLP_SHOW_REMOTE "*Commands SHOW"
885 #define HLP_SHOW_BREAK "*Commands SHOW"
886 #define HLP_SHOW_LOG "*Commands SHOW"
887 #define HLP_SHOW_DEBUG "*Commands SHOW"
888 #define HLP_SHOW_CLOCKS "*Commands SHOW"
889 #define HLP_SHOW_ON "*Commands SHOW"
890 #define HLP_SHOW_SEND "*Commands SHOW"
891 #define HLP_SHOW_EXPECT "*Commands SHOW"
892 #define HLP_HELP "*Commands HELP"
893 /***************** 80 character line width template *************************/
894 "2HELP\n"
895 "+h{elp} type this message\n"
896 "+h{elp} <command> type help for command\n"
897 "+h{elp} <dev> type help for device\n"
898 "+h{elp} <dev> registers type help for device register variables\n"
899 "+h{elp} <dev> attach type help for device specific ATTACH command\n"
900 "+h{elp} <dev> set type help for device specific SET commands\n"
901 "+h{elp} <dev> show type help for device specific SHOW commands\n"
902 "+h{elp} <dev> <command> type help for device specific <command> command\n"
903 /***************** 80 character line width template *************************/
904 "2Altering The Simulated Configuration\n"
905 " In most simulators, the SET <device> DISABLED command removes the\n"
906 " specified device from the configuration. A DISABLED device is invisible\n"
907 " to running programs. The device can still be RESET, but it cannot be\n"
908 " ATTAChed, DETACHed, or BOOTed. SET <device> ENABLED restores a disabled\n"
909 " device to a configuration.\n\n"
910 " Most multi-unit devices allow units to be enabled or disabled:\n\n"
911 "++SET <unit> ENABLED\n"
912 "++SET <unit> DISABLED\n\n"
913 " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n"
914 #define HLP_ASSIGN "*Commands Logical_Names"
915 #define HLP_DEASSIGN "*Commands Logical_Names"
916 "2Logical Names\n"
917 " The standard device names can be supplemented with logical names. Logical\n"
918 " names must be unique within a simulator (that is, they cannot be the same\n"
919 " as an existing device name). To assign a logical name to a device:\n\n"
920 "++ASSIGN <device> <log-name> assign log-name to device\n\n"
921 " To remove a logical name:\n\n"
922 "++DEASSIGN <device> remove logical name\n\n"
923 " To show the current logical name assignment:\n\n"
924 "++SHOW <device> NAMES show logical name, if any\n\n"
925 " To show all logical names:\n\n"
926 "++SHOW NAMES\n\n"
927 /***************** 80 character line width template *************************/
928 #define HLP_DO "*Commands Executing_Command_Files"
929 "2Executing Command Files\n"
930 " The simulator can execute command files with the DO command:\n\n"
931 "++DO <filename> {arguments...} execute commands in file\n\n"
932 " The DO command allows command files to contain substitutable arguments.\n"
933 " The string %%n, where n is between 1 and 9, is replaced with argument n\n"
934 " from the DO command line. The string %%0 is replaced with <filename>.\n"
935 " The sequences \\%% and \\\\ are replaced with the literal characters %% and \\,\n"
936 " respectively. Arguments with spaces can be enclosed in matching single\n"
937 " or double quotation marks.\n\n"
938 " DO commands may be nested up to ten invocations deep.\n\n"
939 "3Switches\n"
940 " If the switch -v is specified, the commands in the file are echoed before\n"
941 " they are executed.\n\n"
942 " If the switch -e is specified, command processing (including nested command\n"
943 " invocations) will be aborted if a command error is encountered.\n"
944 " (Simulation stop never abort processing; use ASSERT to catch unexpected\n"
945 " stops.) Without the switch, all errors except ASSERT failures will be\n"
946 " ignored, and command processing will continue.\n\n"
947 " If the switch -o is specified, the on conditions and actions from the\n"
948 " calling command file will be inherited in the command file being invoked.\n"
949 " If the switch -q is specified, the quiet mode will be explicitly enabled\n"
950 " for the called command file, otherwise quiet mode is inherited from the\n"
951 " calling context.\n"
952 /***************** 80 character line width template *************************/
953 #define HLP_GOTO "*Commands Executing_Command_Files GOTO"
954 "3GOTO\n"
955 " Commands in a command file execute in sequence until either an error\n"
956 " trap occurs (when a command completes with an error status), or when an\n"
957 " explict request is made to start command execution elsewhere with the\n"
958 " GOTO command:\n\n"
959 "++GOTO <label>\n\n"
960 " Labels are lines in a command file which the first non whitespace\n"
961 " character is a \":\". The target of a goto is the first matching label\n"
962 " in the current do command file which is encountered. Since labels\n"
963 " don't do anything else besides being the targets of goto's, they could\n"
964 " also be used to provide comments in do command files.\n\n"
965 "4Examples\n\n"
966 "++:: This is a comment\n"
967 "++echo Some Message to Output\n"
968 "++:Target\n"
969 "++:: This is a comment\n"
970 "++GOTO Target\n\n"
971 #define HLP_RETURN "*Commands Executing_Command_Files RETURN"
972 /***************** 80 character line width template *************************/
973 "3RETURN\n"
974 " The RETURN command causes the current procedure call to be restored to the\n"
975 " calling context, possibly returning a specific return status.\n"
976 " If no return status is specified, the return status from the last command\n"
977 " executed will be returned. The calling context may have ON traps defined\n"
978 " which may redirect command flow in that context.\n\n"
979 "++return return from command file with last command status\n"
980 "++return {-Q} <status> return from command file with specific status\n\n"
981 " The status return can be any numeric value or one of the standard SCPE_\n"
982 " condition names.\n\n"
983 " The -Q switch on the RETURN command will cause the specified status to\n"
984 " be returned, but normal error status message printing to be suppressed.\n"
985 "4Condition Names\n"
986 " The available standard SCPE_ condition names are\n"
987 "5 NXM\n"
988 " Address space exceeded\n"
989 "5 UNATT\n"
990 " Unit not attached\n"
991 "5 IOERR\n"
992 " I/O error\n"
993 "5 CSUM\n"
994 " Checksum error\n"
995 "5 FMT\n"
996 " Format error\n"
997 "5 NOATT\n"
998 " Unit not attachable\n"
999 "5 OPENERR\n"
1000 " File open error\n"
1001 "5 MEM\n"
1002 " Memory exhausted\n"
1003 "5 ARG\n"
1004 " Invalid argument\n"
1005 "5 STEP\n"
1006 " Step expired\n"
1007 "5 UNK\n"
1008 " Unknown command\n"
1009 "5 RO\n"
1010 " Read only argument\n"
1011 "5 INCOMP\n"
1012 " Command not completed\n"
1013 "5 STOP\n"
1014 " Simulation stopped\n"
1015 "5 EXIT\n"
1016 " Goodbye\n"
1017 "5 TTIERR\n"
1018 " Console input I/O error\n"
1019 "5 TTOERR\n"
1020 " Console output I/O error\n"
1021 "5 EOF\n"
1022 " End of file\n"
1023 "5 REL\n"
1024 " Relocation error\n"
1025 "5 NOPARAM\n"
1026 " No settable parameters\n"
1027 "5 ALATT\n"
1028 " Unit already attached\n"
1029 "5 TIMER\n"
1030 " Hardware timer error\n"
1031 "5 SIGERR\n"
1032 " Signal handler setup error\n"
1033 "5 TTYERR\n"
1034 " Console terminal setup error\n"
1035 "5 NOFNC\n"
1036 " Command not allowed\n"
1037 "5 UDIS\n"
1038 " Unit disabled\n"
1039 "5 NORO\n"
1040 " Read only operation not allowed\n"
1041 "5 INVSW\n"
1042 " Invalid switch\n"
1043 "5 MISVAL\n"
1044 " Missing value\n"
1045 "5 2FARG\n"
1046 " Too few arguments\n"
1047 "5 2MARG\n"
1048 " Too many arguments\n"
1049 "5 NXDEV\n"
1050 " Non-existent device\n"
1051 "5 NXUN\n"
1052 " Non-existent unit\n"
1053 "5 NXREG\n"
1054 " Non-existent register\n"
1055 "5 NXPAR\n"
1056 " Non-existent parameter\n"
1057 "5 NEST\n"
1058 " Nested DO command limit exceeded\n"
1059 "5 IERR\n"
1060 " Internal error\n"
1061 "5 MTRLNT\n"
1062 " Invalid magtape record length\n"
1063 "5 LOST\n"
1064 " Console Telnet connection lost\n"
1065 "5 TTMO\n"
1066 " Console Telnet connection timed out\n"
1067 "5 STALL\n"
1068 " Console Telnet output stall\n"
1069 "5 AFAIL\n"
1070 " Assertion failed\n"
1071 "5 INVREM\n"
1072 " Invalid remote console command\n"
1073 #define HLP_SHIFT "*Commands Executing_Command_Files SHIFT"
1074 "3SHIFT\n"
1075 "++shift shift the command file's positional parameters\n"
1076 #define HLP_CALL "*Commands Executing_Command_Files CALL"
1077 "3CALL\n"
1078 "++call transfer control to a labeled subroutine\n"
1079 #define HLP_ON "*Commands Executing_Command_Files ON"
1080 "3ON\n"
1081 "++on <condition> <action> perform action(s) after condition\n"
1082 "++on <condition> clear action for specific condition\n"
1083 #define HLP_PROCEED "*Commands Executing_Command_Files PROCEED"
1084 #define HLP_IGNORE "*Commands Executing_Command_Files PROCEED"
1085 /***************** 80 character line width template *************************/
1086 "3PROCEED/IGNORE\n"
1087 " The PROCEED or IGNORE commands do nothing. They are potentially useful\n"
1088 " placeholders for an ON action condition which should be explicitly ignored\n"
1089 "++proceed continue command file execution without doing anything\n"
1090 "++ignore continue command file execution without doing anything\n"
1091 #define HLP_ECHO "*Commands Executing_Command_Files Displaying_Arbitrary_Text"
1092 /***************** 80 character line width template *************************/
1093 "3Displaying Arbitrary Text\n"
1094 " The ECHO command is a useful way of annotating command files. ECHO prints\n"
1095 " out its arguments on the console (and log):\n\n"
1096 "++ECHO <string> output string to console\n\n"
1097 " If there is no argument, ECHO prints a blank line on the console. This\n"
1098 " may be used to provide spacing in the console display or log.\n"
1099 /***************** 80 character line width template *************************/
1100 #define HLP_SEND "*Commands Executing_Command_Files Injecting_Console_Input"
1101 /***************** 80 character line width template *************************/
1102 "3Injecting Console Input\n"
1103 " The SEND command provides a way to insert input into the console device of\n"
1104 " a simulated system as if it was entered by a user.\n\n"
1105 "++SEND {-t} {after=nn,}{delay=nn,}\"<string>\"\n\n"
1106 " The string argument must be delimited by quote characters. Quotes may\n"
1107 " be either single or double but the opening and closing quote characters\n"
1108 " must match. Data in the string may contain escaped character strings.\n\n"
1109 " The SEND command can also insert input into any serial device on a\n"
1110 " simulated system as if it was entered by a user.\n\n"
1111 "++SEND {-t} <dev>:line {after=nn,}{delay=nn,}\"<string>\"\n\n"
1112 "4Delay\n"
1113 " Specifies a positive integer representing a minimal instruction delay\n"
1114 " between characters being sent. The value specified in a delay\n"
1115 " argument persists across SEND commands to the same device (console or\n"
1116 " serial device). The delay parameter can be set by itself with:\n\n"
1117 "++SEND DELAY=n\n\n"
1118 " The default value of the delay parameter is 1000.\n"
1119 /***************** 80 character line width template *************************/
1120 "4After\n"
1121 " Specifies a positive integer representing a minimal number of instructions\n"
1122 " which must execute before the first character in the string is sent.\n"
1123 " The value specified as the after parameter persists across SEND commands\n"
1124 " to the same device (console or serial device). The after parameter value\n"
1125 " can be set by itself with:\n\n"
1126 "++SEND AFTER=n\n\n"
1127 " If the after parameter isn't explicitly set, it defaults to the value of\n"
1128 " the delay parameter.\n"
1129 "4Escaping String Data\n"
1130 " The following character escapes are explicitly supported:\n"
1131 "++\\r Sends the ASCII Carriage Return character (Decimal value 13)\n"
1132 "++\\n Sends the ASCII Linefeed character (Decimal value 10)\n"
1133 "++\\f Sends the ASCII Formfeed character (Decimal value 12)\n"
1134 "++\\t Sends the ASCII Horizontal Tab character (Decimal value 9)\n"
1135 "++\\v Sends the ASCII Vertical Tab character (Decimal value 11)\n"
1136 "++\\b Sends the ASCII Backspace character (Decimal value 8)\n"
1137 "++\\\\ Sends the ASCII Backslash character (Decimal value 92)\n"
1138 "++\\' Sends the ASCII Single Quote character (Decimal value 39)\n"
1139 "++\\\" Sends the ASCII Double Quote character (Decimal value 34)\n"
1140 "++\\? Sends the ASCII Question Mark character (Decimal value 63)\n"
1141 "++\\e Sends the ASCII Escape character (Decimal value 27)\n"
1142 " as well as octal character values of the form:\n"
1143 "++\\n{n{n}} where each n is an octal digit (0-7)\n"
1144 " and hext character values of the form:\n"
1145 "++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n"
1146 "4Switches\n"
1147 " Switches can be used to influence the behavior of SEND commands\n\n"
1148 "5-t\n"
1149 " The -t switch indicates that the Delay and After values are in\n"
1150 " units of microseconds rather than instructions.\n"
1151 /***************** 80 character line width template *************************/
1152 #define HLP_EXPECT "*Commands Executing_Command_Files Reacting_To_Console_Output"
1153 /***************** 80 character line width template *************************/
1154 "3Reacting To Console Output\n"
1155 " The EXPECT command provides a way to stop execution and take actions\n"
1156 " when specific output has been generated by the simulated system.\n"
1157 "++EXPECT {dev:line} {[count]} {HALTAFTER=n,}\"<string>\" {actioncommand {; actioncommand}...}\n\n"
1158 " The string argument must be delimited by quote characters. Quotes may\n"
1159 " be either single or double but the opening and closing quote characters\n"
1160 " must match. Data in the string may contain escaped character strings.\n"
1161 " If a [count] is specified, the rule will match after the match string\n"
1162 " has matched count times.\n\n"
1163 " When multiple expect rules are defined with the same match string, they\n"
1164 " will match in the same order they were defined in.\n\n"
1165 " When expect rules are defined, they are evaluated agains recently\n"
1166 " produced output as each character is output to the device. Since this\n"
1167 " evaluation processing is done on each output character, rule matching\n"
1168 " is not specifically line oriented. If line oriented matching is desired\n"
1169 " then rules should be defined which contain the simulated system's line\n"
1170 " ending character sequence (i.e. \"\\r\\n\").\n"
1171 " Once data has matched any expect rule, that data is no longer eligible\n"
1172 " to match other expect rules which may already be defined.\n"
1173 " Data which is output prior to the definition of an expect rule is not\n"
1174 " eligible to be matched against.\n"
1175 /***************** 80 character line width template *************************/
1176 "4Switches\n"
1177 " Switches can be used to influence the behavior of EXPECT rules\n\n"
1178 "5-p\n"
1179 " EXPECT rules default to be one shot activities. That is a rule is\n"
1180 " automatically removed when it matches unless it is designated as a\n"
1181 " persistent rule by using a -p switch when the rule is defined.\n"
1182 "5-c\n"
1183 " If an expect rule is defined with the -c switch, it will cause all\n"
1184 " pending expect rules on the current device to be cleared when the rule\n"
1185 " matches data in the device output stream.\n"
1186 "5-r\n"
1187 " If an expect rule is defined with the -r switch, the string is interpreted\n"
1188 " as a regular expression applied to the output data stream. This regular\n"
1189 " expression may contain parentheses delimited sub-groups.\n\n"
1190 /***************** 80 character line width template *************************/
1191 " Regular expression support is not currently available on your environment.\n"
1192 " This simulator could use regular expression support provided by the\n"
1193 " Perl Compatible Regular Expression (PCRE) package if it was available\n"
1194 " when you simulator was compiled.\n"
1195 "5-i\n"
1196 " If a regular expression expect rule is defined with the -i switch,\n"
1197 " character matching for that expression will be case independent.\n"
1198 " The -i switch is only valid for regular expression expect rules.\n"
1199 "5-t\n"
1200 " The -t switch indicates that the value specified by the HaltAfter\n"
1201 " parameter are in units of microseconds rather than instructions.\n"
1202 "4Determining Which Output Matched\n"
1203 " When an expect rule matches data in the output stream, the rule which\n"
1204 " matched is recorded in the environment variable _EXPECT_MATCH_PATTERN.\n"
1205 " If the expect rule was a regular expression rule, then the environment\n"
1206 " variable _EXPECT_MATCH_GROUP_0 is set to the whole string which matched\n"
1207 " and if the match pattern had any parentheses delimited sub-groups, the\n"
1208 " environment variables _EXPECT_MATCH_PATTERN_1 thru _EXPECT_MATCH_PATTERN_n\n"
1209 " are set to the values within the string which matched the respective\n"
1210 " sub-groups.\n"
1211 /***************** 80 character line width template *************************/
1212 "4Escaping String Data\n"
1213 " The following character escapes are explicitly supported when NOT using\n"
1214 " regular expression match patterns:\n"
1215 "++\\r Expect the ASCII Carriage Return character (Decimal value 13)\n"
1216 "++\\n Expect the ASCII Linefeed character (Decimal value 10)\n"
1217 "++\\f Expect the ASCII Formfeed character (Decimal value 12)\n"
1218 "++\\t Expect the ASCII Horizontal Tab character (Decimal value 9)\n"
1219 "++\\v Expect the ASCII Vertical Tab character (Decimal value 11)\n"
1220 "++\\b Expect the ASCII Backspace character (Decimal value 8)\n"
1221 "++\\\\ Expect the ASCII Backslash character (Decimal value 92)\n"
1222 "++\\' Expect the ASCII Single Quote character (Decimal value 39)\n"
1223 "++\\\" Expect the ASCII Double Quote character (Decimal value 34)\n"
1224 "++\\? Expect the ASCII Question Mark character (Decimal value 63)\n"
1225 "++\\e Expect the ASCII Escape character (Decimal value 27)\n"
1226 " as well as octal character values of the form:\n"
1227 "++\\n{n{n}} where each n is an octal digit (0-7)\n"
1228 " and hext character values of the form:\n"
1229 "++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n"
1230 "4HaltAfter\n"
1231 " Specifies the number of instructions which should be executed before\n"
1232 " simulator instruction execution should stop. The default is to stop\n"
1233 " executing instructions immediately (i.e. HALTAFTER=0).\n"
1234 " The HaltAfter delay, once set, persists for all expect behaviors for\n"
1235 " that device.\n"
1236 " The HaltAfter parameter value can be set by itself with:\n\n"
1237 "++EXPECT HALTAFTER=n\n\n"
1238 " To avoid potentially unpredictable system hehavior that will happen\n"
1239 " if multiple expect rules are in effect and a haltafter value is large\n"
1240 " enough for more than one expect rule to match before an earlier haltafter\n"
1241 " delay has expired, only a single EXPECT rule can be defined if a non-zero\n"
1242 " HaltAfter parameter has been set.\n"
1243 /***************** 80 character line width template *************************/
1244 #define HLP_ASSERT "*Commands Executing_Command_Files Testing_Simulator_State"
1245 #define HLP_IF "*Commands Executing_Command_Files Testing_Simulator_State"
1246 "3Testing Simulator State\n"
1247 " There are two ways for a command file to examine simulator state and\n"
1248 " then take action based on that state:\n"
1249 "4ASSERT\n"
1250 " The ASSERT command tests a simulator state condition and halts command\n"
1251 " file execution if the condition is false:\n\n"
1252 "++ASSERT <Simulator State Expressions>\n\n"
1253 " If the indicated expression evaluates to false, the command completes\n"
1254 " with an AFAIL condition. By default, when a command file encounters a\n"
1255 " command which returns the AFAIL condition, it will exit the running\n"
1256 " command file with the AFAIL status to the calling command file. This\n"
1257 " behavior can be changed with the ON command as well as switches to the\n"
1258 " invoking DO command.\n\n"
1259 "5Examples:\n"
1260 " A command file might be used to bootstrap an operating system that\n"
1261 " halts after the initial load from disk. The ASSERT command is then\n"
1262 " used to confirm that the load completed successfully by examining the\n"
1263 " CPU's \"A\" register for the expected value:\n\n"
1264 "++; OS bootstrap command file\n"
1265 "++;\n"
1266 "++ATTACH DS0 os.disk\n"
1267 "++BOOT DS\n"
1268 "++; A register contains error code; 0 = good boot\n"
1269 "++ASSERT A=0\n"
1270 "++ATTACH MT0 sys.tape\n"
1271 "++ATTACH MT1 user.tape\n"
1272 "++RUN\n\n"
1273 /***************** 80 character line width template *************************/
1274 " In the example, if the A register is not 0, the \"ASSERT A=0\" command will\n"
1275 " be echoed, the command file will be aborted with an \"Assertion failed\"\n"
1276 " message. Otherwise, the command file will continue to bring up the\n"
1277 " operating system.\n"
1278 "4IF\n"
1279 " The IF command tests a simulator state condition and executes additional\n"
1280 " commands if the condition is true:\n\n"
1281 "++IF <Simulator State Expressions> commandtoprocess{; additionalcommandtoprocess}...\n\n"
1282 "5Examples:\n"
1283 " A command file might be used to bootstrap an operating system that\n"
1284 " halts after the initial load from disk. The ASSERT command is then\n"
1285 " used to confirm that the load completed successfully by examining the\n"
1286 " CPU's \"A\" register for the expected value:\n\n"
1287 "++; OS bootstrap command file\n"
1288 "++;\n"
1289 "++ATTACH DS0 os.disk\n"
1290 "++BOOT DS\n"
1291 "++; A register contains error code; 0 = good boot\n"
1292 "++IF NOT A=0 echo Boot failed - Failure Code; EX A; exit AFAIL\n"
1293 "++ATTACH MT0 sys.tape\n"
1294 "++ATTACH MT1 user.tape\n"
1295 "++RUN\n\n"
1296 /***************** 80 character line width template *************************/
1297 " In the example, if the A register is not 0, the message \"Boot failed -\n"
1298 " Failure Code:\" command will be displayed, the contents of the A register\n"
1299 " will be displayed and the command file will be aborted with an \"Assertion\n"
1300 " failed\" message. Otherwise, the command file will continue to bring up\n"
1301 " the operating system.\n"
1302 "4Conditional Expressions\n"
1303 " The IF and ASSERT commands evaluate two different forms of conditional\n"
1304 " expressions.:\n\n"
1305 "5Simulator State Expressions\n"
1306 " The values of simulator registers can be evaluated with:\n\n"
1307 "++{NOT} {<dev>} <reg>|<addr>{<logical-op><value>}<conditional-op><value>\n\n"
1308 " If <dev> is not specified, CPU is assumed. <reg> is a register (scalar\n"
1309 " or subscripted) belonging to the indicated device. <addr> is an address\n"
1310 " in the address space of the indicated device. The <conditional-op>\n"
1311 " and optional <logical-op> are the same as those used for \"search\n"
1312 " specifiers\" by the EXAMINE and DEPOSIT commands. The <value>s are\n"
1313 " expressed in the radix specified for <reg>, not in the radix for the\n"
1314 " device when referencing a register and when an address is referenced\n"
1315 " the device radix is used as the default.\n\n"
1316 " If the <logical-op> and <value> are specified, the target register value\n"
1317 " is first altered as indicated. The result is then compared to the\n"
1318 " <value> via the <conditional-op>. If the result is true, the additional\n"
1319 " command(s) are executed before proceeding to the next line in the command\n"
1320 " file. Otherwise, the next command in the command file is processed.\n\n"
1321 "5String Comparison Expressions\n"
1322 " String Values can be compared with:\n"
1323 "++{-i} {NOT} \"<string1>\" <compare-op> \"<string2>\"\n\n"
1324 " The -i switch, if present, causes comparisons to be case insensitive.\n"
1325 " <string1> and <string2> are quoted string values which may have\n"
1326 " environment variables substituted as desired.\n"
1327 " <compare-op> may be one of:\n\n"
1328 "++== - equal\n"
1329 "++EQU - equal\n"
1330 "++!= - not equal\n"
1331 "++NEQ - not equal\n"
1332 "++< - less than\n"
1333 "++LSS - less than\n"
1334 "++<= - less than or equal\n"
1335 "++LEQ - less than or equal\n"
1336 "++> - greater than\n"
1337 "++GTR - greater than\n"
1338 "++>= - greater than or equal\n"
1339 "++GEQ - greater than or equal\n\n"
1340 " Comparisons are generic. This means that if both string1 and string2 are\n"
1341 " comprised of all numeric digits, then the strings are converted to numbers\n"
1342 " and a numeric comparison is performed. For example: \"+1\" EQU \"1\" will be\n"
1343 " true.\n"
1344 /***************** 80 character line width template *************************/
1345 #define HLP_EXIT "*Commands Exiting_The_Simulator"
1346 "2Exiting The Simulator\n"
1347 " EXIT (synonyms QUIT and BYE) returns control to the operating system.\n"
1348 /***************** 80 character line width template *************************/
1349 #define HLP_SPAWN "*Commands Executing_System_Commands"
1350 "2Executing System Commands\n"
1351 " The simulator can execute operating system commands with the ! (spawn)\n"
1352 " command:\n\n"
1353 "++! execute local command interpreter\n"
1354 "++! <command> execute local host command\n"
1355 " If no operating system command is provided, the simulator attempts to\n"
1356 " launch the host operating system's command shell.\n"
1357 " The exit status from the command which was executed is set as the command\n"
1358 " completion status for the ! command. This may influence any enabled ON\n"
1359 " condition traps\n";
1360
1361 static CTAB cmd_table[] = {
1362 { "RESET", &reset_cmd, 0, HLP_RESET },
1363 { "EXAMINE", &exdep_cmd, EX_E, HLP_EXAMINE },
1364 { "IEXAMINE", &exdep_cmd, EX_E+EX_I, HLP_IEXAMINE },
1365 { "DEPOSIT", &exdep_cmd, EX_D, HLP_DEPOSIT },
1366 { "IDEPOSIT", &exdep_cmd, EX_D+EX_I, HLP_IDEPOSIT },
1367 { "EVALUATE", &eval_cmd, 0, HLP_EVALUATE },
1368 { "RUN", &run_cmd, RU_RUN, HLP_RUN, NULL, &run_cmd_message },
1369 { "GO", &run_cmd, RU_GO, HLP_GO, NULL, &run_cmd_message },
1370 { "STEP", &run_cmd, RU_STEP, HLP_STEP, NULL, &run_cmd_message },
1371 { "NEXT", &run_cmd, RU_NEXT, HLP_NEXT, NULL, &run_cmd_message },
1372 { "CONTINUE", &run_cmd, RU_CONT, HLP_CONTINUE, NULL, &run_cmd_message },
1373 { "BOOT", &run_cmd, RU_BOOT, HLP_BOOT, NULL, &run_cmd_message },
1374 { "BREAK", &brk_cmd, SSH_ST, HLP_BREAK },
1375 { "NOBREAK", &brk_cmd, SSH_CL, HLP_NOBREAK },
1376 { "ATTACH", &attach_cmd, 0, HLP_ATTACH },
1377 { "DETACH", &detach_cmd, 0, HLP_DETACH },
1378 { "ASSIGN", &assign_cmd, 0, HLP_ASSIGN },
1379 { "DEASSIGN", &deassign_cmd, 0, HLP_DEASSIGN },
1380 { "SAVE", &save_cmd, 0, HLP_SAVE },
1381 { "RESTORE", &restore_cmd, 0, HLP_RESTORE },
1382 { "GET", &restore_cmd, 0, NULL },
1383 { "EXIT", &exit_cmd, 0, HLP_EXIT },
1384 { "QUIT", &exit_cmd, 0, NULL },
1385 { "BYE", &exit_cmd, 0, NULL },
1386 { "SET", &set_cmd, 0, HLP_SET },
1387 { "SHOW", &show_cmd, 0, HLP_SHOW },
1388 { "DO", &do_cmd, 1, HLP_DO },
1389 { "GOTO", &goto_cmd, 1, HLP_GOTO },
1390 { "RETURN", &return_cmd, 0, HLP_RETURN },
1391 { "SHIFT", &shift_cmd, 0, HLP_SHIFT },
1392 { "CALL", &call_cmd, 0, HLP_CALL },
1393 { "ON", &on_cmd, 0, HLP_ON },
1394 { "IF", &assert_cmd, 0, HLP_IF },
1395 { "PROCEED", &noop_cmd, 0, HLP_PROCEED },
1396 { "IGNORE", &noop_cmd, 0, HLP_IGNORE },
1397 { "ECHO", &echo_cmd, 0, HLP_ECHO },
1398 { "ASSERT", &assert_cmd, 1, HLP_ASSERT },
1399 { "SEND", &send_cmd, 0, HLP_SEND },
1400 { "EXPECT", &expect_cmd, 1, HLP_EXPECT },
1401 { "NOEXPECT", &expect_cmd, 0, HLP_EXPECT },
1402 { "!", &spawn_cmd, 0, HLP_SPAWN },
1403 { "HELP", &help_cmd, 0, HLP_HELP },
1404 { NULL, NULL, 0 }
1405 };
1406
1407 static CTAB set_glob_tab[] = {
1408 { "CONSOLE", &sim_set_console, 0, HLP_SET_CONSOLE },
1409 { "REMOTE", &sim_set_remote_console, 0, HLP_SET_REMOTE },
1410 { "BREAK", &brk_cmd, SSH_ST, HLP_SET_BREAK },
1411 { "NOBREAK", &brk_cmd, SSH_CL, HLP_SET_BREAK },
1412 { "TELNET", &sim_set_telnet, 0 }, /* deprecated */
1413 { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */
1414 { "LOG", &sim_set_logon, 0, HLP_SET_LOG },
1415 { "NOLOG", &sim_set_logoff, 0, HLP_SET_LOG },
1416 { "DEBUG", &sim_set_debon, 0, HLP_SET_DEBUG },
1417 { "NODEBUG", &sim_set_deboff, 0, HLP_SET_DEBUG },
1418 { "ENVIRONMENT", &sim_set_environment, 1, HLP_SET_ENVIRON },
1419 { "ON", &set_on, 1, HLP_SET_ON },
1420 { "NOON", &set_on, 0, HLP_SET_ON },
1421 { "VERIFY", &set_verify, 1, HLP_SET_VERIFY },
1422 { "VERBOSE", &set_verify, 1, HLP_SET_VERIFY },
1423 { "NOVERIFY", &set_verify, 0, HLP_SET_VERIFY },
1424 { "NOVERBOSE", &set_verify, 0, HLP_SET_VERIFY },
1425 { "MESSAGE", &set_message, 1, HLP_SET_MESSAGE },
1426 { "NOMESSAGE", &set_message, 0, HLP_SET_MESSAGE },
1427 { "QUIET", &set_quiet, 1, HLP_SET_QUIET },
1428 { "NOQUIET", &set_quiet, 0, HLP_SET_QUIET },
1429 { "PROMPT", &set_prompt, 0, HLP_SET_PROMPT },
1430 { NULL, NULL, 0 }
1431 };
1432
1433 static C1TAB set_dev_tab[] = {
1434 { "OCTAL", &set_dev_radix, 8 },
1435 { "DECIMAL", &set_dev_radix, 10 },
1436 { "HEX", &set_dev_radix, 16 },
1437 { "ENABLED", &set_dev_enbdis, 1 },
1438 { "DISABLED", &set_dev_enbdis, 0 },
1439 { "DEBUG", &set_dev_debug, 1 },
1440 { "NODEBUG", &set_dev_debug, 0 },
1441 { NULL, NULL, 0 }
1442 };
1443
1444 static C1TAB set_unit_tab[] = {
1445 { "ENABLED", &set_unit_enbdis, 1 },
1446 { "DISABLED", &set_unit_enbdis, 0 },
1447 { NULL, NULL, 0 }
1448 };
1449
1450 static SHTAB show_glob_tab[] = {
1451 { "CONFIGURATION", &show_config, 0, HLP_SHOW_CONFIG },
1452 { "DEVICES", &show_config, 1, HLP_SHOW_DEVICES },
1453 { "FEATURES", &show_config, 2, HLP_SHOW_FEATURES },
1454 { "QUEUE", &show_queue, 0, HLP_SHOW_QUEUE },
1455 { "TIME", &show_time, 0, HLP_SHOW_TIME },
1456 { "MODIFIERS", &show_mod_names, 0, HLP_SHOW_MODIFIERS },
1457 { "NAMES", &show_log_names, 0, HLP_SHOW_NAMES },
1458 { "SHOW", &show_show_commands, 0, HLP_SHOW_SHOW },
1459 { "VERSION", &show_version, 1, HLP_SHOW_VERSION },
1460 { "BUILDINFO", &show_buildinfo, 1, HLP_SHOW_BUILDINFO },
1461 { "PROM", &show_prom, 0, HLP_SHOW_PROM },
1462 { "CONSOLE", &sim_show_console, 0, HLP_SHOW_CONSOLE },
1463 { "REMOTE", &sim_show_remote_console, 0, HLP_SHOW_REMOTE },
1464 { "BREAK", &show_break, 0, HLP_SHOW_BREAK },
1465 { "LOG", &sim_show_log, 0, HLP_SHOW_LOG },
1466 { "TELNET", &sim_show_telnet, 0 }, /* deprecated */
1467 { "DEBUG", &sim_show_debug, 0, HLP_SHOW_DEBUG },
1468 { "CLOCKS", &sim_show_timers, 0, HLP_SHOW_CLOCKS },
1469 { "SEND", &sim_show_send, 0, HLP_SHOW_SEND },
1470 { "EXPECT", &sim_show_expect, 0, HLP_SHOW_EXPECT },
1471 { "ON", &show_on, 0, HLP_SHOW_ON },
1472 { NULL, NULL, 0 }
1473 };
1474
1475 static SHTAB show_dev_tab[] = {
1476 { "RADIX", &show_dev_radix, 0 },
1477 { "DEBUG", &show_dev_debug, 0 },
1478 { "MODIFIERS", &show_dev_modifiers, 0 },
1479 { "NAMES", &show_dev_logicals, 0 },
1480 { "SHOW", &show_dev_show_commands, 0 },
1481 { NULL, NULL, 0 }
1482 };
1483
1484 static SHTAB show_unit_tab[] = {
1485 { NULL, NULL, 0 }
1486 };
1487
1488 #if defined(_WIN32)
1489 static
setenv(const char * envname,const char * envval,int overwrite)1490 int setenv(const char *envname, const char *envval, int overwrite)
1491 {
1492 char *envstr = (char *)malloc(strlen(envname)+strlen(envval)+2);
1493 int r;
1494
1495 sprintf(envstr, "%s=%s", envname, envval);
1496 r = _putenv(envstr);
1497 free(envstr);
1498 return r;
1499 }
1500
1501 static
unsetenv(const char * envname)1502 int unsetenv(const char *envname)
1503 {
1504 setenv(envname, "", 1);
1505 return 0;
1506 }
1507 #endif
1508
1509 t_stat process_stdin_commands (t_stat stat, char *argv[]);
1510
1511 /* Check if running on Rosetta 2 */
1512
1513 #if defined(__APPLE__)
processIsTranslated(void)1514 int processIsTranslated(void)
1515 {
1516 int ret = 0;
1517 size_t size = sizeof(ret);
1518 if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
1519 if (errno == ENOENT)
1520 return 0;
1521 return -1; }
1522 return ret;
1523 }
1524 #endif
1525
1526 /* Substring removal hack */
1527
strremove(char * str,const char * sub)1528 char *strremove(char *str, const char *sub)
1529 {
1530 char *p, *q, *r;
1531 if (*sub && (q = r = strstr(str, sub)) != NULL) {
1532 size_t len = strlen(sub);
1533 while ((r = strstr(p = r + len, sub)) != NULL) {
1534 while (p < r)
1535 *q++ = *p++;
1536 }
1537 while ((*q++ = *p++) != '\0')
1538 continue;
1539 }
1540 return str;
1541 }
1542
1543 /* Main command loop */
1544
1545 #ifndef PERF_STRIP
main(int argc,char * argv[])1546 int main (int argc, char *argv[])
1547 {
1548 char *cptr, *cptr2;
1549 char nbuf[PATH_MAX + 7];
1550 char cbuf[4*CBUFSIZE];
1551 char **targv = NULL;
1552 int32 i, sw;
1553 t_bool lookswitch;
1554 t_stat stat;
1555
1556 # ifdef __MINGW32__
1557 # ifndef NEED_CONSOLE_SETUP
1558 # define NEED_CONSOLE_SETUP
1559 # endif
1560 # endif /* ifdef __MINGW32__ */
1561
1562 # ifdef CROSS_MINGW32
1563 # ifndef NEED_CONSOLE_SETUP
1564 # define NEED_CONSOLE_SETUP
1565 # endif
1566 # endif /* ifdef CROSS_MINGW32 */
1567
1568 # ifdef __MINGW64__
1569 # ifndef NEED_CONSOLE_SETUP
1570 # define NEED_CONSOLE_SETUP
1571 # endif
1572 # endif /* ifdef __MINGW64__ */
1573
1574 # ifdef CROSS_MINGW64
1575 # ifndef NEED_CONSOLE_SETUP
1576 # define NEED_CONSOLE_SETUP
1577 # endif
1578 # endif /* ifdef CROSS_MINGW64 */
1579
1580 # if defined(NEED_CONSOLE_SETUP) && defined(_WIN32)
1581 # include <windows.h>
1582 # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
1583 # define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1584 # endif
1585 # endif /* if defined(NEED_CONSOLE_SETUP) && defined(_WIN32) */
1586
1587 # ifdef NEED_CONSOLE_SETUP
1588 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
1589 if (handle != INVALID_HANDLE_VALUE)
1590 {
1591 DWORD mode = 0;
1592 if (GetConsoleMode(handle, &mode))
1593 {
1594 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
1595 SetConsoleMode(handle, mode);
1596 }
1597 }
1598 puts ("\e[0m");
1599 # endif /* NEED_CONSOLE_SETUP */
1600
1601 /* endian-ness sanity test */
1602 int testEndian = decContextTestEndian(1);
1603 if (testEndian != 0) {
1604 if (testEndian == 1) {
1605 fprintf (stderr,
1606 "Error: Compiled for big-endian, but little-endian ordering detected; aborting.\n");
1607 return 0;
1608 }
1609 if (testEndian == -1) {
1610 fprintf (stderr,
1611 "Error: Compiled for little-endian, but big-endian ordering detected; aborting.\n");
1612 return 0;
1613 }
1614 fprintf (stderr,
1615 "Error: Unable to determine system byte order; aborting.\n");
1616 return 0;
1617 }
1618
1619 /* invocation sanity check */
1620 if (argc == 0) {
1621 fprintf (stderr, "Error: main() called directly!\n");
1622 return 0;
1623 }
1624
1625 /* patch intel dispatcher */
1626 # ifdef __DISPATCH_H_
1627 # if defined(__INTEL_COMPILER) || \
1628 defined(__INTEL_CLANG_COMPILER) || \
1629 defined(__INTEL_LLVM_COMPILER) || \
1630 defined(INTEL_MKL_VERSION) || \
1631 defined(__INTEL_MKL__)
1632 (void)agner_compiler_patch();
1633 # endif
1634 # endif
1635
1636 /* Make sure that argv has at least 10 elements and that it ends in a NULL pointer */
1637 targv = (char **)calloc (1+MAX(10, argc), sizeof(*targv));
1638 for (i=0; i<argc; i++)
1639 targv[i] = argv[i];
1640 argv = targv;
1641
1642 /* setup defaults */
1643 set_prompt (0, "sim>"); /* start with set standard prompt */
1644 *cbuf = 0; /* init arg buffer */
1645 sim_switches = 0; /* init switches */
1646 lookswitch = TRUE;
1647 stdnul = fopen(NULL_DEVICE,"wb");
1648
1649 /* process arguments */
1650 for (i = 1; i < argc; i++) { /* loop thru args */
1651 if (argv[i] == NULL) /* paranoia */
1652 continue;
1653
1654 /* requested only version? */
1655 int onlyvers = strcmp(argv[i], "--version");
1656 if (onlyvers == 0) {
1657 # ifdef VER_H_GIT_VERSION
1658 # if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1659 # if VER_H_GIT_PATCH_INT < 1
1660 fprintf (stdout, "%s simulator %s\n", sim_name, VER_H_GIT_VERSION);
1661 # else
1662 fprintf (stdout, "%s simulator %s+%s\n", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1663 # endif /* if VER_H_GIT_PATCH_INT < 1 */
1664 # else
1665 fprintf (stdout, "%s simulator %s\n", sim_name, VER_H_GIT_VERSION);
1666 # endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1667 # else
1668 fprintf (stdout, "%s simulator\n", sim_name);
1669 # endif /* ifdef VER_H_GIT_VERSION */
1670 return 0;
1671 }
1672
1673 /* requested short or long help? */
1674 int longhelp = strcmp(argv[i], "--help");
1675 int shorthelp = strcmp(argv[i], "-h");
1676 if (shorthelp != 0) shorthelp = strcmp(argv[i], "-H");
1677 if (longhelp == 0 || shorthelp == 0) {
1678 # ifdef VER_H_GIT_VERSION
1679 # if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
1680 # if VER_H_GIT_PATCH_INT < 1
1681 fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1682 # else
1683 fprintf (stdout, "%s simulator %s+%s", sim_name, VER_H_GIT_VERSION, VER_H_GIT_PATCH);
1684 # endif /* if VER_H_GIT_PATCH_INT < 1 */
1685 # else
1686 fprintf (stdout, "%s simulator %s", sim_name, VER_H_GIT_VERSION);
1687 # endif /* if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT) */
1688 # else
1689 fprintf (stdout, "%s simulator", sim_name);
1690 # endif /* ifdef VER_H_GIT_VERSION */
1691 fprintf (stdout, "\n\nUsage: %s { [SWITCHES] ... } { <SCRIPT> }\n", argv[0]);
1692 fprintf (stdout, "\nInvoke the %s simulator, with optional switches and/or script.\n", sim_name);
1693 fprintf (stdout, "\n Switches:");
1694 fprintf (stdout, "\n -e, -E Abort script processing immediately on errors");
1695 fprintf (stdout, "\n -h, -H, --help Display this informational help text and exit");
1696 fprintf (stdout, "\n -k, -K Do not utilize exclusive file locking support");
1697 fprintf (stdout, "\n -l, -L Disregard fatal exclusive file locking errors");
1698 fprintf (stdout, "\n -o, -O Configure script conditions to be inheritable");
1699 fprintf (stdout, "\n -q, -Q Disables non-error and informational messages");
1700 fprintf (stdout, "\n -r, -R Enables ephemeral (session-only) system state");
1701 fprintf (stdout, "\n -s, -S Use a randomized name to persist system state");
1702 fprintf (stdout, "\n -v, -V Display every script command before execution");
1703 fprintf (stdout, "\n --version Display the simulator version string and exit");
1704 fprintf (stdout, "\n\nThis software is made available under the terms of the ICU License,");
1705 fprintf (stdout, "\nversion 1.8.1 or later. For complete details, see the \"LICENSE.md\"");
1706 fprintf (stdout, "\nincluded or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md\n");
1707 return 0;
1708 }
1709 /* invalid arguments? */
1710 if ((*argv[i] == '-') && lookswitch) { /* switch? */
1711 if ((sw = get_switches (argv[i])) < 0) {
1712 fprintf (stderr, "Invalid switch \"%s\".\nTry \"%s -h\" for help.\n", argv[i], argv[0]);
1713 return 0;
1714 }
1715 sim_switches = sim_switches | sw;
1716 }
1717 /* parse arguments */
1718 else {
1719 if ((strlen (argv[i]) + strlen (cbuf) + 3) >= sizeof(cbuf)) {
1720 fprintf (stderr, "Argument string too long\n");
1721 return 0;
1722 }
1723 if (*cbuf) /* concat args */
1724 strcat (cbuf, " ");
1725 sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? \
1726 "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : "");
1727 lookswitch = FALSE; /* no more switches */
1728 }
1729 } /* end for */
1730 sim_nolock = sim_switches & SWMASK ('K'); /* -k means skip locking */
1731 sim_iglock = sim_switches & SWMASK ('L'); /* -l means ignore locking */
1732 sim_randompst = sim_switches & SWMASK ('S'); /* -s means persist random */
1733 sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */
1734 sim_randstate = sim_switches & SWMASK ('R'); /* -r means random sys_state */
1735 if (sim_randompst) sim_randstate = 1; /* and is implied with -s */
1736 sim_on_inherit = sim_switches & SWMASK ('O'); /* -o means inherit on state */
1737
1738 sim_init_sock (); /* init socket capabilities */
1739 if (sim_dflt_dev == NULL) /* if no default */
1740 sim_dflt_dev = sim_devices[0];
1741 if (sim_vm_init != NULL) /* call once only */
1742 (*sim_vm_init)();
1743 sim_finit (); /* init fio package */
1744 for (i = 0; cmd_table[i].name; i++) {
1745 size_t alias_len = strlen (cmd_table[i].name);
1746 char *cmd_name = (char *)calloc (1 + alias_len, sizeof (*cmd_name));
1747
1748 strcpy (cmd_name, cmd_table[i].name);
1749 while (alias_len > 1) {
1750 cmd_name[alias_len] = '\0'; /* Possible short form command name */
1751 --alias_len;
1752 if (getenv (cmd_name)) /* Externally defined command alias? */
1753 unsetenv (cmd_name); /* Remove it to protect against possibly malicious aliases */
1754 }
1755 free (cmd_name);
1756 }
1757 stop_cpu = 0;
1758 sim_interval = 0;
1759 sim_time = sim_rtime = 0;
1760 noqueue_time = 0;
1761 sim_clock_queue = QUEUE_LIST_END;
1762 sim_is_running = 0;
1763 sim_log = NULL;
1764 if (sim_emax <= 0)
1765 sim_emax = 1;
1766 sim_timer_init ();
1767
1768 if ((stat = sim_ttinit ()) != SCPE_OK) {
1769 fprintf (stderr, "Fatal terminal initialization error\n%s\n",
1770 sim_error_text (stat));
1771 return 0;
1772 }
1773 if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {
1774 fprintf (stderr, "Unable to allocate examine buffer\n");
1775 return 0;
1776 };
1777 if ((stat = reset_all_p (0)) != SCPE_OK) {
1778 fprintf (stderr, "Fatal simulator initialization error\n%s\n",
1779 sim_error_text (stat));
1780 return 0;
1781 }
1782 if ((stat = sim_brk_init ()) != SCPE_OK) {
1783 fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",
1784 sim_error_text (stat));
1785 return 0;
1786 }
1787 if (!sim_quiet) {
1788 printf ("\n");
1789 show_version (stdout, NULL, NULL, 0, NULL);
1790 }
1791
1792 sim_argv = argv;
1793 cptr = getenv("HOME");
1794 if (cptr == NULL) {
1795 cptr = getenv("HOMEPATH");
1796 cptr2 = getenv("HOMEDRIVE");
1797 }
1798 else
1799 cptr2 = NULL;
1800 (void)cptr2;
1801 if ( (*cbuf) && (strcmp(cbuf, "")) ) /* cmd file arg? */
1802 stat = do_cmd (0, cbuf); /* proc cmd file */
1803 else if (*argv[0]) { /* sim name arg? */
1804 char *np; /* "path.ini" */
1805 nbuf[0] = '"'; /* starting " */
1806 stat = do_cmd (-1, nbuf) & ~SCPE_NOMESSAGE; /* proc default cmd file */
1807 if (stat == SCPE_OPENERR) { /* didn't exist/can't open? */
1808 np = strrchr (nbuf, '/'); /* stript path and try again in cwd */
1809 if (np == NULL)
1810 np = strrchr (nbuf, '\\'); /* windows path separator */
1811 if (np != NULL) {
1812 *np = '"';
1813 stat = do_cmd (-1, np) & ~SCPE_NOMESSAGE; /* proc default cmd file */
1814 }
1815 }
1816 }
1817
1818 stat = process_stdin_commands (SCPE_BARE_STATUS(stat), argv);
1819
1820 detach_all (0, TRUE); /* close files */
1821 sim_set_deboff (0, NULL); /* close debug */
1822 sim_set_logoff (0, NULL); /* close log */
1823 sim_set_notelnet (0, NULL); /* close Telnet */
1824 sim_ttclose (); /* close console */
1825 sim_cleanup_sock (); /* cleanup sockets */
1826 fclose (stdnul); /* close bit bucket file handle */
1827 free (targv); /* release any argv copy that was made */
1828 return 0;
1829 }
1830 #endif
1831
process_stdin_commands(t_stat stat,char * argv[])1832 t_stat process_stdin_commands (t_stat stat, char *argv[])
1833 {
1834 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE];
1835 CONST char *cptr;
1836 t_stat stat_nomessage;
1837 CTAB *cmdp = NULL;
1838
1839 stat = SCPE_BARE_STATUS(stat); /* remove possible flag */
1840 while (stat != SCPE_EXIT) { /* in case exit */
1841 if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf)))) /* pending action? */
1842 printf ("%s%s\n", sim_prompt, cptr); /* echo */
1843 else if (sim_vm_read != NULL) { /* sim routine? */
1844 printf ("%s", sim_prompt); /* prompt */
1845 cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin);
1846 }
1847 else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prmopt*/
1848 if (cptr == NULL) { /* EOF? */
1849 if (sim_ttisatty()) continue; /* ignore tty EOF */
1850 else break; /* otherwise exit */
1851 }
1852 if (*cptr == 0) /* ignore blank */
1853 continue;
1854 sim_sub_args (cbuf, sizeof(cbuf), argv);
1855 if (sim_log) /* log cmd */
1856 fprintf (sim_log, "%s%s\n", sim_prompt, cptr);
1857 if (sim_deb && (sim_deb != sim_log) && (sim_deb != stdout))
1858 fprintf (sim_deb, "%s%s\n", sim_prompt, cptr);
1859 cptr = get_glyph_cmd (cptr, gbuf); /* get command glyph */
1860 sim_switches = 0; /* init switches */
1861 if ((cmdp = find_cmd (gbuf))) /* lookup command */
1862 stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */
1863 else
1864 stat = SCPE_UNK;
1865 stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */
1866 stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
1867 stat = SCPE_BARE_STATUS(stat); /* remove possible flag */
1868 sim_last_cmd_stat = stat; /* save command error status */
1869 if (!stat_nomessage) { /* displaying message status? */
1870 if (cmdp && (cmdp->message)) /* special message handler? */
1871 cmdp->message (NULL, stat); /* let it deal with display */
1872 else
1873 if (stat >= SCPE_BASE) /* error? */
1874 sim_printf ("%s\n", sim_error_text (stat));
1875 }
1876 if (sim_vm_post != NULL)
1877 (*sim_vm_post) (TRUE);
1878 } /* end while */
1879 return stat;
1880 }
1881
1882 /* Set prompt routine */
1883
set_prompt(int32 flag,CONST char * cptr)1884 t_stat set_prompt (int32 flag, CONST char *cptr)
1885 {
1886 char gbuf[CBUFSIZE], *gptr;
1887
1888 if ((!cptr) || (*cptr == '\0'))
1889 return SCPE_ARG;
1890
1891 cptr = get_glyph_nc (cptr, gbuf, '"'); /* get quote delimited token */
1892 if (gbuf[0] == '\0') { /* Token started with quote */
1893 gbuf[sizeof (gbuf)-1] = '\0';
1894 strncpy (gbuf, cptr, sizeof (gbuf)-1);
1895 gptr = strchr (gbuf, '"');
1896 if (gptr)
1897 *gptr = '\0';
1898 }
1899 sim_prompt = (char *)realloc (sim_prompt, strlen (gbuf) + 2); /* nul terminator and trailing blank */
1900 sprintf (sim_prompt, "%s ", gbuf);
1901 return SCPE_OK;
1902 }
1903
1904 /* Find command routine */
1905
find_cmd(const char * gbuf)1906 CTAB *find_cmd (const char *gbuf)
1907 {
1908 CTAB *cmdp = NULL;
1909
1910 if (sim_vm_cmd) /* try ext commands */
1911 cmdp = find_ctab (sim_vm_cmd, gbuf);
1912 if (cmdp == NULL) /* try regular cmds */
1913 cmdp = find_ctab (cmd_table, gbuf);
1914 return cmdp;
1915 }
1916
1917 /* Exit command */
1918
exit_cmd(int32 flag,CONST char * cptr)1919 t_stat exit_cmd (int32 flag, CONST char *cptr)
1920 {
1921 return SCPE_EXIT;
1922 }
1923
1924 /* Help command */
1925
1926 /* Used when sorting a list of command names */
_cmd_name_compare(const void * pa,const void * pb)1927 static int _cmd_name_compare (const void *pa, const void *pb)
1928 {
1929 CTAB * const *a = (CTAB * const *)pa;
1930 CTAB * const *b = (CTAB * const *)pb;
1931
1932 return strcmp((*a)->name, (*b)->name);
1933 }
1934
fprint_help(FILE * st)1935 void fprint_help (FILE *st)
1936 {
1937 CTAB *cmdp;
1938 CTAB **hlp_cmdp = NULL;
1939 int cmd_cnt = 0;
1940 int cmd_size = 0;
1941 size_t max_cmdname_size = 0;
1942 int i, line_offset;
1943
1944 for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {
1945 if (cmdp->help) {
1946 if (cmd_cnt >= cmd_size) {
1947 cmd_size += 20;
1948 hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
1949 }
1950 hlp_cmdp[cmd_cnt] = cmdp;
1951 ++cmd_cnt;
1952 if (strlen(cmdp->name) > max_cmdname_size)
1953 max_cmdname_size = strlen(cmdp->name);
1954 }
1955 }
1956 for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {
1957 if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) {
1958 if (cmd_cnt >= cmd_size) {
1959 cmd_size += 20;
1960 hlp_cmdp = (CTAB **)realloc (hlp_cmdp, sizeof(*hlp_cmdp)*cmd_size);
1961 }
1962 hlp_cmdp[cmd_cnt] = cmdp;
1963 ++cmd_cnt;
1964 if (strlen (cmdp->name) > max_cmdname_size)
1965 max_cmdname_size = strlen(cmdp->name);
1966 }
1967 }
1968 fprintf (st, "Help is available for the following commands:\n\n ");
1969 if (hlp_cmdp)
1970 qsort (hlp_cmdp, cmd_cnt, sizeof(*hlp_cmdp), _cmd_name_compare);
1971 line_offset = 4;
1972 for ( i = 0 ; i < cmd_cnt ; ++i ) {
1973 fputs (hlp_cmdp[i]->name, st);
1974 line_offset += 5 + max_cmdname_size;
1975 if (line_offset + max_cmdname_size > 79) {
1976 line_offset = 4;
1977 fprintf (st, "\n ");
1978 }
1979 else
1980 fprintf (st, "%*s", (int)(max_cmdname_size + 5 - strlen (hlp_cmdp[i]->name)), "");
1981 }
1982 free (hlp_cmdp);
1983 fprintf (st, "\n");
1984 return;
1985 }
1986
fprint_header(FILE * st,t_bool * pdone,char * context)1987 static void fprint_header (FILE *st, t_bool *pdone, char *context)
1988 {
1989 if (!*pdone)
1990 fprintf (st, "%s", context);
1991 *pdone = TRUE;
1992 }
1993
fprint_reg_help_ex(FILE * st,DEVICE * dptr,t_bool silent)1994 void fprint_reg_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
1995 {
1996 REG *rptr, *trptr;
1997 t_bool found = FALSE;
1998 t_bool all_unique = TRUE;
1999 size_t max_namelen = 0;
2000 DEVICE *tdptr;
2001 CONST char *tptr;
2002 char *namebuf;
2003 char rangebuf[32];
2004
2005 if (dptr->registers)
2006 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2007 if (rptr->flags & REG_HIDDEN)
2008 continue;
2009 if (rptr->depth > 1)
2010 sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2011 else
2012 strcpy (rangebuf, "");
2013 if (max_namelen < (strlen(rptr->name) + strlen (rangebuf)))
2014 max_namelen = strlen(rptr->name) + strlen (rangebuf);
2015 found = TRUE;
2016 trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2017 if ((trptr == NULL) || (tdptr != dptr))
2018 all_unique = FALSE;
2019 }
2020 if (!found) {
2021 if (!silent)
2022 fprintf (st, "No register help is available for the %s device\n", dptr->name);
2023 }
2024 else {
2025 namebuf = (char *)calloc (max_namelen + 1, sizeof (*namebuf));
2026 fprintf (st, "\nThe %s device implements these registers:\n\n", dptr->name);
2027 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
2028 if (rptr->flags & REG_HIDDEN)
2029 continue;
2030 if (rptr->depth <= 1)
2031 sprintf (namebuf, "%*s", -((int)max_namelen), rptr->name);
2032 else {
2033 sprintf (rangebuf, "[%d:%d]", 0, rptr->depth-1);
2034 sprintf (namebuf, "%s%*s", rptr->name, (int)(strlen(rptr->name))-((int)max_namelen), rangebuf);
2035 }
2036 if (all_unique) {
2037 fprintf (st, " %s %4d %s\n", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2038 continue;
2039 }
2040 trptr = find_reg_glob (rptr->name, &tptr, &tdptr);
2041 if ((trptr == NULL) || (tdptr != dptr))
2042 fprintf (st, " %s %s %4d %s\n", dptr->name, namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2043 else
2044 fprintf (st, " %*s %s %4d %s\n", (int)strlen(dptr->name), "", namebuf, rptr->width, rptr->desc ? rptr->desc : "");
2045 }
2046 free (namebuf);
2047 }
2048 }
2049
fprint_reg_help(FILE * st,DEVICE * dptr)2050 void fprint_reg_help (FILE *st, DEVICE *dptr)
2051 {
2052 fprint_reg_help_ex (st, dptr, TRUE);
2053 }
2054
fprint_attach_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2055 void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2056 {
2057 if (dptr->attach_help) {
2058 fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2059 dptr->attach_help (st, dptr, NULL, 0, NULL);
2060 return;
2061 }
2062 if (DEV_TYPE(dptr) == DEV_DISK) {
2063 fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2064 sim_disk_attach_help (st, dptr, NULL, 0, NULL);
2065 return;
2066 }
2067 if (DEV_TYPE(dptr) == DEV_TAPE) {
2068 fprintf (st, "\n%s device attach commands:\n\n", dptr->name);
2069 sim_tape_attach_help (st, dptr, NULL, 0, NULL);
2070 return;
2071 }
2072 if (!silent) {
2073 fprintf (st, "No ATTACH help is available for the %s device\n", dptr->name);
2074 if (dptr->help)
2075 dptr->help (st, dptr, NULL, 0, NULL);
2076 }
2077 }
2078
fprint_set_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2079 void fprint_set_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2080 {
2081 MTAB *mptr;
2082 DEBTAB *dep;
2083 t_bool found = FALSE;
2084 char buf[CBUFSIZE], header[CBUFSIZE];
2085 uint32 enabled_units = dptr->numunits;
2086 uint32 unit;
2087
2088 sprintf (header, "\n%s device SET commands:\n\n", dptr->name);
2089 for (unit=0; unit < dptr->numunits; unit++)
2090 if (dptr->units[unit].flags & UNIT_DIS)
2091 --enabled_units;
2092 if (dptr->modifiers) {
2093 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2094 if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2095 continue; /* skip unit only extended modifiers */
2096 if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2097 continue; /* skip unit only simple modifiers */
2098 if (mptr->mstring) {
2099 fprint_header (st, &found, header);
2100 sprintf (buf, "set %s %s%s", sim_dname (dptr), mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}" : "")));
2101 if ((strlen (buf) < 30) || (!mptr->help))
2102 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2103 else
2104 fprintf (st, "%s\n%-30s\t%s\n", buf, "", mptr->help);
2105 }
2106 }
2107 }
2108 if (dptr->flags & DEV_DISABLE) {
2109 fprint_header (st, &found, header);
2110 sprintf (buf, "set %s ENABLE", sim_dname (dptr));
2111 fprintf (st, "%-30s\tEnables device %s\n", buf, sim_dname (dptr));
2112 sprintf (buf, "set %s DISABLE", sim_dname (dptr));
2113 fprintf (st, "%-30s\tDisables device %s\n", buf, sim_dname (dptr));
2114 }
2115 if (dptr->flags & DEV_DEBUG) {
2116 fprint_header (st, &found, header);
2117 sprintf (buf, "set %s DEBUG", sim_dname (dptr));
2118 fprintf (st, "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr));
2119 sprintf (buf, "set %s NODEBUG", sim_dname (dptr));
2120 fprintf (st, "%-30s\tDisables debugging for device %s\n", buf, sim_dname (dptr));
2121 if (dptr->debflags) {
2122 t_bool desc_available = FALSE;
2123 strcpy (buf, "");
2124 fprintf (st, "set %s DEBUG=", sim_dname (dptr));
2125 for (dep = dptr->debflags; dep->name != NULL; dep++) {
2126 fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2127 desc_available |= ((dep->desc != NULL) && (dep->desc[0] != '\0'));
2128 }
2129 fprintf (st, "\n");
2130 fprintf (st, "%-30s\tEnables specific debugging for device %s\n", buf, sim_dname (dptr));
2131 fprintf (st, "set %s NODEBUG=", sim_dname (dptr));
2132 for (dep = dptr->debflags; dep->name != NULL; dep++)
2133 fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ";"), dep->name);
2134 fprintf (st, "\n");
2135 fprintf (st, "%-30s\tDisables specific debugging for device %s\n", buf, sim_dname (dptr));
2136 if (desc_available) {
2137 fprintf (st, "\n*%s device DEBUG settings:\n", sim_dname (dptr));
2138 for (dep = dptr->debflags; dep->name != NULL; dep++)
2139 fprintf (st, "%4s%-12s%s\n", "", dep->name, dep->desc ? dep->desc : "");
2140 }
2141 }
2142 }
2143 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2144 if (dptr->units->flags & UNIT_DISABLE) {
2145 fprint_header (st, &found, header);
2146 sprintf (buf, "set %sn ENABLE", sim_dname (dptr));
2147 fprintf (st, "%-30s\tEnables unit %sn\n", buf, sim_dname (dptr));
2148 sprintf (buf, "set %sn DISABLE", sim_dname (dptr));
2149 fprintf (st, "%-30s\tDisables unit %sn\n", buf, sim_dname (dptr));
2150 }
2151 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2152 if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2153 continue; /* skip device only modifiers */
2154 if ((!mptr->valid) && MODMASK(mptr,MTAB_XTD))
2155 continue; /* skip show only modifiers */
2156 if (mptr->mstring) {
2157 fprint_header (st, &found, header);
2158 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}": "")));
2159 fprintf (st, "%-30s\t%s\n", buf, (strchr(mptr->mstring, '=')) ? "" : (mptr->help ? mptr->help : ""));
2160 }
2161 }
2162 }
2163 if (!found && !silent)
2164 fprintf (st, "No SET help is available for the %s device\n", dptr->name);
2165 }
2166
fprint_set_help(FILE * st,DEVICE * dptr)2167 void fprint_set_help (FILE *st, DEVICE *dptr)
2168 {
2169 fprint_set_help_ex (st, dptr, TRUE);
2170 }
2171
fprint_show_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2172 void fprint_show_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2173 {
2174 MTAB *mptr;
2175 t_bool found = FALSE;
2176 char buf[CBUFSIZE], header[CBUFSIZE];
2177 uint32 enabled_units = dptr->numunits;
2178 uint32 unit;
2179
2180 sprintf (header, "\n%s device SHOW commands:\n\n", dptr->name);
2181 for (unit=0; unit < dptr->numunits; unit++)
2182 if (dptr->units[unit].flags & UNIT_DIS)
2183 --enabled_units;
2184 if (dptr->modifiers) {
2185 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2186 if (!MODMASK(mptr,MTAB_VDV) && MODMASK(mptr,MTAB_VUN) && (dptr->numunits != 1))
2187 continue; /* skip unit only extended modifiers */
2188 if ((enabled_units != 1) && !(mptr->mask & MTAB_XTD))
2189 continue; /* skip unit only simple modifiers */
2190 if ((!mptr->disp) || (!mptr->pstring) || !(*mptr->pstring))
2191 continue;
2192 fprint_header (st, &found, header);
2193 sprintf (buf, "show %s %s%s", sim_dname (dptr), mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "{=arg}" : "");
2194 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2195 }
2196 }
2197 if (dptr->flags & DEV_DEBUG) {
2198 fprint_header (st, &found, header);
2199 sprintf (buf, "show %s DEBUG", sim_dname (dptr));
2200 fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr));
2201 }
2202 if ((dptr->modifiers) && (dptr->units) && (enabled_units != 1)) {
2203 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
2204 if ((!MODMASK(mptr,MTAB_VUN)) && MODMASK(mptr,MTAB_XTD))
2205 continue; /* skip device only modifiers */
2206 if ((!mptr->disp) || (!mptr->pstring))
2207 continue;
2208 fprint_header (st, &found, header);
2209 sprintf (buf, "show %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->pstring, MODMASK(mptr,MTAB_SHP) ? "=arg" : "");
2210 fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : "");
2211 }
2212 }
2213 if (!found && !silent)
2214 fprintf (st, "No SHOW help is available for the %s device\n", dptr->name);
2215 }
2216
fprint_show_help(FILE * st,DEVICE * dptr)2217 void fprint_show_help (FILE *st, DEVICE *dptr)
2218 {
2219 fprint_show_help_ex (st, dptr, TRUE);
2220 }
2221
fprint_brk_help_ex(FILE * st,DEVICE * dptr,t_bool silent)2222 void fprint_brk_help_ex (FILE *st, DEVICE *dptr, t_bool silent)
2223 {
2224 BRKTYPTAB *brkt = dptr->brk_types;
2225 char gbuf[CBUFSIZE];
2226
2227 if (sim_brk_types == 0) {
2228 if ((dptr != sim_dflt_dev) && (!silent)) {
2229 fprintf (st, "Breakpoints are not supported in the %s simulator\n", sim_name);
2230 if (dptr->help)
2231 dptr->help (st, dptr, NULL, 0, NULL);
2232 }
2233 return;
2234 }
2235 if (brkt == NULL) {
2236 int i;
2237
2238 if (dptr == sim_dflt_dev) {
2239 if (sim_brk_types & ~sim_brk_dflt) {
2240 fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2241 for (i=0; i<26; i++) {
2242 if (sim_brk_types & (1<<i))
2243 fprintf (st, " -%c\n", 'A'+i);
2244 }
2245 }
2246 fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2247 }
2248 return;
2249 }
2250 fprintf (st, "%s supports the following breakpoint types:\n", sim_dname (dptr));
2251 while (brkt->btyp) {
2252 fprintf (st, " %s %s\n", put_switches (gbuf, sizeof(gbuf), brkt->btyp), brkt->desc);
2253 ++brkt;
2254 }
2255 fprintf (st, "The default breakpoint type is: %s\n", put_switches (gbuf, sizeof(gbuf), sim_brk_dflt));
2256 }
2257
help_dev_help(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,const char * cptr)2258 t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
2259 {
2260 char gbuf[CBUFSIZE];
2261 CTAB *cmdp;
2262
2263 if (*cptr) {
2264 get_glyph (cptr, gbuf, 0);
2265 if ((cmdp = find_cmd (gbuf))) {
2266 if (cmdp->action == &exdep_cmd) {
2267 if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
2268 return dptr->help (st, dptr, uptr, flag, cptr);
2269 else
2270 fprintf (st, "No help available for the %s %s command\n", cmdp->name, sim_dname(dptr));
2271 return SCPE_OK;
2272 }
2273 if (cmdp->action == &set_cmd) {
2274 fprint_set_help_ex (st, dptr, FALSE);
2275 return SCPE_OK;
2276 }
2277 if (cmdp->action == &show_cmd) {
2278 fprint_show_help_ex (st, dptr, FALSE);
2279 return SCPE_OK;
2280 }
2281 if (cmdp->action == &attach_cmd) {
2282 fprint_attach_help_ex (st, dptr, FALSE);
2283 return SCPE_OK;
2284 }
2285 if (cmdp->action == &brk_cmd) {
2286 fprint_brk_help_ex (st, dptr, FALSE);
2287 return SCPE_OK;
2288 }
2289 if (dptr->help)
2290 return dptr->help (st, dptr, uptr, flag, cptr);
2291 fprintf (st, "No %s help is available for the %s device\n", cmdp->name, dptr->name);
2292 return SCPE_OK;
2293 }
2294 if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
2295 fprint_reg_help_ex (st, dptr, FALSE);
2296 return SCPE_OK;
2297 }
2298 if (dptr->help)
2299 return dptr->help (st, dptr, uptr, flag, cptr);
2300 fprintf (st, "No %s help is available for the %s device\n", gbuf, dptr->name);
2301 return SCPE_OK;
2302 }
2303 if (dptr->help) {
2304 return dptr->help (st, dptr, uptr, flag, cptr);
2305 }
2306 if (dptr->description)
2307 fprintf (st, "%s %s help\n", dptr->description (dptr), dptr->name);
2308 else
2309 fprintf (st, "%s help\n", dptr->name);
2310 fprint_set_help_ex (st, dptr, TRUE);
2311 fprint_show_help_ex (st, dptr, TRUE);
2312 fprint_attach_help_ex (st, dptr, TRUE);
2313 fprint_reg_help_ex (st, dptr, TRUE);
2314 fprint_brk_help_ex (st, dptr, TRUE);
2315 return SCPE_OK;
2316 }
2317
help_cmd_output(int32 flag,const char * help,const char * help_base)2318 t_stat help_cmd_output (int32 flag, const char *help, const char *help_base)
2319 {
2320 switch (help[0]) {
2321 case '*':
2322 scp_help (stdout, NULL, NULL, flag, help_base ? help_base : simh_help, help+1);
2323 if (sim_log)
2324 scp_help (sim_log, NULL, NULL, flag | SCP_HELP_FLAT, help_base ? help_base : simh_help, help+1);
2325 break;
2326 default:
2327 fputs (help, stdout);
2328 if (sim_log)
2329 fputs (help, sim_log);
2330 break;
2331 }
2332 return SCPE_OK;
2333 }
2334
help_cmd(int32 flag,CONST char * cptr)2335 t_stat help_cmd (int32 flag, CONST char *cptr)
2336 {
2337 char gbuf[CBUFSIZE];
2338 CTAB *cmdp;
2339
2340 GET_SWITCHES (cptr);
2341 if (sim_switches & SWMASK ('F'))
2342 flag = flag | SCP_HELP_FLAT;
2343 if (*cptr) {
2344 cptr = get_glyph (cptr, gbuf, 0);
2345 if ((cmdp = find_cmd (gbuf))) {
2346 if (*cptr) {
2347 if ((cmdp->action == &set_cmd) || (cmdp->action == &show_cmd)) {
2348 DEVICE *dptr;
2349 UNIT *uptr;
2350 t_stat r;
2351 cptr = get_glyph (cptr, gbuf, 0);
2352 dptr = find_unit (gbuf, &uptr);
2353 if (dptr == NULL)
2354 dptr = find_dev (gbuf);
2355 if (dptr != NULL) {
2356 r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2357 if (sim_log)
2358 help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
2359 return r;
2360 }
2361 if (cmdp->action == &set_cmd) { /* HELP SET xxx (not device or unit) */
2362 if ((cmdp = find_ctab (set_glob_tab, gbuf)) &&
2363 (cmdp->help))
2364 return help_cmd_output (flag, cmdp->help, cmdp->help_base);
2365 }
2366 else { /* HELP SHOW xxx (not device or unit) */
2367 SHTAB *shptr = find_shtab (show_glob_tab, gbuf);
2368 if ((shptr == NULL) || (shptr->help == NULL) || (*shptr->help == '\0'))
2369 return SCPE_ARG;
2370 return help_cmd_output (flag, shptr->help, NULL);
2371 }
2372 return SCPE_ARG;
2373 }
2374 else
2375 return SCPE_2MARG;
2376 }
2377 if (cmdp->help) {
2378 if (strcmp (cmdp->name, "HELP") == 0) {
2379 DEVICE *dptr;
2380 int i;
2381 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
2382 if (dptr->help)
2383 sim_printf ("h{elp} %-17s display help for device %s\n", dptr->name, dptr->name);
2384 if (dptr->attach_help ||
2385 (DEV_TYPE(dptr) == DEV_MUX) ||
2386 (DEV_TYPE(dptr) == DEV_DISK) ||
2387 (DEV_TYPE(dptr) == DEV_TAPE)) {
2388 sim_printf ("h{elp} %s ATTACH\t display help for device %s ATTACH command\n", dptr->name, dptr->name);
2389 }
2390 if (dptr->registers) {
2391 if (dptr->registers->name != NULL)
2392 sim_printf ("h{elp} %s REGISTERS\t display help for device %s register variables\n", dptr->name, dptr->name);
2393 }
2394 if (dptr->modifiers) {
2395 MTAB *mptr;
2396 for (mptr = dptr->modifiers; mptr->pstring != NULL; mptr++) {
2397 if (mptr->help) {
2398 sim_printf ("h{elp} %s SET\t\t display help for device %s SET commands (modifiers)\n", dptr->name, dptr->name);
2399 break;
2400 }
2401 }
2402 }
2403 }
2404 }
2405 else {
2406 if (((cmdp->action == &exdep_cmd) || (0 == strcmp(cmdp->name, "BOOT"))) &&
2407 sim_dflt_dev && sim_dflt_dev->help) {
2408 sim_dflt_dev->help (stdout, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2409 if (sim_log)
2410 sim_dflt_dev->help (sim_log, sim_dflt_dev, sim_dflt_dev->units, 0, cmdp->name);
2411 }
2412 }
2413 help_cmd_output (flag, cmdp->help, cmdp->help_base);
2414 }
2415 else { /* no help so it is likely a command alias */
2416 CTAB *cmdpa;
2417 for (cmdpa=cmd_table; cmdpa->name != NULL; cmdpa++)
2418 if ((cmdpa->action == cmdp->action) && (cmdpa->help)) {
2419 sim_printf ("%s is an alias for the %s command:\n%s",
2420 cmdp->name, cmdpa->name, cmdpa->help);
2421 break;
2422 }
2423 if (cmdpa->name == NULL) /* not found? */
2424 sim_printf ("No help available for the %s command\n", cmdp->name);
2425 }
2426 }
2427 else {
2428 DEVICE *dptr;
2429 UNIT *uptr;
2430 t_stat r;
2431 dptr = find_unit (gbuf, &uptr);
2432 if (dptr == NULL) {
2433 dptr = find_dev (gbuf);
2434 if (dptr == NULL)
2435 return SCPE_ARG;
2436 if (dptr->flags & DEV_DIS)
2437 sim_printf ("Device %s is currently disabled\n", dptr->name);
2438 }
2439 r = help_dev_help (stdout, dptr, uptr, flag, cptr);
2440 if (sim_log)
2441 help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
2442 return r;
2443 }
2444 }
2445 else {
2446 fprint_help (stdout);
2447 if (sim_log)
2448 fprint_help (sim_log);
2449 }
2450 return SCPE_OK;
2451 }
2452
2453 /* Spawn command */
2454
spawn_cmd(int32 flag,CONST char * cptr)2455 t_stat spawn_cmd (int32 flag, CONST char *cptr)
2456 {
2457 t_stat status;
2458 if ((cptr == NULL) || (strlen (cptr) == 0))
2459 cptr = getenv("SHELL");
2460 if ((cptr == NULL) || (strlen (cptr) == 0))
2461 cptr = getenv("ComSpec");
2462 fflush(stdout); /* flush stdout */
2463 if (sim_log) /* flush log if enabled */
2464 fflush (sim_log);
2465 if (sim_deb) /* flush debug if enabled */
2466 fflush (sim_deb);
2467 status = system (cptr);
2468
2469 return status;
2470 }
2471
2472 /* Echo command */
2473
echo_cmd(int32 flag,CONST char * cptr)2474 t_stat echo_cmd (int32 flag, CONST char *cptr)
2475 {
2476 sim_printf ("%s\n", cptr);
2477 return SCPE_OK;
2478 }
2479
2480 /* Do command
2481
2482 Syntax: DO {-E} {-V} <filename> {<arguments>...}
2483
2484 -E causes all command errors to be fatal; without it, only EXIT and ASSERT
2485 failure will stop a command file.
2486
2487 -V causes commands to be echoed before execution.
2488
2489 Note that SCPE_STEP ("Step expired") is considered a note and not an error
2490 and so does not abort command execution when using -E.
2491
2492 Inputs:
2493 flag = caller and nesting level indicator
2494 fcptr = filename and optional arguments, space-separated
2495 Outputs:
2496 status = error status
2497
2498 The "flag" input value indicates the source of the call, as follows:
2499
2500 -1 = initialization file (no error if not found)
2501 0 = command line file
2502 1 = "DO" command
2503 >1 = nested "DO" command
2504 */
2505
do_cmd(int32 flag,CONST char * fcptr)2506 t_stat do_cmd (int32 flag, CONST char *fcptr)
2507 {
2508 return do_cmd_label (flag, fcptr, NULL);
2509 }
2510
do_position(void)2511 static char *do_position(void)
2512 {
2513 static char cbuf[4*CBUFSIZE];
2514
2515 snprintf (cbuf, sizeof (cbuf), "%s%s%s-%d", sim_do_filename[sim_do_depth], sim_do_label[sim_do_depth] ? "::" : "", sim_do_label[sim_do_depth] ? sim_do_label[sim_do_depth] : "", sim_goto_line[sim_do_depth]);
2516 return cbuf;
2517 }
2518
do_cmd_label(int32 flag,CONST char * fcptr,CONST char * label)2519 t_stat do_cmd_label (int32 flag, CONST char *fcptr, CONST char *label)
2520 {
2521 char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], abuf[4*CBUFSIZE], quote, *c, *do_arg[11];
2522 CONST char *cptr;
2523 FILE *fpin;
2524 CTAB *cmdp = NULL;
2525 int32 echo, nargs, errabort, i;
2526 int32 saved_sim_do_echo = sim_do_echo,
2527 saved_sim_show_message = sim_show_message,
2528 saved_sim_on_inherit = sim_on_inherit,
2529 saved_sim_quiet = sim_quiet;
2530 t_bool staying;
2531 t_stat stat, stat_nomessage;
2532
2533 stat = SCPE_OK;
2534 staying = TRUE;
2535 if (flag > 0) /* need switches? */
2536 GET_SWITCHES (fcptr); /* get switches */
2537 echo = (sim_switches & SWMASK ('V')) || sim_do_echo; /* -v means echo */
2538 sim_quiet = (sim_switches & SWMASK ('Q')) || sim_quiet; /* -q means quiet */
2539 sim_on_inherit =(sim_switches & SWMASK ('O')) || sim_on_inherit; /* -o means inherit ON condition actions */
2540 errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */
2541
2542 abuf[sizeof(abuf)-1] = '\0';
2543 strncpy (abuf, fcptr, sizeof(abuf)-1);
2544 c = abuf;
2545 do_arg[10] = NULL; /* make sure the argument list always ends with a NULL */
2546 for (nargs = 0; nargs < 10; ) { /* extract arguments */
2547 while (sim_isspace (*c)) /* skip blanks */
2548 c++;
2549 if (*c == 0) /* all done? */
2550 do_arg [nargs++] = NULL; /* null argument */
2551 else {
2552 if (*c == '\'' || *c == '"') /* quoted string? */
2553 quote = *c++;
2554 else quote = 0;
2555 do_arg[nargs++] = c; /* save start */
2556 while (*c && (quote ? (*c != quote) : !sim_isspace (*c)))
2557 c++;
2558 if (*c) /* term at quote/spc */
2559 *c++ = 0;
2560 }
2561 } /* end for */
2562
2563 if (do_arg [0] == NULL) /* need at least 1 */
2564 return SCPE_2FARG;
2565 if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */
2566 strcat (strcpy (cbuf, do_arg[0]), ".ini"); /* try again with .ini extension */
2567 if ((fpin = fopen (cbuf, "r")) == NULL) { /* failed a second time? */
2568 if (flag == 0) /* cmd line file? */
2569 fprintf (stderr, "Can't open file %s\n", do_arg[0]);
2570 return SCPE_OPENERR; /* return failure */
2571 }
2572 }
2573 if (flag >= 0) { /* Only bump nesting from command or nested */
2574 ++sim_do_depth;
2575 if (sim_on_inherit) { /* inherit ON condition actions? */
2576 sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */
2577 for (i=0; i<SCPE_MAX_ERR; i++) { /* replicate any on commands */
2578 if (sim_on_actions[sim_do_depth-1][i]) {
2579 sim_on_actions[sim_do_depth][i] = (char *)malloc(1+strlen(sim_on_actions[sim_do_depth-1][i]));
2580 if (NULL == sim_on_actions[sim_do_depth][i]) {
2581 while (--i >= 0) {
2582 free(sim_on_actions[sim_do_depth][i]);
2583 sim_on_actions[sim_do_depth][i] = NULL;
2584 }
2585 sim_on_check[sim_do_depth] = 0;
2586 sim_brk_clract (); /* defang breakpoint actions */
2587 --sim_do_depth; /* unwind nesting */
2588 return SCPE_MEM;
2589 }
2590 strcpy(sim_on_actions[sim_do_depth][i], sim_on_actions[sim_do_depth-1][i]);
2591 }
2592 }
2593 }
2594 }
2595
2596 strcpy( sim_do_filename[sim_do_depth], do_arg[0]); /* stash away do file name for possible use by 'call' command */
2597 sim_do_label[sim_do_depth] = label; /* stash away do label for possible use in messages */
2598 sim_goto_line[sim_do_depth] = 0;
2599 if (label) {
2600 sim_gotofile = fpin;
2601 sim_do_echo = echo;
2602 stat = goto_cmd (0, label);
2603 if (stat != SCPE_OK) {
2604 strcpy(cbuf, "RETURN SCPE_ARG");
2605 cptr = get_glyph (cbuf, gbuf, 0); /* get command glyph */
2606 cmdp = find_cmd (gbuf); /* return the errorStage things to the stat will be returned */
2607 goto Cleanup_Return;
2608 }
2609 }
2610 if (errabort) /* -e flag? */
2611 set_on (1, NULL); /* equivalent to ON ERROR RETURN */
2612
2613 do {
2614 sim_do_ocptr[sim_do_depth] = cptr = sim_brk_getact (cbuf, sizeof(cbuf)); /* get bkpt action */
2615 if (!sim_do_ocptr[sim_do_depth]) { /* no pending action? */
2616 sim_do_ocptr[sim_do_depth] = cptr = read_line (cbuf, sizeof(cbuf), fpin);/* get cmd line */
2617 sim_goto_line[sim_do_depth] += 1;
2618 }
2619 sim_sub_args (cbuf, sizeof(cbuf), do_arg); /* substitute args */
2620 if (cptr == NULL) { /* EOF? */
2621 stat = SCPE_OK; /* set good return */
2622 break;
2623 }
2624 if (*cptr == 0) /* ignore blank */
2625 continue;
2626 if (echo) /* echo if -v */
2627 sim_printf("%s> %s\n", do_position(), cptr);
2628 if (*cptr == ':') /* ignore label */
2629 continue;
2630 cptr = get_glyph_cmd (cptr, gbuf); /* get command glyph */
2631 sim_switches = 0; /* init switches */
2632 sim_gotofile = fpin;
2633 sim_do_echo = echo;
2634 if ((cmdp = find_cmd (gbuf))) { /* lookup command */
2635 if (cmdp->action == &return_cmd) /* RETURN command? */
2636 break; /* done! */
2637 if (cmdp->action == &do_cmd) { /* DO command? */
2638 if (sim_do_depth >= MAX_DO_NEST_LVL) /* nest too deep? */
2639 stat = SCPE_NEST;
2640 else
2641 stat = do_cmd (sim_do_depth+1, cptr); /* exec DO cmd */
2642 }
2643 else
2644 stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */
2645 }
2646 else stat = SCPE_UNK; /* bad cmd given */
2647 echo = sim_do_echo; /* Allow for SET VERIFY */
2648 stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */
2649 stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */
2650 stat = SCPE_BARE_STATUS(stat); /* remove possible flag */
2651 if (((stat != SCPE_OK) && (stat != SCPE_EXPECT)) ||
2652 ((cmdp->action != &return_cmd) &&
2653 (cmdp->action != &goto_cmd) &&
2654 (cmdp->action != &on_cmd) &&
2655 (cmdp->action != &echo_cmd)))
2656 sim_last_cmd_stat = stat; /* save command error status */
2657 switch (stat) {
2658 case SCPE_AFAIL:
2659 staying = (sim_on_check[sim_do_depth] && /* if trap action defined */
2660 sim_on_actions[sim_do_depth][stat]); /* use it, otherwise exit */
2661 break;
2662 case SCPE_EXIT:
2663 staying = FALSE;
2664 break;
2665 case SCPE_OK:
2666 case SCPE_STEP:
2667 break;
2668 default:
2669 break;
2670 }
2671 if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */
2672 (stat != SCPE_STEP)) {
2673 if (!echo && !sim_quiet && /* report if not echoing */
2674 !stat_nomessage && /* and not suppressing messages */
2675 !(cmdp && cmdp->message)) { /* and not handling them specially */
2676 sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
2677 }
2678 }
2679 if (!stat_nomessage) { /* report error if not suppressed */
2680 if (cmdp && cmdp->message) /* special message handler */
2681 cmdp->message ((!echo && !sim_quiet) ? sim_do_ocptr[sim_do_depth] : NULL, stat);
2682 else
2683 if (stat >= SCPE_BASE) /* report error if not suppressed */
2684 sim_printf ("%s\n", sim_error_text (stat));
2685 }
2686 if (stat == SCPE_EXPECT) /* EXPECT status is non actionable */
2687 stat = SCPE_OK; /* so adjust it to SCPE_OK */
2688 if (staying &&
2689 (sim_on_check[sim_do_depth]) &&
2690 (stat != SCPE_OK) &&
2691 (stat != SCPE_STEP)) {
2692 if ((stat <= SCPE_MAX_ERR) && sim_on_actions[sim_do_depth][stat])
2693 sim_brk_setact (sim_on_actions[sim_do_depth][stat]);
2694 else
2695 sim_brk_setact (sim_on_actions[sim_do_depth][0]);
2696 }
2697 if (sim_vm_post != NULL)
2698 (*sim_vm_post) (TRUE);
2699 } while (staying);
2700 Cleanup_Return:
2701 fclose (fpin); /* close file */
2702 sim_gotofile = NULL;
2703 if (flag >= 0) {
2704 sim_do_echo = saved_sim_do_echo; /* restore echo state we entered with */
2705 sim_show_message = saved_sim_show_message; /* restore message display state we entered with */
2706 sim_on_inherit = saved_sim_on_inherit; /* restore ON inheritance state we entered with */
2707 }
2708 sim_quiet = saved_sim_quiet; /* restore quiet mode we entered with */
2709 if ((flag >= 0) || (!sim_on_inherit)) {
2710 for (i=0; i<SCPE_MAX_ERR; i++) { /* release any on commands */
2711 free (sim_on_actions[sim_do_depth][i]);
2712 sim_on_actions[sim_do_depth][i] = NULL;
2713 }
2714 sim_on_check[sim_do_depth] = 0; /* clear on mode */
2715 }
2716 if (flag >= 0)
2717 --sim_do_depth; /* unwind nesting */
2718 sim_brk_clract (); /* defang breakpoint actions */
2719 if (cmdp && (cmdp->action == &return_cmd) && (0 != *cptr)) { /* return command with argument? */
2720 sim_string_to_stat (cptr, &stat);
2721 sim_last_cmd_stat = stat; /* save explicit status as command error status */
2722 if (sim_switches & SWMASK ('Q'))
2723 stat |= SCPE_NOMESSAGE; /* suppress error message display (in caller) if requested */
2724 return stat; /* return with explicit return status */
2725 }
2726 return stat | SCPE_NOMESSAGE; /* suppress message since we've already done that here */
2727 }
2728
2729 /* Substitute_args - replace %n tokens in 'instr' with the do command's arguments
2730 and other enviroment variables
2731
2732 Calling sequence
2733 instr = input string
2734 instr_size = sizeof input string buffer
2735 do_arg[10] = arguments
2736
2737 Token "%0" expands to the command file name.
2738 Token %n (n being a single digit) expands to the n'th argument
2739 Tonen %* expands to the whole set of arguments (%1 ... %9)
2740
2741 The input sequence "\%" represents a literal "%", and "\\" represents a
2742 literal "\". All other character combinations are rendered literally.
2743
2744 Omitted parameters result in null-string substitutions.
2745
2746 A Tokens preceeded and followed by % characters are expanded as environment
2747 variables, and if one isn't found then can be one of several special
2748 variables:
2749 %DATE% yyyy-mm-dd
2750 %TIME% hh:mm:ss
2751 %STIME% hh_mm_ss
2752 %CTIME% Www Mmm dd hh:mm:ss yyyy
2753 %STATUS% Status value from the last command executed
2754 %TSTATUS% The text form of the last status value
2755 %SIM_VERIFY% The Verify/Verbose mode of the current Do command file
2756 %SIM_VERBOSE% The Verify/Verbose mode of the current Do command file
2757 %SIM_QUIET% The Quiet mode of the current Do command file
2758 %SIM_MESSAGE% The message display status of the current Do command file
2759 Environment variable lookups are done first with the precise name between
2760 the % characters and if that fails, then the name between the % characters
2761 is upcased and a lookup of that valus is attempted.
2762
2763 The first Space delimited token on the line is extracted in uppercase and
2764 then looked up as an environment variable. If found it the value is
2765 supstituted for the original string before expanding everything else. If
2766 it is not found, then the original beginning token on the line is left
2767 untouched.
2768 */
2769
sim_sub_args(char * instr,size_t instr_size,char * do_arg[])2770 void sim_sub_args (char *instr, size_t instr_size, char *do_arg[])
2771 {
2772 char gbuf[CBUFSIZE];
2773 char *ip = instr, *op, *oend, *tmpbuf;
2774 const char *ap;
2775 char rbuf[CBUFSIZE];
2776 int i;
2777 time_t now;
2778 struct tm *tmnow;
2779
2780 time(&now);
2781 tmnow = localtime(&now);
2782 tmpbuf = (char *)malloc(instr_size);
2783 op = tmpbuf;
2784 oend = tmpbuf + instr_size - 2;
2785 while (sim_isspace (*ip)) /* skip leading spaces */
2786 *op++ = *ip++;
2787 for (; *ip && (op < oend); ) {
2788 if ((ip [0] == '\\') && /* literal escape? */
2789 ((ip [1] == '%') || (ip [1] == '\\'))) { /* and followed by '%' or '\'? */
2790 ip++; /* skip '\' */
2791 *op++ = *ip++; /* copy escaped char */
2792 }
2793 else
2794 if ((*ip == '%') &&
2795 (sim_isalnum(ip[1]) || (ip[1] == '*') || (ip[1] == '_'))) {/* sub? */
2796 if ((ip[1] >= '0') && (ip[1] <= ('9'))) { /* %n = sub */
2797 ap = do_arg[ip[1] - '0'];
2798 for (i=0; i<ip[1] - '0'; ++i) /* make sure we're not past the list end */
2799 if (do_arg[i] == NULL) {
2800 ap = NULL;
2801 break;
2802 }
2803 ip = ip + 2;
2804 }
2805 else if (ip[1] == '*') { /* %1 ... %9 = sub */
2806 memset (rbuf, '\0', sizeof(rbuf));
2807 ap = rbuf;
2808 for (i=1; i<=9; ++i)
2809 if (do_arg[i] == NULL)
2810 break;
2811 else
2812 if ((sizeof(rbuf)-strlen(rbuf)) < (2 + strlen(do_arg[i]))) {
2813 if (strchr(do_arg[i], ' ')) { /* need to surround this argument with quotes */
2814 char quote = '"';
2815 if (strchr(do_arg[i], quote))
2816 quote = '\'';
2817 sprintf(&rbuf[strlen(rbuf)], "%s%c%s%c\"", (i != 1) ? " " : "", quote, do_arg[i], quote);
2818 }
2819 else
2820 sprintf(&rbuf[strlen(rbuf)], "%s%s", (i != 1) ? " " : "", do_arg[i]);
2821 }
2822 else
2823 break;
2824 ip = ip + 2;
2825 }
2826 else { /* environment variable */
2827 ap = NULL;
2828 get_glyph_nc (ip+1, gbuf, '%'); /* first try using the literal name */
2829 ap = getenv(gbuf);
2830 if (!ap) {
2831 get_glyph (ip+1, gbuf, '%'); /* now try using the upcased name */
2832 ap = getenv(gbuf);
2833 }
2834 ip += 1 + strlen (gbuf);
2835 if (*ip == '%') ++ip;
2836 if (!ap) {
2837 /* ISO 8601 format date/time info */
2838 if (!strcmp ("DATE", gbuf)) {
2839 sprintf (rbuf, "%4d-%02d-%02d", tmnow->tm_year+1900, tmnow->tm_mon+1, tmnow->tm_mday);
2840 ap = rbuf;
2841 }
2842 else if (!strcmp ("TIME", gbuf)) {
2843 sprintf (rbuf, "%02d:%02d:%02d", tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec);
2844 ap = rbuf;
2845 }
2846 else if (!strcmp ("DATETIME", gbuf)) {
2847 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);
2848 ap = rbuf;
2849 }
2850 /* Locale oriented formatted date/time info */
2851 if (!strcmp ("LDATE", gbuf)) {
2852 strftime (rbuf, sizeof(rbuf), "%x", tmnow);
2853 ap = rbuf;
2854 }
2855 else if (!strcmp ("LTIME", gbuf)) {
2856 strftime (rbuf, sizeof(rbuf), "%r", tmnow);
2857 ap = rbuf;
2858 }
2859 else if (!strcmp ("CTIME", gbuf)) {
2860 strftime (rbuf, sizeof(rbuf), "%c", tmnow);
2861 ap = rbuf;
2862 }
2863 /* Separate Date/Time info */
2864 else if (!strcmp ("DATE_YYYY", gbuf)) {/* Year (0000-9999) */
2865 strftime (rbuf, sizeof(rbuf), "%Y", tmnow);
2866 ap = rbuf;
2867 }
2868 else if (!strcmp ("DATE_YY", gbuf)) {/* Year (00-99) */
2869 strftime (rbuf, sizeof(rbuf), "%y", tmnow);
2870 ap = rbuf;
2871 }
2872 else if (!strcmp ("DATE_YC", gbuf)) {/* Century (year/100) */
2873 sprintf (rbuf, "%d", (tmnow->tm_year + 1900)/100);
2874 ap = rbuf;
2875 }
2876 else if ((!strcmp ("DATE_19XX_YY", gbuf)) || /* Year with same calendar */
2877 (!strcmp ("DATE_19XX_YYYY", gbuf))) {
2878 int year = tmnow->tm_year + 1900;
2879 int days = year - 2001;
2880 int leaps = days/4 - days/100 + days/400;
2881 int lyear = ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0));
2882 int selector = ((days + leaps + 7) % 7) + lyear * 7;
2883 static int years[] = {90, 91, 97, 98, 99, 94, 89,
2884 96, 80, 92, 76, 88, 72, 84};
2885 int cal_year = years[selector];
2886
2887 if (!strcmp ("DATE_19XX_YY", gbuf))
2888 sprintf (rbuf, "%d", cal_year); /* 2 digit year */
2889 else
2890 sprintf (rbuf, "%d", cal_year + 1900); /* 4 digit year */
2891 ap = rbuf;
2892 }
2893 else if (!strcmp ("DATE_MM", gbuf)) {/* Month number (01-12) */
2894 strftime (rbuf, sizeof(rbuf), "%m", tmnow);
2895 ap = rbuf;
2896 }
2897 else if (!strcmp ("DATE_MMM", gbuf)) {/* Month number (01-12) */
2898 strftime (rbuf, sizeof(rbuf), "%b", tmnow);
2899 ap = rbuf;
2900 }
2901 else if (!strcmp ("DATE_DD", gbuf)) {/* Day of Month (01-31) */
2902 strftime (rbuf, sizeof(rbuf), "%d", tmnow);
2903 ap = rbuf;
2904 }
2905 else if (!strcmp ("DATE_D", gbuf)) { /* ISO 8601 weekday number (1-7) */
2906 sprintf (rbuf, "%d", (tmnow->tm_wday ? tmnow->tm_wday : 7));
2907 ap = rbuf;
2908 }
2909 else if ((!strcmp ("DATE_WW", gbuf)) || /* ISO 8601 week number (01-53) */
2910 (!strcmp ("DATE_WYYYY", gbuf))) {/* ISO 8601 week year number (0000-9999) */
2911 int iso_yr = tmnow->tm_year + 1900;
2912 int iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;;
2913
2914 if (iso_wk == 0) {
2915 iso_yr = iso_yr - 1;
2916 tmnow->tm_yday += 365 + (((iso_yr % 4) == 0) ? 1 : 0); /* Adjust for Leap Year (Correct thru 2099) */
2917 iso_wk = (tmnow->tm_yday + 11 - (tmnow->tm_wday ? tmnow->tm_wday : 7))/7;
2918 }
2919 else
2920 if ((iso_wk == 53) && (((31 - tmnow->tm_mday) + tmnow->tm_wday) < 4)) {
2921 ++iso_yr;
2922 iso_wk = 1;
2923 }
2924 if (!strcmp ("DATE_WW", gbuf))
2925 sprintf (rbuf, "%02d", iso_wk);
2926 else
2927 sprintf (rbuf, "%04d", iso_yr);
2928 ap = rbuf;
2929 }
2930 else if (!strcmp ("DATE_JJJ", gbuf)) {/* day of year (001-366) */
2931 strftime (rbuf, sizeof(rbuf), "%j", tmnow);
2932 ap = rbuf;
2933 }
2934 else if (!strcmp ("TIME_HH", gbuf)) {/* Hour of day (00-23) */
2935 strftime (rbuf, sizeof(rbuf), "%H", tmnow);
2936 ap = rbuf;
2937 }
2938 else if (!strcmp ("TIME_MM", gbuf)) {/* Minute of hour (00-59) */
2939 strftime (rbuf, sizeof(rbuf), "%M", tmnow);
2940 ap = rbuf;
2941 }
2942 else if (!strcmp ("TIME_SS", gbuf)) {/* Second of minute (00-59) */
2943 strftime (rbuf, sizeof(rbuf), "%S", tmnow);
2944 ap = rbuf;
2945 }
2946 else if (!strcmp ("STATUS", gbuf)) {
2947 sprintf (rbuf, "%08X", sim_last_cmd_stat);
2948 ap = rbuf;
2949 }
2950 else if (!strcmp ("TSTATUS", gbuf)) {
2951 sprintf (rbuf, "%s", sim_error_text (sim_last_cmd_stat));
2952 ap = rbuf;
2953 }
2954 else if (!strcmp ("SIM_VERIFY", gbuf)) {
2955 sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
2956 ap = rbuf;
2957 }
2958 else if (!strcmp ("SIM_VERBOSE", gbuf)) {
2959 sprintf (rbuf, "%s", sim_do_echo ? "-V" : "");
2960 ap = rbuf;
2961 }
2962 else if (!strcmp ("SIM_QUIET", gbuf)) {
2963 sprintf (rbuf, "%s", sim_quiet ? "-Q" : "");
2964 ap = rbuf;
2965 }
2966 else if (!strcmp ("SIM_MESSAGE", gbuf)) {
2967 sprintf (rbuf, "%s", sim_show_message ? "" : "-Q");
2968 ap = rbuf;
2969 }
2970 }
2971 }
2972 if (ap) { /* non-null arg? */
2973 while (*ap && (op < oend)) /* copy the argument */
2974 *op++ = *ap++;
2975 }
2976 }
2977 else
2978 *op++ = *ip++;
2979 }
2980 *op = 0; /* term buffer */
2981 strcpy (instr, tmpbuf);
2982 free (tmpbuf);
2983 return;
2984 }
2985
2986 static
sim_cmp_string(const char * s1,const char * s2)2987 int sim_cmp_string (const char *s1, const char *s2)
2988 {
2989 long int v1, v2;
2990 char *ep1, *ep2;
2991
2992 v1 = strtol(s1+1, &ep1, 0);
2993 v2 = strtol(s2+1, &ep2, 0);
2994 if ((ep1 != s1 + strlen (s1) - 1) ||
2995 (ep2 != s2 + strlen (s2) - 1))
2996 return strcmp (s1, s2);
2997 if (v1 == v2)
2998 return 0;
2999 if (v1 < v2)
3000 return -1;
3001 return 1;
3002 }
3003
3004 /* Assert command
3005 If command
3006
3007 Syntax: ASSERT {NOT} {<dev>} <reg>{<logical-op><value>}<conditional-op><value>
3008 Syntax: IF {NOT} {<dev>} <reg>{<logical-op><value>}<conditional-op><value> commandtoprocess{; additionalcommandtoprocess}...
3009
3010 If NOT is specified, the resulting expression value is inverted.
3011 If <dev> is not specified, sim_dflt_dev (CPU) is assumed.
3012 <value> is expressed in the radix specified for <reg>.
3013 <logical-op> and <conditional-op> are the same as that
3014 allowed for examine and deposit search specifications.
3015
3016 Syntax: ASSERT {-i} {NOT} "<string1>" <compare-op> "<string2>"
3017 Syntax: IF {-i} {NOT} "<string1>" <compare-op> "<string2>" commandtoprocess{; additionalcommandtoprocess}...
3018
3019 If -i is specified, the comparisons are done in a case insensitive manner.
3020 If NOT is specified, the resulting expression value is inverted.
3021 "<string1>" and "<string2>" are quote delimited strings which include
3022 expansion references to environment variables in the simulator.
3023 <compare-op> can be any one of:
3024 == - equal
3025 EQU - equal
3026 != - not equal
3027 NEQ - not equal
3028 < - less than
3029 LSS - less than
3030 <= - less than or equal
3031 LEQ - less than or equal
3032 > - greater than
3033 GTR - greater than
3034 >= - greater than or equal
3035 GEQ - greater than or equal
3036 */
assert_cmd(int32 flag,CONST char * cptr)3037 t_stat assert_cmd (int32 flag, CONST char *cptr)
3038 {
3039 char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
3040 CONST char *tptr, *gptr;
3041 REG *rptr;
3042 uint32 idx = 0;
3043 t_value val;
3044 t_stat r;
3045 t_bool Not = FALSE;
3046 t_bool result;
3047 t_addr addr = 0;
3048 t_stat reason;
3049
3050 cptr = (CONST char *)get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, (CONST char *)cptr, &r);
3051 /* get sw, default */
3052 sim_stabr.boolop = sim_staba.boolop = -1; /* no relational op dflt */
3053 if (*cptr == 0) /* must be more */
3054 return SCPE_2FARG;
3055 tptr = get_glyph (cptr, gbuf, 0); /* get token */
3056 if (!strcmp (gbuf, "NOT")) { /* Conditional Inversion? */
3057 Not = TRUE; /* remember that, and */
3058 cptr = (CONST char *)tptr;
3059 }
3060 if (*cptr == '"') { /* quoted string comparison? */
3061 char op[CBUFSIZE];
3062 static struct {
3063 const char *op;
3064 int aval;
3065 int bval;
3066 t_bool invert;
3067 } *optr, compare_ops[] =
3068 {
3069 {"==", 0, 0, FALSE},
3070 {"EQU", 0, 0, FALSE},
3071 {"!=", 0, 0, TRUE},
3072 {"NEQ", 0, 0, TRUE},
3073 {"<", -1, -1, FALSE},
3074 {"LSS", -1, -1, FALSE},
3075 {"<=", 0, -1, FALSE},
3076 {"LEQ", 0, -1, FALSE},
3077 {">", 1, 1, FALSE},
3078 {"GTR", 1, 1, FALSE},
3079 {">=", 0, 1, FALSE},
3080 {"GEQ", 0, 1, FALSE},
3081 {NULL}};
3082
3083 tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\');
3084 /* get first string */
3085 if (!*tptr)
3086 return SCPE_2FARG;
3087 cptr += strlen (gbuf);
3088 while (sim_isspace (*cptr)) /* skip spaces */
3089 ++cptr;
3090 get_glyph (cptr, op, '"');
3091 for (optr = compare_ops; optr->op; optr++)
3092 if (0 == strcmp (op, optr->op))
3093 break;
3094 if (!optr->op)
3095 return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op);
3096 cptr += strlen (op);
3097 while (sim_isspace (*cptr)) /* skip spaces */
3098 ++cptr;
3099 cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\');
3100 /* get second string */
3101 if (*cptr) { /* more? */
3102 if (flag) /* ASSERT has no more args */
3103 return SCPE_2MARG;
3104 }
3105 else {
3106 if (!flag)
3107 return SCPE_2FARG; /* IF needs actions! */
3108 }
3109 result = sim_cmp_string (gbuf, gbuf2);
3110 result = ((result == optr->aval) || (result == optr->bval));
3111 if (optr->invert)
3112 result = !result;
3113 }
3114 else {
3115 cptr = get_glyph (cptr, gbuf, 0); /* get register */
3116 rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */
3117 if (rptr) { /* got register? */
3118 if (*gptr == '[') { /* subscript? */
3119 if (rptr->depth <= 1) /* array register? */
3120 return SCPE_ARG;
3121 idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */
3122 if ((gptr == tptr) || (*tptr++ != ']'))
3123 return SCPE_ARG;
3124 gptr = tptr; /* update */
3125 }
3126 else idx = 0; /* not array */
3127 if (idx >= rptr->depth) /* validate subscript */
3128 return SCPE_SUB;
3129 }
3130 else { /* not reg, check for memory */
3131 if (sim_dfdev && sim_vm_parse_addr) /* get addr */
3132 addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr);
3133 else addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev->dradix);
3134 if (gbuf == gptr) /* error? */
3135 return SCPE_NXREG;
3136 }
3137 if (*gptr != 0) /* more? must be search */
3138 get_glyph (gptr, gbuf, 0);
3139 else {
3140 if (*cptr == 0) /* must be more */
3141 return SCPE_2FARG;
3142 cptr = get_glyph (cptr, gbuf, 0); /* get search cond */
3143 }
3144 if (*cptr) { /* more? */
3145 if (flag) /* ASSERT has no more args */
3146 return SCPE_2MARG;
3147 }
3148 else {
3149 if (!flag)
3150 return SCPE_2FARG; /* IF needs actions! */
3151 }
3152 if (rptr) { /* Handle register case */
3153 if (!get_rsearch (gbuf, rptr->radix, &sim_stabr) || /* parse condition */
3154 (sim_stabr.boolop == -1)) /* relational op reqd */
3155 return SCPE_MISVAL;
3156 val = get_rval (rptr, idx); /* get register value */
3157 result = test_search (&val, &sim_stabr); /* test condition */
3158 }
3159 else { /* Handle memory case */
3160 if (!get_asearch (gbuf, sim_dfdev->dradix, &sim_staba) || /* parse condition */
3161 (sim_staba.boolop == -1)) /* relational op reqd */
3162 return SCPE_MISVAL;
3163 reason = get_aval (addr, sim_dfdev, sim_dfunit);/* get data */
3164 if (reason != SCPE_OK) /* return if error */
3165 return reason;
3166 result = test_search (sim_eval, &sim_staba); /* test condition */
3167 }
3168 }
3169 if (Not ^ result) {
3170 if (!flag)
3171 sim_brk_setact (cptr); /* set up IF actions */
3172 }
3173 else
3174 if (flag)
3175 return SCPE_AFAIL; /* return assert status */
3176 return SCPE_OK;
3177 }
3178
3179 /* Send command
3180
3181 Syntax: SEND {After=m},{Delay=n},"string-to-send"
3182
3183 After - is a positive integer representing a number of instruction delay
3184 before the initial characters is sent. The value specified
3185 in a after argument persists across SEND commands. The after
3186 parameter can be set by itself with SEND AFTER=n
3187 Delay - is a positive integer representing a minimal instruction delay
3188 before and between characters being sent. The value specified
3189 in a delay argument persists across SEND commands. The delay
3190 parameter can be set by itself with SEND DELAY=n
3191 String - must be quoted. Quotes may be either single or double but the
3192 opening anc closing quote characters must match. Within quotes
3193 C style character escapes are allowed.
3194 The following character escapes are explicitly supported:
3195 \r Sends the ASCII Carriage Return character (Decimal value 13)
3196 \n Sends the ASCII Linefeed character (Decimal value 10)
3197 \f Sends the ASCII Formfeed character (Decimal value 12)
3198 \t Sends the ASCII Horizontal Tab character (Decimal value 9)
3199 \v Sends the ASCII Vertical Tab character (Decimal value 11)
3200 \b Sends the ASCII Backspace character (Decimal value 8)
3201 \\ Sends the ASCII Backslash character (Decimal value 92)
3202 \' Sends the ASCII Single Quote character (Decimal value 39)
3203 \" Sends the ASCII Double Quote character (Decimal value 34)
3204 \? Sends the ASCII Question Mark character (Decimal value 63)
3205 \e Sends the ASCII Escape character (Decimal value 27)
3206 as well as octal character values of the form:
3207 \n{n{n}} where each n is an octal digit (0-7)
3208 and hext character values of the form:
3209 \xh{h} where each h is a hex digit (0-9A-Fa-f)
3210 */
3211
send_cmd(int32 flag,CONST char * cptr)3212 t_stat send_cmd (int32 flag, CONST char *cptr)
3213 {
3214 char gbuf[CBUFSIZE];
3215 CONST char *tptr;
3216 uint8 dbuf[CBUFSIZE];
3217 uint32 dsize = 0;
3218 uint32 delay = 0;
3219 uint32 after = 0;
3220 t_stat r;
3221 SEND *snd = NULL;
3222
3223 GET_SWITCHES (cptr); /* get switches */
3224 tptr = get_glyph (cptr, gbuf, ',');
3225 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3226 cptr = tptr;
3227 tptr = get_glyph (tptr, gbuf, ',');
3228 }
3229 else
3230 snd = sim_cons_get_send ();
3231
3232 while (*cptr) {
3233 if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) {
3234 delay = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3235 if (r != SCPE_OK)
3236 return sim_messagef (SCPE_ARG, "Invalid Delay Value\n");
3237 cptr = tptr;
3238 tptr = get_glyph (cptr, gbuf, ',');
3239 continue;
3240 }
3241 if ((!strncmp(gbuf, "AFTER=", 6)) && (gbuf[6])) {
3242 after = (uint32)get_uint (&gbuf[6], 10, 2000000000, &r);
3243 if (r != SCPE_OK)
3244 return sim_messagef (SCPE_ARG, "Invalid After Value\n");
3245 cptr = tptr;
3246 tptr = get_glyph (cptr, gbuf, ',');
3247 continue;
3248 }
3249 if ((*cptr == '"') || (*cptr == '\''))
3250 break;
3251 return SCPE_ARG;
3252 }
3253 if (*cptr) {
3254 if ((*cptr != '"') && (*cptr != '\''))
3255 return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
3256 cptr = get_glyph_quoted (cptr, gbuf, 0);
3257 if (*cptr != '\0')
3258 return SCPE_2MARG; /* No more arguments */
3259
3260 if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize))
3261 return sim_messagef (SCPE_ARG, "Invalid String\n");
3262 }
3263 if ((dsize == 0) && (delay == 0) && (after == 0))
3264 return SCPE_2FARG;
3265 return sim_send_input (snd, dbuf, dsize, after, delay);
3266 }
3267
sim_show_send(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3268 t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3269 {
3270 char gbuf[CBUFSIZE];
3271 CONST char *tptr;
3272 SEND *snd = NULL;
3273
3274 tptr = get_glyph (cptr, gbuf, ',');
3275 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3276 cptr = tptr;
3277 }
3278 else
3279 snd = sim_cons_get_send ();
3280 if (*cptr)
3281 return SCPE_2MARG;
3282 return sim_show_send_input (st, snd);
3283 }
3284
expect_cmd(int32 flag,CONST char * cptr)3285 t_stat expect_cmd (int32 flag, CONST char *cptr)
3286 {
3287 char gbuf[CBUFSIZE];
3288 CONST char *tptr;
3289 EXPECT *exp = NULL;
3290
3291 GET_SWITCHES (cptr); /* get switches */
3292 tptr = get_glyph (cptr, gbuf, ',');
3293 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3294 cptr = tptr;
3295 }
3296 else
3297 exp = sim_cons_get_expect ();
3298 if (flag)
3299 return sim_set_expect (exp, cptr);
3300 else
3301 return sim_set_noexpect (exp, cptr);
3302 }
3303
sim_show_expect(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3304 t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3305 {
3306 char gbuf[CBUFSIZE];
3307 CONST char *tptr;
3308 EXPECT *exp = NULL;
3309
3310 tptr = get_glyph (cptr, gbuf, ',');
3311 if (sim_isalpha(gbuf[0]) && (strchr (gbuf, ':'))) {
3312 cptr = tptr;
3313 }
3314 else
3315 exp = sim_cons_get_expect ();
3316 if (*cptr && (*cptr != '"') && (*cptr != '\''))
3317 return SCPE_ARG; /* String must be quote delimited */
3318 tptr = get_glyph_quoted (cptr, gbuf, 0);
3319 if (*tptr != '\0')
3320 return SCPE_2MARG; /* No more arguments */
3321 if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\''))
3322 return SCPE_ARG; /* String must be quote delimited */
3323 return sim_exp_show (st, exp, gbuf);
3324 }
3325
3326 /* Goto command */
3327
goto_cmd(int32 flag,CONST char * fcptr)3328 t_stat goto_cmd (int32 flag, CONST char *fcptr)
3329 {
3330 char cbuf[CBUFSIZE], gbuf[CBUFSIZE], gbuf1[CBUFSIZE];
3331 const char *cptr;
3332 long fpos;
3333 int32 saved_do_echo = sim_do_echo;
3334 int32 saved_goto_line = sim_goto_line[sim_do_depth];
3335
3336 if (NULL == sim_gotofile) return SCPE_UNK; /* only valid inside of do_cmd */
3337 get_glyph (fcptr, gbuf1, 0);
3338 if ('\0' == gbuf1[0]) return SCPE_ARG; /* unspecified goto target */
3339 fpos = ftell(sim_gotofile); /* Save start position */
3340 rewind(sim_gotofile); /* start search for label */
3341 sim_goto_line[sim_do_depth] = 0; /* reset line number */
3342 sim_do_echo = 0; /* Don't echo while searching for label */
3343 while (1) {
3344 cptr = read_line (cbuf, sizeof(cbuf), sim_gotofile);/* get cmd line */
3345 if (cptr == NULL) break; /* exit on eof */
3346 sim_goto_line[sim_do_depth] += 1; /* record line number */
3347 if (*cptr == 0) continue; /* ignore blank */
3348 if (*cptr != ':') continue; /* ignore non-labels */
3349 ++cptr; /* skip : */
3350 while (sim_isspace (*cptr)) ++cptr; /* skip blanks */
3351 cptr = get_glyph (cptr, gbuf, 0); /* get label glyph */
3352 if (0 == strcmp(gbuf, gbuf1)) {
3353 sim_brk_clract (); /* goto defangs current actions */
3354 sim_do_echo = saved_do_echo; /* restore echo mode */
3355 if (sim_do_echo) /* echo if -v */
3356 sim_printf("%s> %s\n", do_position(), cbuf);
3357 return SCPE_OK;
3358 }
3359 }
3360 sim_do_echo = saved_do_echo; /* restore echo mode */
3361 fseek(sim_gotofile, fpos, SEEK_SET); /* resture start position */
3362 sim_goto_line[sim_do_depth] = saved_goto_line; /* restore start line number */
3363 return SCPE_ARG;
3364 }
3365
3366 /* Return command */
3367 /* The return command is invalid unless encountered in a do_cmd context, */
3368 /* and in that context, it is handled as a special case inside of do_cmd() */
3369 /* and not dispatched here, so if we get here a return has been issued from */
3370 /* interactive input */
3371
return_cmd(int32 flag,CONST char * fcptr)3372 t_stat return_cmd (int32 flag, CONST char *fcptr)
3373 {
3374 return SCPE_UNK; /* only valid inside of do_cmd */
3375 }
3376
3377 /* Shift command */
3378 /* The shift command is invalid unless encountered in a do_cmd context, */
3379 /* and in that context, it is handled as a special case inside of do_cmd() */
3380 /* and not dispatched here, so if we get here a shift has been issued from */
3381 /* interactive input (it is not valid interactively since it would have to */
3382 /* mess with the program's argv which is owned by the C runtime library */
3383
shift_cmd(int32 flag,CONST char * fcptr)3384 t_stat shift_cmd (int32 flag, CONST char *fcptr)
3385 {
3386 return SCPE_UNK; /* only valid inside of do_cmd */
3387 }
3388
3389 /* Call command */
3390 /* The call command is invalid unless encountered in a do_cmd context, */
3391 /* and in that context, it is handled as a special case inside of do_cmd() */
3392 /* and not dispatched here, so if we get here a call has been issued from */
3393 /* interactive input */
3394
call_cmd(int32 flag,CONST char * fcptr)3395 t_stat call_cmd (int32 flag, CONST char *fcptr)
3396 {
3397 char cbuf[2*CBUFSIZE], gbuf[CBUFSIZE];
3398 const char *cptr;
3399
3400 if (NULL == sim_gotofile) return SCPE_UNK; /* only valid inside of do_cmd */
3401 cptr = get_glyph (fcptr, gbuf, 0);
3402 if ('\0' == gbuf[0]) return SCPE_ARG; /* unspecified goto target */
3403 snprintf(cbuf, sizeof (cbuf), "%s %s", sim_do_filename[sim_do_depth], cptr);
3404 sim_switches |= SWMASK ('O'); /* inherit ON state and actions */
3405 return do_cmd_label (flag, cbuf, gbuf);
3406 }
3407
3408 /* On command */
3409
on_cmd(int32 flag,CONST char * cptr)3410 t_stat on_cmd (int32 flag, CONST char *cptr)
3411 {
3412 char gbuf[CBUFSIZE];
3413 t_stat cond;
3414
3415 cptr = get_glyph (cptr, gbuf, 0);
3416 if ('\0' == gbuf[0]) return SCPE_ARG; /* unspecified condition */
3417 if (0 == strcmp("ERROR", gbuf))
3418 cond = 0;
3419 else
3420 if (SCPE_OK != sim_string_to_stat (gbuf, &cond))
3421 return SCPE_ARG;
3422 if ((NULL == cptr) || ('\0' == *cptr)) { /* Empty Action */
3423 free(sim_on_actions[sim_do_depth][cond]); /* Clear existing condition */
3424 sim_on_actions[sim_do_depth][cond] = NULL; }
3425 else {
3426 sim_on_actions[sim_do_depth][cond] =
3427 (char *)realloc(sim_on_actions[sim_do_depth][cond], 1+strlen(cptr));
3428 strcpy(sim_on_actions[sim_do_depth][cond], cptr);
3429 }
3430 return SCPE_OK;
3431 }
3432
3433 /* noop command */
3434 /* The noop command (IGNORE, PROCEED) does nothing */
3435
noop_cmd(int32 flag,CONST char * cptr)3436 t_stat noop_cmd (int32 flag, CONST char *cptr)
3437 {
3438 if (cptr && (*cptr != 0)) /* now eol? */
3439 return SCPE_2MARG;
3440 return SCPE_OK; /* we're happy doing nothing */
3441 }
3442
3443 /* Set on/noon routine */
3444
set_on(int32 flag,CONST char * cptr)3445 t_stat set_on (int32 flag, CONST char *cptr)
3446 {
3447 if ((flag) && (cptr) && (*cptr)) { /* Set ON with arg */
3448 char gbuf[CBUFSIZE];
3449
3450 cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */
3451 if (((MATCH_CMD(gbuf,"INHERIT")) &&
3452 (MATCH_CMD(gbuf,"NOINHERIT"))) ||
3453 (*cptr))
3454 return SCPE_2MARG;
3455 if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"INHERIT")))
3456 sim_on_inherit = 1;
3457 if ((gbuf[0]) && (0 == MATCH_CMD(gbuf,"NOINHERIT")))
3458 sim_on_inherit = 0;
3459 return SCPE_OK;
3460 }
3461 if (cptr && (*cptr != 0)) /* now eol? */
3462 return SCPE_2MARG;
3463 sim_on_check[sim_do_depth] = flag;
3464 if ((sim_do_depth != 0) &&
3465 (NULL == sim_on_actions[sim_do_depth][0])) { /* default handler set? */
3466 sim_on_actions[sim_do_depth][0] = /* No, so make "RETURN" */
3467 (char *)malloc(1+strlen("RETURN")); /* be the default action */
3468 strcpy(sim_on_actions[sim_do_depth][0], "RETURN");
3469 }
3470 if ((sim_do_depth != 0) &&
3471 (NULL == sim_on_actions[sim_do_depth][SCPE_AFAIL])) {/* handler set for AFAIL? */
3472 sim_on_actions[sim_do_depth][SCPE_AFAIL] = /* No, so make "RETURN" */
3473 (char *)malloc(1+strlen("RETURN")); /* be the action */
3474 strcpy(sim_on_actions[sim_do_depth][SCPE_AFAIL], "RETURN");
3475 }
3476 return SCPE_OK;
3477 }
3478
3479 /* Set verify/noverify routine */
3480
set_verify(int32 flag,CONST char * cptr)3481 t_stat set_verify (int32 flag, CONST char *cptr)
3482 {
3483 if (cptr && (*cptr != 0)) /* now eol? */
3484 return SCPE_2MARG;
3485 if (flag == sim_do_echo) /* already set correctly? */
3486 return SCPE_OK;
3487 sim_do_echo = flag;
3488 return SCPE_OK;
3489 }
3490
3491 /* Set message/nomessage routine */
3492
set_message(int32 flag,CONST char * cptr)3493 t_stat set_message (int32 flag, CONST char *cptr)
3494 {
3495 if (cptr && (*cptr != 0)) /* now eol? */
3496 return SCPE_2MARG;
3497 if (flag == sim_show_message) /* already set correctly? */
3498 return SCPE_OK;
3499 sim_show_message = flag;
3500 return SCPE_OK;
3501 }
3502
3503 /* Set quiet/noquiet routine */
3504
set_quiet(int32 flag,CONST char * cptr)3505 t_stat set_quiet (int32 flag, CONST char *cptr)
3506 {
3507 if (cptr && (*cptr != 0)) /* now eol? */
3508 return SCPE_2MARG;
3509 if (flag == sim_quiet) /* already set correctly? */
3510 return SCPE_OK;
3511 sim_quiet = flag;
3512 return SCPE_OK;
3513 }
3514
3515 /* Set environment routine */
3516
sim_set_environment(int32 flag,CONST char * cptr)3517 t_stat sim_set_environment (int32 flag, CONST char *cptr)
3518 {
3519 char varname[CBUFSIZE];
3520
3521 if ((!cptr) || (*cptr == 0)) /* now eol? */
3522 return SCPE_2FARG;
3523 cptr = get_glyph (cptr, varname, '='); /* get environment variable name */
3524 setenv(varname, cptr, 1);
3525 return SCPE_OK;
3526 }
3527
3528 /* Set command */
3529
set_cmd(int32 flag,CONST char * cptr)3530 t_stat set_cmd (int32 flag, CONST char *cptr)
3531 {
3532 uint32 lvl = 0;
3533 t_stat r;
3534 char gbuf[CBUFSIZE], *cvptr;
3535 CONST char *svptr;
3536 DEVICE *dptr;
3537 UNIT *uptr;
3538 MTAB *mptr;
3539 CTAB *gcmdp;
3540 C1TAB *ctbr = NULL, *glbr;
3541
3542 GET_SWITCHES (cptr); /* get switches */
3543 if (*cptr == 0) /* must be more */
3544 return SCPE_2FARG;
3545 cptr = get_glyph (svptr = cptr, gbuf, 0); /* get glob/dev/unit */
3546
3547 if ((dptr = find_dev (gbuf))) { /* device match? */
3548 uptr = dptr->units; /* first unit */
3549 ctbr = set_dev_tab; /* global table */
3550 lvl = MTAB_VDV; /* device match */
3551 GET_SWITCHES (cptr); /* get more switches */
3552 }
3553 else if ((dptr = find_unit (gbuf, &uptr))) { /* unit match? */
3554 if (uptr == NULL) /* invalid unit */
3555 return SCPE_NXUN;
3556 ctbr = set_unit_tab; /* global table */
3557 lvl = MTAB_VUN; /* unit match */
3558 GET_SWITCHES (cptr); /* get more switches */
3559 }
3560 else if ((gcmdp = find_ctab (set_glob_tab, gbuf))) { /* global? */
3561 GET_SWITCHES (cptr); /* get more switches */
3562 return gcmdp->action (gcmdp->arg, cptr); /* do the rest */
3563 }
3564 else {
3565 if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3566 if ((cvptr = strchr (gbuf, '='))) /* = value? */
3567 *cvptr++ = 0;
3568 for (mptr = sim_dflt_dev->modifiers; mptr->mask != 0; mptr++) {
3569 if (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)) {
3570 dptr = sim_dflt_dev;
3571 cptr = svptr;
3572 while (sim_isspace(*cptr))
3573 ++cptr;
3574 break;
3575 }
3576 }
3577 }
3578 if (!dptr)
3579 return SCPE_NXDEV; /* no match */
3580 lvl = MTAB_VDV; /* device match */
3581 uptr = dptr->units; /* first unit */
3582 }
3583 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) /* must be more */
3584 return SCPE_2FARG;
3585 GET_SWITCHES (cptr); /* get more switches */
3586
3587 while (*cptr != 0) { /* do all mods */
3588 cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */
3589 if (0 == strcmp (gbuf, ";"))
3590 break;
3591 if ((cvptr = strchr (gbuf, '='))) /* = value? */
3592 *cvptr++ = 0;
3593 for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3594 if ((mptr->mstring) && /* match string */
3595 (MATCH_CMD (gbuf, mptr->mstring) == 0)) { /* matches option? */
3596 if (mptr->mask & MTAB_XTD) { /* extended? */
3597 if (((lvl & mptr->mask) & ~MTAB_XTD) == 0)
3598 return SCPE_ARG;
3599 if ((lvl == MTAB_VUN) && (uptr->flags & UNIT_DIS))
3600 return SCPE_UDIS; /* unit disabled? */
3601 if (mptr->valid) { /* validation rtn? */
3602 if (cvptr && MODMASK(mptr,MTAB_QUOTE)) {
3603 svptr = get_glyph_quoted (svptr, gbuf, ',');
3604 if ((cvptr = strchr (gbuf, '='))) {
3605 *cvptr++ = 0;
3606 cptr = svptr;
3607 }
3608 }
3609 else {
3610 if (cvptr && MODMASK(mptr,MTAB_NC)) {
3611 get_glyph_nc (svptr, gbuf, ',');
3612 if ((cvptr = strchr (gbuf, '=')))
3613 *cvptr++ = 0;
3614 }
3615 }
3616 r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);
3617 if (r != SCPE_OK)
3618 return r;
3619 }
3620 else if (!mptr->desc) /* value desc? */
3621 break;
3622 else if (cvptr) /* = value? */
3623 return SCPE_ARG;
3624 else *((int32 *) mptr->desc) = mptr->match;
3625 } /* end if xtd */
3626 else { /* old style */
3627 if (cvptr) /* = value? */
3628 return SCPE_ARG;
3629 if (uptr->flags & UNIT_DIS) /* disabled? */
3630 return SCPE_UDIS;
3631 if ((mptr->valid) && /* invalid? */
3632 ((r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc)) != SCPE_OK))
3633 return r;
3634 uptr->flags = (uptr->flags & ~(mptr->mask)) |
3635 (mptr->match & mptr->mask); /* set new value */
3636 } /* end else xtd */
3637 break; /* terminate for */
3638 } /* end if match */
3639 } /* end for */
3640 if (!mptr || (mptr->mask == 0)) { /* no match? */
3641 if ((glbr = find_c1tab (ctbr, gbuf))) { /* global match? */
3642 r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */
3643 if (r != SCPE_OK)
3644 return r;
3645 }
3646 else if (!dptr->modifiers) /* no modifiers? */
3647 return SCPE_NOPARAM;
3648 else return SCPE_NXPAR;
3649 } /* end if no mat */
3650 } /* end while */
3651 return SCPE_OK; /* done all */
3652 }
3653
3654 /* Match CTAB/CTAB1 name */
3655
find_ctab(CTAB * tab,const char * gbuf)3656 CTAB *find_ctab (CTAB *tab, const char *gbuf)
3657 {
3658 if (!tab)
3659 return NULL;
3660 for (; tab->name != NULL; tab++) {
3661 if (MATCH_CMD (gbuf, tab->name) == 0)
3662 return tab;
3663 }
3664 return NULL;
3665 }
3666
find_c1tab(C1TAB * tab,const char * gbuf)3667 C1TAB *find_c1tab (C1TAB *tab, const char *gbuf)
3668 {
3669 if (!tab)
3670 return NULL;
3671 for (; tab->name != NULL; tab++) {
3672 if (MATCH_CMD (gbuf, tab->name) == 0)
3673 return tab;
3674 }
3675 return NULL;
3676 }
3677
3678 /* Set device data radix routine */
3679
set_dev_radix(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3680 t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3681 {
3682 if (cptr)
3683 return SCPE_ARG;
3684 dptr->dradix = flag & 037;
3685 return SCPE_OK;
3686 }
3687
3688 /* Set device enabled/disabled routine */
3689
set_dev_enbdis(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3690 t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3691 {
3692 UNIT *up;
3693 uint32 i;
3694
3695 if (cptr)
3696 return SCPE_ARG;
3697 if ((dptr->flags & DEV_DISABLE) == 0) /* allowed? */
3698 return SCPE_NOFNC;
3699 if (flag) { /* enable? */
3700 if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */
3701 return SCPE_OK;
3702 dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */
3703 }
3704 else {
3705 if (dptr->flags & DEV_DIS) /* already dsb? ok */
3706 return SCPE_OK;
3707 for (i = 0; i < dptr->numunits; i++) { /* check units */
3708 up = (dptr->units) + i; /* att or active? */
3709 if ((up->flags & UNIT_ATT) || sim_is_active (up))
3710 return SCPE_NOFNC; /* can't do it */
3711 }
3712 dptr->flags = dptr->flags | DEV_DIS; /* disable */
3713 }
3714 if (dptr->reset) /* reset device */
3715 return dptr->reset (dptr);
3716 else return SCPE_OK;
3717 }
3718
3719 /* Set unit enabled/disabled routine */
3720
set_unit_enbdis(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3721 t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3722 {
3723 if (cptr)
3724 return SCPE_ARG;
3725 if (!(uptr->flags & UNIT_DISABLE)) /* allowed? */
3726 return SCPE_NOFNC;
3727 if (flag) /* enb? enable */
3728 uptr->flags = uptr->flags & ~UNIT_DIS;
3729 else {
3730 if ((uptr->flags & UNIT_ATT) || /* dsb */
3731 sim_is_active (uptr)) /* more tests */
3732 return SCPE_NOFNC;
3733 uptr->flags = uptr->flags | UNIT_DIS; /* disable */
3734 }
3735 return SCPE_OK;
3736 }
3737
3738 /* Set device debug enabled/disabled routine */
3739
set_dev_debug(DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)3740 t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
3741 {
3742 char gbuf[CBUFSIZE];
3743 DEBTAB *dep;
3744
3745 if ((dptr->flags & DEV_DEBUG) == 0)
3746 return SCPE_NOFNC;
3747 if (cptr == NULL) { /* no arguments? */
3748 dptr->dctrl = flag ? (dptr->debflags ? flag : 0xFFFFFFFF) : 0;/* disable/enable w/o table */
3749 if (flag && dptr->debflags) { /* enable with table? */
3750 for (dep = dptr->debflags; dep->name != NULL; dep++)
3751 dptr->dctrl = dptr->dctrl | dep->mask; /* set all */
3752 }
3753 return SCPE_OK;
3754 }
3755 if (dptr->debflags == NULL) /* must have table */
3756 return SCPE_ARG;
3757 while (*cptr) {
3758 cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */
3759 for (dep = dptr->debflags; dep->name != NULL; dep++) {
3760 if (strcmp (dep->name, gbuf) == 0) { /* match? */
3761 if (flag)
3762 dptr->dctrl = dptr->dctrl | dep->mask;
3763 else dptr->dctrl = dptr->dctrl & ~dep->mask;
3764 break;
3765 }
3766 } /* end for */
3767 if (dep->mask == 0) /* no match? */
3768 return SCPE_ARG;
3769 } /* end while */
3770 return SCPE_OK;
3771 }
3772
3773 /* Show command */
3774
show_cmd(int32 flag,CONST char * cptr)3775 t_stat show_cmd (int32 flag, CONST char *cptr)
3776 {
3777 t_stat r;
3778
3779 cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r);
3780 /* get sw, ofile */
3781 if (!cptr) /* error? */
3782 return r;
3783 if (sim_ofile) { /* output file? */
3784 r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */
3785 fclose (sim_ofile);
3786 }
3787 else {
3788 r = show_cmd_fi (stdout, flag, cptr); /* no, stdout, log */
3789 if (sim_log && (sim_log != stdout))
3790 show_cmd_fi (sim_log, flag, cptr);
3791 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
3792 show_cmd_fi (sim_deb, flag, cptr);
3793 }
3794 return r;
3795 }
3796
show_cmd_fi(FILE * ofile,int32 flag,CONST char * cptr)3797 t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr)
3798 {
3799 uint32 lvl = 0xFFFFFFFF;
3800 char gbuf[CBUFSIZE], *cvptr;
3801 CONST char *svptr;
3802 DEVICE *dptr;
3803 UNIT *uptr;
3804 MTAB *mptr;
3805 SHTAB *shtb = NULL, *shptr;
3806
3807 GET_SWITCHES (cptr); /* get switches */
3808 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) /* must be more */
3809 return SCPE_2FARG;
3810 cptr = get_glyph (svptr = cptr, gbuf, 0); /* get next glyph */
3811
3812 if ((dptr = find_dev (gbuf))) { /* device match? */
3813 uptr = dptr->units; /* first unit */
3814 shtb = show_dev_tab; /* global table */
3815 lvl = MTAB_VDV; /* device match */
3816 GET_SWITCHES (cptr); /* get more switches */
3817 }
3818 else if ((dptr = find_unit (gbuf, &uptr))) { /* unit match? */
3819 if (uptr == NULL) /* invalid unit */
3820 return sim_messagef (SCPE_NXUN, "Non-existent unit: %s\n", gbuf);
3821 if (uptr->flags & UNIT_DIS) /* disabled? */
3822 return sim_messagef (SCPE_UDIS, "Unit disabled: %s\n", gbuf);
3823 shtb = show_unit_tab; /* global table */
3824 lvl = MTAB_VUN; /* unit match */
3825 GET_SWITCHES (cptr); /* get more switches */
3826 }
3827 else if ((shptr = find_shtab (show_glob_tab, gbuf))) { /* global? */
3828 GET_SWITCHES (cptr); /* get more switches */
3829 return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);
3830 }
3831 else {
3832 if (sim_dflt_dev && sim_dflt_dev->modifiers) {
3833 if ((cvptr = strchr (gbuf, '='))) /* = value? */
3834 *cvptr++ = 0;
3835 for (mptr = sim_dflt_dev->modifiers; mptr && (mptr->mask != 0); mptr++) {
3836 if ((((mptr->mask & MTAB_VDV) == MTAB_VDV) &&
3837 (mptr->pstring && (MATCH_CMD (gbuf, mptr->pstring) == 0))) ||
3838 (!(mptr->mask & MTAB_VDV) && (mptr->mstring && (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {
3839 dptr = sim_dflt_dev;
3840 lvl = MTAB_VDV; /* device match */
3841 cptr = svptr;
3842 while (sim_isspace(*cptr))
3843 ++cptr;
3844 break;
3845 }
3846 }
3847 }
3848 if (!dptr) {
3849 if (sim_dflt_dev && (shptr = find_shtab (show_dev_tab, gbuf))) /* global match? */
3850 return shptr->action (ofile, sim_dflt_dev, uptr, shptr->arg, cptr);
3851 else
3852 return sim_messagef (SCPE_NXDEV, "Non-existent device: %s\n", gbuf);/* no match */
3853 }
3854 }
3855
3856 if ((*cptr == 0) || (*cptr == ';') || (*cptr == '#')) { /* now eol? */
3857 return (lvl == MTAB_VDV)?
3858 show_device (ofile, dptr, 0):
3859 show_unit (ofile, dptr, uptr, -1);
3860 }
3861 GET_SWITCHES (cptr); /* get more switches */
3862
3863 while (*cptr != 0) { /* do all mods */
3864 cptr = get_glyph (cptr, gbuf, ','); /* get modifier */
3865 if ((cvptr = strchr (gbuf, '='))) /* = value? */
3866 *cvptr++ = 0;
3867 for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {
3868 if (((mptr->mask & MTAB_XTD)? /* right level? */
3869 ((mptr->mask & lvl) == lvl): (MTAB_VUN & lvl)) &&
3870 ((mptr->disp && mptr->pstring && /* named disp? */
3871 (MATCH_CMD (gbuf, mptr->pstring) == 0))
3872 )) {
3873 if (cvptr && !MODMASK(mptr,MTAB_SHP))
3874 return sim_messagef (SCPE_ARG, "Invalid Argument: %s=%s\n", gbuf, cvptr);
3875 show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);
3876 break;
3877 } /* end if */
3878 } /* end for */
3879 if (!mptr || (mptr->mask == 0)) { /* no match? */
3880 if (shtb && (shptr = find_shtab (shtb, gbuf))) {/* global match? */
3881 t_stat r;
3882
3883 r = shptr->action (ofile, dptr, uptr, shptr->arg, cptr);
3884 if (r != SCPE_OK)
3885 return r;
3886 }
3887 else {
3888 if (!dptr->modifiers) /* no modifiers? */
3889 return sim_messagef (SCPE_NOPARAM, "%s device has no parameters\n", dptr->name);
3890 else
3891 return sim_messagef (SCPE_NXPAR, "Non-existent parameter: %s\n", gbuf);
3892 }
3893 } /* end if */
3894 } /* end while */
3895 return SCPE_OK;
3896 }
3897
find_shtab(SHTAB * tab,const char * gbuf)3898 SHTAB *find_shtab (SHTAB *tab, const char *gbuf)
3899 {
3900 if (!tab)
3901 return NULL;
3902 for (; tab->name != NULL; tab++) {
3903 if (MATCH_CMD (gbuf, tab->name) == 0)
3904 return tab;
3905 }
3906 return NULL;
3907 }
3908
3909 /* Show device and unit */
3910
show_device(FILE * st,DEVICE * dptr,int32 flag)3911 t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
3912 {
3913 uint32 j, udbl, ucnt;
3914 UNIT *uptr;
3915 int32 toks = 0;
3916
3917 fprintf (st, "%s", sim_dname (dptr)); /* print dev name */
3918 if ((flag == 2) && dptr->description) {
3919 fprintf (st, "\t%s\n", dptr->description(dptr));
3920 }
3921 else {
3922 if ((sim_switches & SWMASK ('D')) && dptr->description)
3923 fprintf (st, "\t%s\n", dptr->description(dptr));
3924 }
3925 if (qdisable (dptr)) { /* disabled? */
3926 fprintf (st, "\tdisabled\n");
3927 return SCPE_OK;
3928 }
3929 for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */
3930 uptr = dptr->units + j;
3931 if (!(uptr->flags & UNIT_DIS)) /* count enabled units */
3932 ucnt++;
3933 else if (uptr->flags & UNIT_DISABLE)
3934 udbl++; /* count user-disabled */
3935 }
3936 show_all_mods (st, dptr, dptr->units, MTAB_VDV, &toks); /* show dev mods */
3937 if (dptr->numunits == 0) {
3938 if (toks)
3939 fprintf (st, "\n");
3940 }
3941 else {
3942 if (ucnt == 0) {
3943 fprint_sep (st, &toks);
3944 fprintf (st, "all units disabled\n");
3945 }
3946 else if ((ucnt > 1) || (udbl > 0)) {
3947 fprint_sep (st, &toks);
3948 fprintf (st, "%d units\n", ucnt + udbl);
3949 }
3950 else
3951 if ((flag != 2) || !dptr->description || toks)
3952 fprintf (st, "\n");
3953 toks = 0;
3954 }
3955 if (flag) /* dev only? */
3956 return SCPE_OK;
3957 for (j = 0; j < dptr->numunits; j++) { /* loop thru units */
3958 uptr = dptr->units + j;
3959 if ((uptr->flags & UNIT_DIS) == 0)
3960 show_unit (st, dptr, uptr, ucnt + udbl);
3961 }
3962 return SCPE_OK;
3963 }
3964
fprint_sep(FILE * st,int32 * tokens)3965 void fprint_sep (FILE *st, int32 *tokens)
3966 {
3967 fprintf (st, "%s", (*tokens > 0) ? ", " : "\t");
3968 *tokens += 1;
3969 }
3970
show_unit(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag)3971 t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
3972 {
3973 int32 u = (int32)(uptr - dptr->units);
3974 int32 toks = 0;
3975
3976 if (flag > 1)
3977 fprintf (st, " %s%d", sim_dname (dptr), u);
3978 else if (flag < 0)
3979 fprintf (st, "%s%d", sim_dname (dptr), u);
3980 if (uptr->flags & UNIT_FIX) {
3981 fprint_sep (st, &toks);
3982 fprint_capac (st, dptr, uptr);
3983 }
3984 if (uptr->flags & UNIT_ATT) {
3985 fprint_sep (st, &toks);
3986 fprintf (st, "attached to %s", uptr->filename);
3987 if (uptr->flags & UNIT_RO)
3988 fprintf (st, ", read only");
3989 }
3990 else {
3991 if (uptr->flags & UNIT_ATTABLE) {
3992 fprint_sep (st, &toks);
3993 fprintf (st, "not attached");
3994 }
3995 }
3996 show_all_mods (st, dptr, uptr, MTAB_VUN, &toks); /* show unit mods */
3997 if (toks || (flag < 0) || (flag > 1))
3998 fprintf (st, "\n");
3999 return SCPE_OK;
4000 }
4001
sprint_capac(DEVICE * dptr,UNIT * uptr)4002 const char *sprint_capac (DEVICE *dptr, UNIT *uptr)
4003 {
4004 static char capac_buf[((CHAR_BIT * sizeof (t_value) * 4 + 3)/3) + 8];
4005 t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000;
4006 t_addr mval;
4007 t_addr psize = uptr->capac;
4008 char *scale, *width;
4009
4010 if (sim_switches & SWMASK ('B'))
4011 kval = 1024;
4012 mval = kval * kval;
4013 if (dptr->flags & DEV_SECTORS) {
4014 kval = kval / 512;
4015 mval = mval / 512;
4016 }
4017 if ((dptr->dwidth / dptr->aincr) > 8)
4018 width = "W";
4019 else
4020 width = "B";
4021 if ((psize < (kval * 10)) &&
4022 (0 != (psize % kval))) {
4023 scale = "";
4024 }
4025 else if ((psize < (mval * 10)) &&
4026 (0 != (psize % mval))){
4027 scale = "K";
4028 psize = psize / kval;
4029 }
4030 else {
4031 scale = "M";
4032 psize = psize / mval;
4033 }
4034 sprint_val (capac_buf, (t_value) psize, 10, T_ADDR_W, PV_LEFT);
4035 sprintf (&capac_buf[strlen (capac_buf)], "%s%s", scale, width);
4036 return capac_buf;
4037 }
4038
fprint_capac(FILE * st,DEVICE * dptr,UNIT * uptr)4039 void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)
4040 {
4041 fprintf (st, "%s", sprint_capac (dptr, uptr));
4042 }
4043
4044 /* Show <global name> processors */
4045
show_prom(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4046 t_stat show_prom (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4047 {
4048 #define DPS8_SCP
4049 #define PROM SCPPROM
4050 #include "../dps8/dps8_prom.h"
4051 int n = 6;
4052 int l = 174; /* end of populated area */
4053 sim_printf(" PROM initialization data: \n\n");
4054 for (int prombyte = 0; prombyte < l; ++prombyte) {
4055 if (prombyte % n == 0 && prombyte != 0) {
4056 sim_printf("\n"); }
4057 sim_printf("%03d: %02x [", prombyte, PROM[prombyte]);
4058 if (PROM[prombyte] > 31) {
4059 if (PROM[prombyte] < 128) {
4060 sim_printf("%c] ", (unsigned char)PROM[prombyte]); }
4061 } else {
4062 sim_printf(".] "); }
4063 }
4064 sim_printf("\n");
4065 return 0;
4066 }
4067
show_buildinfo(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4068 t_stat show_buildinfo (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4069 {
4070 fprintf (st, " Build Information:\n");
4071 #if defined(BUILDINFO_scp) && defined(SYSDEFS_USED)
4072 fprintf (st, " Compilation info: %s\n", BUILDINFO_scp );
4073 # ifndef __OPEN64__
4074 fprintf (st, " Relevant definitions: %s\n", SYSDEFS_USED );
4075 # endif
4076 #elif defined(BUILDINFO_scp)
4077 fprintf (st, " Compilation info: %s\n", BUILDINFO_scp );
4078 #else
4079 fprintf (st, " Compilation info: Not available\n" );
4080 #endif
4081 #if defined (UV_VERSION_MAJOR) && \
4082 defined (UV_VERSION_MINOR) && \
4083 defined (UV_VERSION_PATCH)
4084 # ifdef UV_VERSION_MAJOR
4085 # ifndef UV_VERSION_MINOR
4086 # ifndef UV_VERSION_PATCH
4087 # ifndef UV_VERSION_SUFFIX
4088 fprintf (st, " Event loop library: Built with libuv v%d", UV_VERSION_MAJOR);
4089 # endif /* ifndef UV_VERSION_SUFFIX */
4090 # endif /* ifndef UV_VERSION_PATCH */
4091 # endif /* ifndef UV_VERSION_MINOR */
4092 # ifdef UV_VERSION_MINOR
4093 # ifndef UV_VERSION_PATCH
4094 # ifndef UV_VERSION_SUFFIX
4095 fprintf (st, " Event loop library: Built with libuv %d.%d", UV_VERSION_MAJOR,
4096 UV_VERSION_MINOR);
4097 # endif /* ifndef UV_VERSION_SUFFIX */
4098 # endif /* ifndef UV_VERSION_PATCH */
4099 # ifdef UV_VERSION_PATCH
4100 # ifndef UV_VERSION_SUFFIX
4101 fprintf (st, " Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4102 UV_VERSION_MINOR, UV_VERSION_PATCH);
4103 # endif /* ifndef UV_VERSION_SUFFIX */
4104 # ifdef UV_VERSION_SUFFIX
4105 fprintf (st, " Event loop library: Built with libuv %d.%d.%d", UV_VERSION_MAJOR,
4106 UV_VERSION_MINOR, UV_VERSION_PATCH);
4107 # ifdef UV_VERSION_IS_RELEASE
4108 # if UV_VERSION_IS_RELEASE == 1
4109 # define UV_RELEASE_TYPE " (release)"
4110 # endif /* if UV_VERSION_IS_RELEASE == 1 */
4111 # if UV_VERSION_IS_RELEASE == 0
4112 # define UV_RELEASE_TYPE " (snapshot)"
4113 # endif /* if UV_VERSION_IS_RELEASE == 0 */
4114 # ifndef UV_RELEASE_TYPE
4115 # define UV_RELEASE_TYPE ""
4116 # endif /* ifndef UV_RELEASE_TYPE */
4117 # ifdef UV_RELEASE_TYPE
4118 fprintf (st, "%s", UV_RELEASE_TYPE);
4119 # endif /* ifdef UV_RELEASE_TYPE */
4120 # endif /* ifdef UV_VERSION_IS_RELEASE */
4121 # endif /* ifdef UV_VERSION_SUFFIX */
4122 # endif /* ifdef UV_VERSION_PATCH */
4123 # endif /* ifdef UV_VERSION_MINOR */
4124 unsigned int CurrentUvVersion = uv_version();
4125 if (((void *)&CurrentUvVersion != NULL) && (CurrentUvVersion > 0))
4126 if (uv_version_string() != NULL)
4127 fprintf (st, "; %s in use", uv_version_string());
4128 # endif /* ifdef UV_VERSION_MAJOR */
4129 #else
4130 fprintf (st, " Event loop library: Using libuv (or compatible) library, unknown version");
4131 #endif /* if defined(UV_VERSION_MAJOR) && \
4132 * defined(UV_VERSION_MINOR) && \
4133 * defined(UV_VERSION_PATCH) \
4134 */
4135 #ifdef DECNUMBERLOC
4136 fprintf (st, "\n");
4137 # ifdef DECVERSION
4138 # ifdef DECNLAUTHOR
4139 fprintf (st, " Math library: %s (%s and contributors)", DECVERSION, DECNLAUTHOR);
4140 # else
4141 fprintf (st, " Math library: %s", DECVERSION);
4142 # endif /* ifdef DECNLAUTHOR */
4143 # else
4144 fprintf (st, " Math library: decNumber, unknown version");
4145 # endif /* ifdef DECVERSION */
4146 #endif /* ifdef DECNUMBERLOC */
4147 #ifdef LOCKLESS
4148 fprintf (st, "\n Atomic operations: ");
4149 # if defined(AIX_ATOMICS)
4150 fprintf (st, "IBM AIX-style");
4151 # elif defined(BSD_ATOMICS)
4152 fprintf (st, "FreeBSD-style");
4153 # elif defined(GNU_ATOMICS)
4154 fprintf (st, "GNU-style");
4155 # elif defined(SYNC_ATOMICS)
4156 fprintf (st, "GNU sync-style");
4157 # elif defined(ISO_ATOMICS)
4158 fprintf (st, "ISO/IEC 9899:2011 (C11) standard");
4159 # elif defined(NT_ATOMICS)
4160 fprintf (st, "Windows NT interlocked operations");
4161 # endif
4162 #endif /* ifdef LOCKLESS */
4163 fprintf (st, "\n File locking: ");
4164 #if defined(USE_FCNTL) && defined(USE_FLOCK)
4165 fprintf (st, "POSIX-style fcntl() and BSD-style flock() locking");
4166 #endif
4167 #if defined(USE_FCNTL) && !defined(USE_FLOCK)
4168 fprintf (st, "POSIX-style fcntl() locking");
4169 #endif
4170 #if defined(USE_FLOCK) && !defined(USE_FCNTL)
4171 fprintf (st, "BSD-style flock() locking");
4172 #endif
4173 #if !defined(USE_FLOCK) && !defined(USE_FCNTL)
4174 fprintf (st, "No file locking available");
4175 #endif
4176 fprintf (st, "\n");
4177 return 0;
4178 }
4179
show_version(FILE * st,DEVICE * dptr,UNIT * uptr,int32 flag,CONST char * cptr)4180 t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4181 {
4182 const char *arch = "";
4183 int dirty = 0;
4184
4185 if (cptr && (*cptr != 0))
4186 return SCPE_2MARG;
4187 if (flag) {
4188 fprintf (st, " %s Simulator:", sim_name);
4189 #if defined(NO_SUPPORT_VERSION) || \
4190 defined(TESTING) || \
4191 defined(ISOLTS)
4192 # ifndef NO_SUPPORT_VERSION
4193 # define NO_SUPPORT_VERSION 1
4194 # endif
4195 #endif
4196 #if defined(NO_SUPPORT_VERSION)
4197 dirty++;
4198 #endif
4199 #if defined(GENERATED_MAKE_VER_H)
4200 # if defined(VER_H_GIT_VERSION)
4201
4202 /* Dirty if git source is dirty */
4203 if (strstr(VER_H_GIT_VERSION, "*"))
4204 {
4205 dirty++;
4206 }
4207
4208 /* Dirty if post-tag patches detected */
4209 if (strstr(VER_H_GIT_VERSION, "+"))
4210 {
4211 dirty++;
4212 }
4213
4214 /* Dirty if version contains "Z", "D", "A", or "B" */
4215 if ((strstr(VER_H_GIT_VERSION, "Z")) || \
4216 (strstr(VER_H_GIT_VERSION, "D")) || \
4217 (strstr(VER_H_GIT_VERSION, "A")) || \
4218 (strstr(VER_H_GIT_VERSION, "B"))) {
4219 dirty++;
4220 }
4221 # if defined(VER_H_GIT_PATCH) && defined(VER_H_GIT_PATCH_INT)
4222 # if defined(VER_H_GIT_HASH)
4223 # if VER_H_GIT_PATCH_INT < 1
4224 fprintf (st, "\n Version: %s\n Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_HASH);
4225 # else
4226 # define NO_SUPPORT_VERSION 1
4227 fprintf (st, "\n Version: %s+%s\n Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_PATCH, VER_H_GIT_HASH);
4228 # endif
4229 # else
4230 # if VER_H_GIT_PATCH_INT < 1
4231 fprintf (st, "\n Version: %s", VER_H_GIT_VERSION);
4232 # else
4233 # define NO_SUPPORT_VERSION 1
4234 fprintf (st, "\n Version: %s+%s", VER_H_GIT_VERSION, VER_H_GIT_PATCH);
4235 # endif
4236 # endif
4237 # else
4238 # if defined(VER_H_GIT_HASH)
4239 fprintf (st, "\n Version: %s\n Commit: %s", VER_H_GIT_VERSION, VER_H_GIT_HASH);
4240 # else
4241 fprintf (st, "\n Version: %s", VER_H_GIT_VERSION);
4242 # endif
4243 # endif
4244 # endif
4245 #endif
4246 #ifdef TESTING
4247 fprintf (st, "\n Options: ");
4248 # ifndef HAVE_DPSOPT
4249 # define HAVE_DPSOPT 1
4250 # endif
4251 fprintf (st, "TESTING");
4252 #endif
4253 #ifdef ISOLTS
4254 # ifdef HAVE_DPSOPT
4255 fprintf (st, ", ");
4256 # else
4257 fprintf (st, "\n Options: ");
4258 # endif
4259 # ifndef HAVE_DPSOPT
4260 # define HAVE_DPSOPT 1
4261 # endif
4262 fprintf (st, "ISOLTS");
4263 #endif
4264 #ifdef NEED_128
4265 # ifdef HAVE_DPSOPT
4266 fprintf (st, ", ");
4267 # else
4268 fprintf (st, "\n Options: ");
4269 # endif
4270 # ifndef HAVE_DPSOPT
4271 # define HAVE_DPSOPT 1
4272 # endif
4273 fprintf (st, "NEED_128");
4274 #endif
4275 #ifdef WAM
4276 # ifdef HAVE_DPSOPT
4277 fprintf (st, ", ");
4278 # else
4279 fprintf (st, "\n Options: ");
4280 # endif
4281 # ifndef HAVE_DPSOPT
4282 # define HAVE_DPSOPT 1
4283 # endif
4284 fprintf (st, "WAM");
4285 #endif
4286 #ifdef ROUND_ROBIN
4287 # ifdef HAVE_DPSOPT
4288 fprintf (st, ", ");
4289 # else
4290 fprintf (st, "\n Options: ");
4291 # endif
4292 # ifndef HAVE_DPSOPT
4293 # define HAVE_DPSOPT 1
4294 # endif
4295 fprintf (st, "ROUND_ROBIN");
4296 #endif
4297 #ifndef LOCKLESS
4298 # ifdef HAVE_DPSOPT
4299 fprintf (st, ", ");
4300 # else
4301 fprintf (st, "\n Options: ");
4302 # endif
4303 # ifndef HAVE_DPSOPT
4304 # define HAVE_DPSOPT 1
4305 # endif
4306 fprintf (st, "NO_LOCKLESS");
4307 #endif
4308 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE)
4309 # if defined(NO_SUPPORT_VERSION)
4310 fprintf (st, "\n Modified: %s", VER_H_GIT_DATE);
4311 # else
4312 fprintf (st, "\n Released: %s", VER_H_GIT_DATE);
4313 # endif
4314 #endif
4315 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_GIT_DATE) && defined(VER_H_PREP_DATE)
4316 fprintf (st, " - Kit Prepared: %s", VER_H_PREP_DATE);
4317 #endif
4318 #ifdef VER_CURRENT_TIME
4319 fprintf (st, "\n Compiled: %s", VER_CURRENT_TIME);
4320 #endif
4321 if (dirty)
4322 {
4323 fprintf (st, "\r\n\r\n ****** THIS BUILD IS NOT SUPPORTED BY THE DPS8M DEVELOPMENT TEAM ******");
4324 }
4325 fprintf (st, "\n\n Build Information:");
4326 #if defined(VER_H_PREP_OSVN)
4327 fprintf (st, "\n Build OS: %s", VER_H_PREP_OSVN);
4328 #endif
4329 #if defined (__VERSION__)
4330 char gnumver[2];
4331 char postver[1024];
4332 sprintf(gnumver, "%.1s", __VERSION__);
4333 sprintf(postver, "%.1023s", __VERSION__);
4334 strremove(postver, "(TM)");
4335 strremove(postver, "(R)");
4336 strremove(postver, "git://github.com/OpenIndiana/oi-userland.git ");
4337 strremove(postver, "4.2.1 Compatible ");
4338 strremove(postver, "git@github.com:llvm/llvm-project.git ");
4339 strremove(postver, "https://github.com/llvm/llvm-project.git ");
4340 strremove(postver, " (https://github.com/yrnkrn/zapcc)");
4341 strremove(postver, "https://github.com/yrnkrn/zapcc ");
4342 #endif
4343 #if defined (__GNUC__) && defined (__VERSION__)
4344 # ifndef __clang_version__
4345 if (isdigit(gnumver[0])) {
4346 fprintf (st, "\n Compiler: GCC %s", postver);
4347 } else {
4348 fprintf (st, "\n Compiler: %s", postver);
4349 }
4350 # endif
4351 # if defined (__clang_version__) && defined (__VERSION__)
4352 char clangllvmver[1024];
4353 sprintf(clangllvmver, "%.1023s", __clang_version__);
4354 strremove(clangllvmver, "git://github.com/OpenIndiana/oi-userland.git ");
4355 if (gnumver[0] == 'c' || gnumver[0] == 'C') {
4356 fprintf (st, "\n Compiler: Clang %s", clangllvmver);
4357 } else {
4358 fprintf (st, "\n Compiler: %s", postver);
4359 }
4360 # elif defined (__clang_version__)
4361 fprintf (st, "\n Compiler: %s", postver);
4362 # endif
4363 #elif defined (_MSC_FULL_VER) && defined (_MSC_BUILD)
4364 fprintf (st, "\n Compiler: Microsoft C %d.%02d.%05d.%02d", _MSC_FULL_VER/10000000, (_MSC_FULL_VER/100000)%100, _MSC_FULL_VER%100000, _MSC_BUILD);
4365 #elif ( defined (__xlc__) && !defined(__clang_version__) )
4366 # if defined (_AIX) && defined (PASE)
4367 fprintf (st, "\n Compiler: IBM XL C/C++ V%s (PASE for IBM i)", __xlc__);
4368 # endif
4369 # if defined (_AIX) && !defined (PASE)
4370 fprintf (st, "\n Compiler: IBM XL C/C++ for AIX V%s", __xlc__);
4371 # endif
4372 # if defined (__linux__) && ( !defined(_AIX) || !defined(PASE) )
4373 fprintf (st, "\n Compiler: IBM XL C/C++ for Linux V%s", __xlc__);
4374 # endif
4375 # if ( !defined(_AIX) && !defined(__clang_version__) && !defined(PASE) && !defined(__linux__) && defined(__xlc__) )
4376 fprintf (st, "\n Compiler: IBM XL C/C++ V%s", __xlc__);
4377 # endif
4378 #elif defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__SUNPRO_CC_COMPAT)
4379 fprintf (st, "\n Compiler: Oracle Developer Studio C/C++");
4380 #elif defined (__DMC__)
4381 fprintf (st, "\n Compiler: Digital Mars C/C++");
4382 #elif defined (__PCC__)
4383 fprintf (st, "\n Compiler: Portable C Compiler");
4384 #elif defined (KENC) || defined (KENCC) || defined (__KENC__) || defined (__KENCC__)
4385 fprintf (st, "\n Compiler: Plan 9 Compiler Suite");
4386 #elif defined (__ACK__)
4387 fprintf (st, "\n Compiler: Amsterdam Compiler Kit");
4388 #elif defined (__COMO__)
4389 fprintf (st, "\n Compiler: Comeau C++");
4390 #elif defined (__COMPCERT__)
4391 fprintf (st, "\n Compiler: CompCert C");
4392 #elif defined (__COVERITY__)
4393 fprintf (st, "\n Compiler: Coverity C/C++ Static Analyzer");
4394 #elif defined (__LCC__)
4395 fprintf (st, "\n Compiler: Local C Compiler (lcc)");
4396 #elif defined (sgi) || defined (__sgi) || defined (_sgi) || defined (_SGI_COMPILER_VERSION)
4397 fprintf (st, "\n Compiler: SGI MIPSpro");
4398 #elif defined (__OPEN64__)
4399 fprintf (st, "\n Compiler: Open64 %s", __OPEN64__);
4400 #elif defined (__PGI) || defined (__PGIC__)
4401 fprintf (st, "\n Compiler: Portland Group/PGI C/C++");
4402 #elif defined (__VBCC__)
4403 fprintf (st, "\n Compiler: Volker Barthelmann C Compiler (vbcc)");
4404 #elif defined (__WATCOMC__)
4405 fprintf (st, "\n Compiler: Watcom C/C++ %d.%d", __WATCOMC__ / 100, __WATCOMC__ % 100);
4406 #elif defined (__xlC__)
4407 fprintf (st, "\n Compiler: IBM XL C/C++");
4408 #elif defined (SIM_COMPILER)
4409 # define S_xstr(a) S_str(a)
4410 # define S_str(a) #a
4411 fprintf (st, "\n Compiler: %s", S_xstr(SIM_COMPILER));
4412 # undef S_str
4413 # undef S_xstr
4414 #else
4415 fprintf (st, "\n Compiler: Unknown");
4416 #endif
4417 #if defined(_M_X64) || defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__) || defined(__AMD64)
4418 arch = " x86_64";
4419 #elif defined(_M_IX86) || defined(__i386) || defined(__i486) || defined(__i586) || defined(__i686) || defined(__ix86)
4420 arch = " x86";
4421 #elif defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__)
4422 arch = " arm64";
4423 #elif defined(_M_ARM) || defined(__arm__)
4424 arch = " arm";
4425 #elif defined(__ia64__) || defined(_M_IA64) || defined(__itanium__)
4426 arch = " ia64";
4427 #elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__) || defined(__powerpc64__) || defined(__POWERPC64__) || defined(_M_PPC64) || defined(__PPC64) || defined(_ARCH_PPC64)
4428 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4429 arch = " ppc64be";
4430 # else
4431 arch = " ppc64el";
4432 # endif
4433 #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)
4434 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4435 arch = " ppc";
4436 # else
4437 arch = " ppcel";
4438 # endif
4439 #elif defined(__s390x__)
4440 arch = " s390x";
4441 #elif defined(__s390__)
4442 arch = " s390";
4443 #elif defined(__J2__) || defined(__J2P__) || defined(__j2__) || defined(__j2p__)
4444 arch = " j2";
4445 #elif defined(__SH4__) || defined(__sh4__) || defined(__SH4) || defined(__sh4)
4446 arch = " sh4";
4447 #elif defined(__SH2__) || defined(__sh2__) || defined(__SH2) || defined(__sh2)
4448 arch = " sh2";
4449 #elif defined(__alpha__)
4450 arch = " alpha";
4451 #elif defined(__hppa__) || defined(__HPPA__) || defined(__PARISC__) || defined(__parisc__)
4452 arch = " hppa";
4453 #elif defined(__ICE9__) || defined(__ice9__) || defined(__ICE9) || defined(__ice9)
4454 arch = " ice9";
4455 #elif defined(mips64) || defined(__mips64__) || defined(MIPS64) || defined(_MIPS64_) || defined(__mips64)
4456 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4457 arch = " mips64be";
4458 # else
4459 arch = " mips64el";
4460 # endif
4461 #elif defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) || defined(__mips)
4462 # if defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
4463 arch = " mipsbe";
4464 # else
4465 arch = " mipsel";
4466 # endif
4467 #elif defined(__OpenRISC__) || defined(__OPENRISC__) || defined(__openrisc__) || defined(__OR1K__) || defined(__JOR1K__) || defined(__OPENRISC1K__) || defined(__OPENRISC1200__)
4468 arch = " openrisc";
4469 #elif defined(__sparc64) || defined(__SPARC64) || defined(__SPARC64__) || defined(__sparc64__)
4470 arch = " sparc64";
4471 #elif defined(__sparc) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc__)
4472 arch = " sparc";
4473 #elif defined(__riscv) || defined(__riscv__)
4474 arch = " riscv";
4475 #elif defined(__myriad2__)
4476 arch = " myriad2";
4477 #else
4478 arch = " ";
4479 #endif
4480 fprintf (st, "%s", arch);
4481 #if defined(GENERATED_MAKE_VER_H) && defined(VER_H_PREP_USER)
4482 fprintf (st, "\n Built by: %s", VER_H_PREP_USER);
4483 #endif
4484 fprintf (st, "\n\n Host System Information:");
4485 #if defined(_WIN32)
4486 if (1) {
4487 char *proc_id = getenv ("PROCESSOR_IDENTIFIER");
4488 char *arch = getenv ("PROCESSOR_ARCHITECTURE");
4489 char *procs = getenv ("NUMBER_OF_PROCESSORS");
4490 char *proc_level = getenv ("PROCESSOR_LEVEL");
4491 char *proc_rev = getenv ("PROCESSOR_REVISION");
4492 char *proc_arch3264 = getenv ("PROCESSOR_ARCHITEW6432");
4493 char osversion[PATH_MAX+1] = "";
4494 FILE *f;
4495
4496 if ((f = _popen ("ver", "r"))) {
4497 memset (osversion, 0, sizeof(osversion));
4498 do {
4499 if (NULL == fgets (osversion, sizeof(osversion)-1, f))
4500 break;
4501 sim_trim_endspc (osversion);
4502 } while (osversion[0] == '\0');
4503 _pclose (f);
4504 }
4505 fprintf (st, "\n Host OS: %s", osversion);
4506 fprintf (st, " %s%s%s", arch, proc_arch3264 ? " on " : "", proc_arch3264 ? proc_arch3264 : "");
4507 }
4508 #else
4509 if (1) {
4510 char osversion[2*PATH_MAX+1] = "";
4511 FILE *f;
4512 # ifndef _AIX
4513 if ((f = popen ("uname -mrs 2> /dev/null", "r"))) {
4514 # else
4515 if ((f = popen \
4516 ("sh -c 'command -p env uname -svM 2> /dev/null' \
4517 2> /dev/null || \
4518 /bin/sh -c 'command -p env uname -svM \
4519 2> /dev/null' \
4520 2> /dev/null || \
4521 uname -svM 2> /dev/null || \
4522 sh -c 'command -p env uname -svp \
4523 2> /dev/null' \
4524 2> /dev/null || \
4525 /bin/sh -c 'command -p env uname -svp \
4526 2> /dev/null' \
4527 2> /dev/null || \
4528 uname -svp 2> /dev/null \
4529 ", "r"))) \
4530 {
4531 # endif /* ifndef _AIX */
4532 memset (osversion, 0, sizeof(osversion));
4533 do {
4534 if (NULL == fgets (osversion, sizeof(osversion)-1, f)) {
4535 break;
4536 }
4537 sim_trim_endspc (osversion);
4538 } while (osversion[0] == '\0');
4539 pclose (f);
4540 strremove(osversion, "0000000000000000 ");
4541 strremove(osversion, " 0000000000000000");
4542 strremove(osversion, "000000000000 ");
4543 strremove(osversion, " 000000000000");
4544 strremove(osversion, "IBM ");
4545 strremove(osversion, " (emulated by qemu)");
4546 strremove(osversion, " (emulated by QEMU)");
4547 }
4548 # ifndef _AIX
4549 fprintf (st, "\n Host OS: %s", osversion);
4550 # else
4551 strremove(osversion, "AIX ");
4552 fprintf (st, "\n Host OS: IBM AIX %s", osversion);
4553 # endif /* ifndef _AIX */
4554 } else {
4555 # ifndef _AIX
4556 fprintf (st, "\n Host OS: Unknown");
4557 # else
4558 fprintf (st, "\n Host OS: IBM AIX");
4559 # endif /* ifndef _AIX */
4560 }
4561 #endif
4562 #if defined(__APPLE__)
4563 int isRosetta = processIsTranslated();
4564 if (isRosetta == 1) {
4565 sim_printf ("\n\n ****** RUNNING UNDER APPLE ROSETTA 2, EXPECT REDUCED PERFORMANCE ******");
4566 }
4567 #endif
4568 fprintf (st, "\n");
4569 fprintf (st, "\n This software is made available under the terms of the ICU License,");
4570 fprintf (st, "\n version 1.8.1 or later. For complete details, see the \"LICENSE.md\"");
4571 fprintf (st, "\n included or https://gitlab.com/dps8m/dps8m/-/blob/master/LICENSE.md");
4572 fprintf (st, "\n");
4573 }
4574 return SCPE_OK;
4575 }
4576
4577 t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4578 {
4579 int32 i;
4580 DEVICE *dptr;
4581 t_bool only_enabled = (sim_switches & SWMASK ('E'));
4582
4583 if (cptr && (*cptr != 0))
4584 return SCPE_2MARG;
4585 fprintf (st, "%s simulator configuration%s\n\n", sim_name, only_enabled ? " (enabled devices)" : "");
4586 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4587 if (!only_enabled || !qdisable (dptr))
4588 show_device (st, dptr, flag);
4589 return SCPE_OK;
4590 }
4591
4592 t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4593 {
4594 int32 i;
4595 DEVICE *dptr;
4596
4597 if (cptr && (*cptr != 0))
4598 return SCPE_2MARG;
4599 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4600 show_dev_logicals (st, dptr, NULL, 1, cptr);
4601 return SCPE_OK;
4602 }
4603
4604 t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4605 {
4606 if (dptr->lname)
4607 fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);
4608 else if (!flag)
4609 fputs ("no logical name assigned\n", st);
4610 return SCPE_OK;
4611 }
4612
4613 t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4614 {
4615 DEVICE *dptr;
4616 UNIT *uptr;
4617 int32 accum;
4618
4619 if (cptr && (*cptr != 0))
4620 return SCPE_2MARG;
4621 if (sim_clock_queue == QUEUE_LIST_END)
4622 fprintf (st, "%s event queue empty, time = %.0f, executing %.0f instructios/sec\n",
4623 sim_name, sim_time, sim_timer_inst_per_sec ());
4624 else {
4625 const char *tim;
4626
4627 fprintf (st, "%s event queue status, time = %.0f, executing %.0f instructions/sec\n",
4628 sim_name, sim_time, sim_timer_inst_per_sec ());
4629 accum = 0;
4630 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
4631 if (uptr == &sim_step_unit)
4632 fprintf (st, " Step timer");
4633 else
4634 if (uptr == &sim_expect_unit)
4635 fprintf (st, " Expect fired");
4636 else
4637 if ((dptr = find_dev_from_unit (uptr)) != NULL) {
4638 fprintf (st, " %s", sim_dname (dptr));
4639 if (dptr->numunits > 1)
4640 fprintf (st, " unit %d", (int32) (uptr - dptr->units));
4641 }
4642 else
4643 fprintf (st, " Unknown");
4644 tim = sim_fmt_secs((accum + uptr->time)/sim_timer_inst_per_sec ());
4645 fprintf (st, " at %d%s%s%s%s\n", accum + uptr->time,
4646 (*tim) ? " (" : "", tim, (*tim) ? ")" : "",
4647 (uptr->flags & UNIT_IDLE) ? " (Idle capable)" : "");
4648 accum = accum + uptr->time;
4649 }
4650 }
4651 sim_show_clock_queues (st, dnotused, unotused, flag, cptr);
4652 return SCPE_OK;
4653 }
4654
4655 t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4656 {
4657 if (cptr && (*cptr != 0))
4658 return SCPE_2MARG;
4659 fprintf (st, "Time:\t%.0f\n", sim_gtime());
4660 return SCPE_OK;
4661 }
4662
4663 t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4664 {
4665 t_stat r;
4666
4667 if (cptr && (*cptr != 0))
4668 r = ssh_break (st, cptr, 1); /* more? */
4669 else
4670 r = sim_brk_showall (st, sim_switches);
4671 return r;
4672 }
4673
4674 t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4675 {
4676 fprintf (st, "Radix=%d\n", dptr->dradix);
4677 return SCPE_OK;
4678 }
4679
4680 t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4681 {
4682 int32 any = 0;
4683 DEBTAB *dep;
4684
4685 if (dptr->flags & DEV_DEBUG) {
4686 if (dptr->dctrl == 0)
4687 fputs ("Debugging disabled", st);
4688 else if (dptr->debflags == NULL)
4689 fputs ("Debugging enabled", st);
4690 else {
4691 uint32 dctrl = dptr->dctrl;
4692
4693 fputs ("Debug=", st);
4694 for (dep = dptr->debflags; (dctrl != 0) && (dep->name != NULL); dep++) {
4695 if ((dctrl & dep->mask) == dep->mask) {
4696 dctrl &= ~dep->mask;
4697 if (any)
4698 fputc (';', st);
4699 fputs (dep->name, st);
4700 any = 1;
4701 }
4702 }
4703 }
4704 fputc ('\n', st);
4705 return SCPE_OK;
4706 }
4707 else return SCPE_NOFNC;
4708 }
4709
4710 /* Show On actions */
4711
4712 t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4713 {
4714 int32 lvl, i;
4715
4716 if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */
4717 for (lvl=sim_do_depth; lvl >= 0; --lvl) {
4718 if (lvl > 0)
4719 fprintf(st, "On Processing at Do Nest Level: %d", lvl);
4720 else
4721 fprintf(st, "On Processing for input commands");
4722 fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled");
4723 for (i=1; i<SCPE_BASE; ++i) {
4724 if (sim_on_actions[lvl][i])
4725 fprintf(st, " on %5d %s\n", i, sim_on_actions[lvl][i]); }
4726 for (i=SCPE_BASE; i<=SCPE_MAX_ERR; ++i) {
4727 if (sim_on_actions[lvl][i])
4728 fprintf(st, " on %-5s %s\n", scp_errors[i-SCPE_BASE].code, sim_on_actions[lvl][i]); }
4729 if (sim_on_actions[lvl][0])
4730 fprintf(st, " on ERROR %s\n", sim_on_actions[lvl][0]);
4731 fprintf(st, "\n");
4732 }
4733 if (sim_on_inherit)
4734 fprintf(st, "on state and actions are inherited by nested do commands and subroutines\n");
4735 return SCPE_OK;
4736 }
4737
4738 /* Show modifiers */
4739
4740 t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4741 {
4742 int32 i;
4743 DEVICE *dptr;
4744
4745 if (cptr && (*cptr != 0)) /* now eol? */
4746 return SCPE_2MARG;
4747 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4748 show_dev_modifiers (st, dptr, NULL, flag, cptr);
4749 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
4750 show_dev_modifiers (st, dptr, NULL, flag, cptr);
4751 return SCPE_OK;
4752 }
4753
4754 t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4755 {
4756 fprint_set_help (st, dptr);
4757 return SCPE_OK;
4758 }
4759
4760 t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, int32 *toks)
4761 {
4762 MTAB *mptr;
4763 t_stat r = SCPE_OK;
4764
4765 if (dptr->modifiers == NULL)
4766 return SCPE_OK;
4767 for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {
4768 if (mptr->pstring &&
4769 ((mptr->mask & MTAB_XTD)?
4770 (MODMASK(mptr,flag) && !MODMASK(mptr,MTAB_NMO)):
4771 ((MTAB_VUN == (uint32)flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {
4772 if (*toks > 2) {
4773 fprintf (st, "\n");
4774 *toks = 0;
4775 }
4776 if (r == SCPE_OK)
4777 fprint_sep (st, toks);
4778 r = show_one_mod (st, dptr, uptr, mptr, NULL, 0);
4779 }
4780 }
4781 return SCPE_OK;
4782 }
4783
4784 t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,
4785 CONST char *cptr, int32 flag)
4786 {
4787 t_stat r = SCPE_OK;
4788
4789 if (mptr->disp)
4790 r = mptr->disp (st, uptr, mptr->match, (CONST void *)(cptr? cptr: mptr->desc));
4791 else
4792 fputs (mptr->pstring, st);
4793 if ((r == SCPE_OK) && (flag && !((mptr->mask & MTAB_XTD) && MODMASK(mptr,MTAB_NMO))))
4794 fputc ('\n', st);
4795 return r;
4796 }
4797
4798 /* Show show commands */
4799
4800 t_stat show_show_commands (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr)
4801 {
4802 int32 i;
4803 DEVICE *dptr;
4804
4805 if (cptr && (*cptr != 0)) /* now eol? */
4806 return SCPE_2MARG;
4807 for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
4808 show_dev_show_commands (st, dptr, NULL, flag, cptr);
4809 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
4810 show_dev_show_commands (st, dptr, NULL, flag, cptr);
4811 return SCPE_OK;
4812 }
4813
4814 t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
4815 {
4816 fprint_show_help (st, dptr);
4817 return SCPE_OK;
4818 }
4819
4820 /* Breakpoint commands */
4821
4822 t_stat brk_cmd (int32 flg, CONST char *cptr)
4823 {
4824 GET_SWITCHES (cptr); /* get switches */
4825 return ssh_break (NULL, cptr, flg); /* call common code */
4826 }
4827
4828 t_stat ssh_break (FILE *st, const char *cptr, int32 flg)
4829 {
4830 char gbuf[CBUFSIZE], *aptr, abuf[4*CBUFSIZE];
4831 CONST char *tptr, *t1ptr;
4832 DEVICE *dptr = sim_dflt_dev;
4833 UNIT *uptr = dptr ? dptr->units : NULL;
4834 t_stat r;
4835 t_addr lo, hi, max = uptr->capac - 1;
4836 int32 cnt;
4837
4838 if (sim_brk_types == 0)
4839 return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
4840 if ((dptr == NULL) || (uptr == NULL))
4841 return SCPE_IERR;
4842 abuf[sizeof(abuf)-1] = '\0';
4843 strncpy (abuf, cptr, sizeof(abuf)-1);
4844 cptr = abuf;
4845 if ((aptr = strchr (abuf, ';'))) { /* ;action? */
4846 if (flg != SSH_ST) /* only on SET */
4847 return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", aptr);
4848 *aptr++ = 0; /* separate strings */
4849 }
4850 if (*cptr == 0) { /* no argument? */
4851 lo = (t_addr) get_rval (sim_PC, 0); /* use PC */
4852 return ssh_break_one (st, flg, lo, 0, aptr);
4853 }
4854 while (*cptr) {
4855 cptr = get_glyph (cptr, gbuf, ',');
4856 tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);
4857 if (tptr == NULL)
4858 return sim_messagef (SCPE_ARG, "Invalid address specifier: %s\n", gbuf);
4859 if (*tptr == '[') {
4860 cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);
4861 if ((tptr == t1ptr) || (*t1ptr != ']') || (flg != SSH_ST))
4862 return sim_messagef (SCPE_ARG, "Invalid repeat count specifier: %s\n", tptr + 1);
4863 tptr = t1ptr + 1;
4864 }
4865 else cnt = 0;
4866 if (*tptr != 0)
4867 return sim_messagef (SCPE_ARG, "Unexpected argument: %s\n", tptr);
4868 if ((lo == 0) && (hi == max)) {
4869 if (flg == SSH_CL)
4870 sim_brk_clrall (sim_switches);
4871 else
4872 if (flg == SSH_SH)
4873 sim_brk_showall (st, sim_switches);
4874 else
4875 return SCPE_ARG;
4876 }
4877 else {
4878 for ( ; lo <= hi; lo = lo + 1) {
4879 r = ssh_break_one (st, flg, lo, cnt, aptr);
4880 if (r != SCPE_OK)
4881 return r;
4882 }
4883 }
4884 }
4885 return SCPE_OK;
4886 }
4887
4888 t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr)
4889 {
4890 if (!sim_brk_types)
4891 return sim_messagef (SCPE_NOFNC, "No breakpoint support in this simulator\n");
4892 switch (flg) {
4893
4894 case SSH_ST:
4895 return sim_brk_set (lo, sim_switches, cnt, aptr);
4896 break;
4897
4898 case SSH_CL:
4899 return sim_brk_clr (lo, sim_switches);
4900 break;
4901
4902 case SSH_SH:
4903 return sim_brk_show (st, lo, sim_switches);
4904 break;
4905
4906 default:
4907 return SCPE_ARG;
4908 }
4909 }
4910
4911 /* Reset command and routines
4912
4913 re[set] reset all devices
4914 re[set] all reset all devices
4915 re[set] device reset specific device
4916 */
4917
4918 static t_bool run_cmd_did_reset = FALSE;
4919
4920 t_stat reset_cmd (int32 flag, CONST char *cptr)
4921 {
4922 char gbuf[CBUFSIZE];
4923 DEVICE *dptr;
4924
4925 GET_SWITCHES (cptr); /* get switches */
4926 run_cmd_did_reset = FALSE;
4927 if (*cptr == 0) /* reset(cr) */
4928 return (reset_all (0));
4929 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
4930 if (*cptr != 0) /* now eol? */
4931 return SCPE_2MARG;
4932 if (strcmp (gbuf, "ALL") == 0)
4933 return (reset_all (0));
4934 dptr = find_dev (gbuf); /* locate device */
4935 if (dptr == NULL) /* found it? */
4936 return SCPE_NXDEV;
4937 if (dptr->reset != NULL)
4938 return dptr->reset (dptr);
4939 else return SCPE_OK;
4940 }
4941
4942 /* Reset devices start..end
4943
4944 Inputs:
4945 start = number of starting device
4946 Outputs:
4947 status = error status
4948 */
4949
4950 t_stat reset_all (uint32 start)
4951 {
4952 DEVICE *dptr;
4953 uint32 i;
4954 t_stat reason;
4955
4956 for (i = 0; i < start; i++) {
4957 if (sim_devices[i] == NULL)
4958 return SCPE_IERR;
4959 }
4960 for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
4961 if (dptr->reset != NULL) {
4962 reason = dptr->reset (dptr);
4963 if (reason != SCPE_OK)
4964 return reason;
4965 }
4966 }
4967 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
4968 if (dptr->reset != NULL) {
4969 reason = dptr->reset (dptr);
4970 if (reason != SCPE_OK)
4971 return reason;
4972 }
4973 }
4974 return SCPE_OK;
4975 }
4976
4977 /* Reset to powerup state
4978
4979 Inputs:
4980 start = number of starting device
4981 Outputs:
4982 status = error status
4983 */
4984
4985 t_stat reset_all_p (uint32 start)
4986 {
4987 t_stat r;
4988 int32 old_sw = sim_switches;
4989
4990 sim_switches = SWMASK ('P');
4991 r = reset_all (start);
4992 sim_switches = old_sw;
4993 return r;
4994 }
4995
4996 /* Attach command
4997
4998 at[tach] unit file attach specified unit to file
4999 */
5000
5001 t_stat attach_cmd (int32 flag, CONST char *cptr)
5002 {
5003 char gbuf[4*CBUFSIZE];
5004 DEVICE *dptr;
5005 UNIT *uptr;
5006 t_stat r;
5007
5008 GET_SWITCHES (cptr); /* get switches */
5009 if (*cptr == 0) /* must be more */
5010 return SCPE_2FARG;
5011 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5012 GET_SWITCHES (cptr); /* get switches */
5013 if (*cptr == 0) /* now eol? */
5014 return SCPE_2FARG;
5015 dptr = find_unit (gbuf, &uptr); /* locate unit */
5016 if (dptr == NULL) /* found dev? */
5017 return SCPE_NXDEV;
5018 if (uptr == NULL) /* valid unit? */
5019 return SCPE_NXUN;
5020 if (uptr->flags & UNIT_ATT) { /* already attached? */
5021 if (!(uptr->dynflags & UNIT_ATTMULT) && /* and only single attachable */
5022 !(dptr->flags & DEV_DONTAUTO)) { /* and auto detachable */
5023 r = scp_detach_unit (dptr, uptr); /* detach it */
5024 if (r != SCPE_OK) /* error? */
5025 return r;
5026 }
5027 else {
5028 if (!(uptr->dynflags & UNIT_ATTMULT))
5029 return SCPE_ALATT; /* Already attached */
5030 }
5031 }
5032 gbuf[sizeof(gbuf)-1] = '\0';
5033 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5034 sim_trim_endspc (gbuf); /* trim trailing spc */
5035 return scp_attach_unit (dptr, uptr, gbuf); /* attach */
5036 }
5037
5038 /* Call device-specific or file-oriented attach unit routine */
5039
5040 t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr)
5041 {
5042 if (dptr->attach != NULL) /* device routine? */
5043 return dptr->attach (uptr, (CONST char *)cptr); /* call it */
5044 return attach_unit (uptr, (CONST char *)cptr); /* no, std routine */
5045 }
5046
5047 /* Attach unit to file */
5048
5049 t_stat attach_unit (UNIT *uptr, CONST char *cptr)
5050 {
5051 DEVICE *dptr;
5052
5053 if (uptr->flags & UNIT_DIS) /* disabled? */
5054 return SCPE_UDIS;
5055 if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */
5056 return SCPE_NOATT;
5057 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5058 return SCPE_NOATT;
5059 uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */
5060 if (uptr->filename == NULL)
5061 return SCPE_MEM;
5062 strncpy (uptr->filename, cptr, CBUFSIZE-1); /* save name */
5063 if ((sim_switches & SWMASK ('R')) || /* read only? */
5064 ((uptr->flags & UNIT_RO) != 0)) {
5065 if (((uptr->flags & UNIT_ROABLE) == 0) && /* allowed? */
5066 ((uptr->flags & UNIT_RO) == 0))
5067 return attach_err (uptr, SCPE_NORO); /* no, error */
5068 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
5069 if (uptr->fileref == NULL) /* open fail? */
5070 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5071 uptr->flags = uptr->flags | UNIT_RO; /* set rd only */
5072 if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5073 sim_printf ("%s: unit is read only\n", sim_dname (dptr));
5074 }
5075 }
5076 else {
5077 if (sim_switches & SWMASK ('N')) { /* new file only? */
5078 uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */
5079 if (uptr->fileref == NULL) /* open fail? */
5080 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5081 if (!sim_quiet && !(sim_switches & SWMASK ('Q'))) {
5082 sim_printf ("%s: creating new file\n", sim_dname (dptr));
5083 }
5084 }
5085 else { /* normal */
5086 uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */
5087 if (uptr->fileref == NULL) { /* open fail? */
5088 #if defined (EWOULDBLOCK)
5089 if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
5090 #else
5091 if ((errno == EAGAIN))
5092 #endif
5093 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5094
5095 #if defined(EPERM)
5096 if ((errno == EROFS) || (errno == EACCES) || (errno == EPERM)) {/* read only? */
5097 #else
5098 if ((errno == EROFS) || (errno == EACCES)) {/* read only? */
5099 #endif
5100 if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */
5101 return attach_err (uptr, SCPE_NORO);/* no error */
5102 uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */
5103 if (uptr->fileref == NULL) /* open fail? */
5104 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5105 uptr->flags = uptr->flags | UNIT_RO; /* set rd only */
5106 if (!sim_quiet) {
5107 sim_printf ("%s: unit is read only\n", sim_dname (dptr));
5108 }
5109 }
5110 else { /* doesn't exist */
5111 if (sim_switches & SWMASK ('E')) /* must exist? */
5112 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5113 uptr->fileref = sim_fopen (cptr, "wb+");/* open new file */
5114 if (uptr->fileref == NULL) /* open fail? */
5115 return attach_err (uptr, SCPE_OPENERR); /* yes, error */
5116 if (!sim_quiet) {
5117 sim_printf ("%s: creating new file\n", sim_dname (dptr));
5118 }
5119 }
5120 } /* end if null */
5121 } /* end else */
5122 }
5123 if (uptr->flags & UNIT_BUFABLE) { /* buffer? */
5124 uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */
5125 if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */
5126 uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */
5127 if (uptr->filebuf == NULL) /* no buffer? */
5128 return attach_err (uptr, SCPE_MEM); /* error */
5129 if (!sim_quiet) {
5130 sim_printf ("%s: buffering file in memory\n", sim_dname (dptr));
5131 }
5132 uptr->hwmark = (uint32)sim_fread (uptr->filebuf, /* read file */
5133 SZ_D (dptr), cap, uptr->fileref);
5134 uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */
5135 }
5136 uptr->flags = uptr->flags | UNIT_ATT;
5137 uptr->pos = 0;
5138 return SCPE_OK;
5139 }
5140
5141 t_stat attach_err (UNIT *uptr, t_stat stat)
5142 {
5143 free (uptr->filename);
5144 uptr->filename = NULL;
5145 return stat;
5146 }
5147
5148 /* Detach command
5149
5150 det[ach] all detach all units
5151 det[ach] unit detach specified unit
5152 */
5153
5154 t_stat detach_cmd (int32 flag, CONST char *cptr)
5155 {
5156 char gbuf[CBUFSIZE];
5157 DEVICE *dptr;
5158 UNIT *uptr;
5159
5160 GET_SWITCHES (cptr); /* get switches */
5161 if (*cptr == 0) /* must be more */
5162 return SCPE_2FARG;
5163 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5164 if (*cptr != 0) /* now eol? */
5165 return SCPE_2MARG;
5166 if (strcmp (gbuf, "ALL") == 0)
5167 return (detach_all (0, FALSE));
5168 dptr = find_unit (gbuf, &uptr); /* locate unit */
5169 if (dptr == NULL) /* found dev? */
5170 return SCPE_NXDEV;
5171 if (uptr == NULL) /* valid unit? */
5172 return SCPE_NXUN;
5173 return scp_detach_unit (dptr, uptr); /* detach */
5174 }
5175
5176 /* Detach devices start..end
5177
5178 Inputs:
5179 start = number of starting device
5180 shutdown = TRUE if simulator shutting down
5181 Outputs:
5182 status = error status
5183
5184 Note that during shutdown, detach routines for non-attachable devices
5185 will be called. These routines can implement simulator shutdown. Error
5186 returns during shutdown are ignored.
5187 */
5188
5189 t_stat detach_all (int32 start, t_bool shutdown)
5190 {
5191 uint32 i, j;
5192 DEVICE *dptr;
5193 UNIT *uptr;
5194 t_stat r;
5195
5196 if ((start < 0) || (start > 1))
5197 return SCPE_IERR;
5198 if (shutdown)
5199 sim_switches = sim_switches | SIM_SW_SHUT; /* flag shutdown */
5200 for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5201 for (j = 0; j < dptr->numunits; j++) { /* loop thru units */
5202 uptr = (dptr->units) + j;
5203 if ((uptr->flags & UNIT_ATT) || /* attached? */
5204 (shutdown && dptr->detach && /* shutdown, spec rtn, */
5205 !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */
5206 r = scp_detach_unit (dptr, uptr); /* detach unit */
5207
5208 if ((r != SCPE_OK) && !shutdown) /* error and not shutting down? */
5209 return r; /* bail out now with error status */
5210 }
5211 }
5212 }
5213 return SCPE_OK;
5214 }
5215
5216 /* Call device-specific or file-oriented detach unit routine */
5217
5218 t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
5219 {
5220 if (dptr->detach != NULL) /* device routine? */
5221 return dptr->detach (uptr);
5222 return detach_unit (uptr); /* no, standard */
5223 }
5224
5225 /* Detach unit from file */
5226
5227 t_stat detach_unit (UNIT *uptr)
5228 {
5229 DEVICE *dptr;
5230
5231 if (uptr == NULL)
5232 return SCPE_IERR;
5233 if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */
5234 return SCPE_NOATT;
5235 if (!(uptr->flags & UNIT_ATT)) { /* not attached? */
5236 if (sim_switches & SIM_SW_REST) /* restoring? */
5237 return SCPE_OK; /* allow detach */
5238 else
5239 return SCPE_NOTATT; /* complain */
5240 }
5241 if ((dptr = find_dev_from_unit (uptr)) == NULL)
5242 return SCPE_OK;
5243 if (uptr->flags & UNIT_BUF) {
5244 uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5245 if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {
5246 if (!sim_quiet) {
5247 sim_printf ("%s: writing buffer to file\n", sim_dname (dptr));
5248 }
5249 rewind (uptr->fileref);
5250 sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5251 if (ferror (uptr->fileref))
5252 sim_printf ("%s: I/O error - %s", sim_dname (dptr), strerror (errno));
5253 }
5254 if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */
5255 free (uptr->filebuf); /* free buf */
5256 uptr->filebuf = NULL;
5257 }
5258 uptr->flags = uptr->flags & ~UNIT_BUF;
5259 }
5260 uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);
5261 free (uptr->filename);
5262 uptr->filename = NULL;
5263 if (fclose (uptr->fileref) == EOF)
5264 return SCPE_IOERR;
5265 return SCPE_OK;
5266 }
5267
5268 /* Assign command
5269
5270 as[sign] device name assign logical name to device
5271 */
5272
5273 t_stat assign_cmd (int32 flag, CONST char *cptr)
5274 {
5275 char gbuf[CBUFSIZE];
5276 DEVICE *dptr;
5277
5278 GET_SWITCHES (cptr); /* get switches */
5279 if (*cptr == 0) /* must be more */
5280 return SCPE_2FARG;
5281 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5282 GET_SWITCHES (cptr); /* get switches */
5283 if (*cptr == 0) /* now eol? */
5284 return SCPE_2FARG;
5285 dptr = find_dev (gbuf); /* locate device */
5286 if (dptr == NULL) /* found dev? */
5287 return SCPE_NXDEV;
5288 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5289 if (*cptr != 0) /* must be eol */
5290 return SCPE_2MARG;
5291 if (find_dev (gbuf)) /* name in use */
5292 return SCPE_ARG;
5293 deassign_device (dptr); /* release current */
5294 return assign_device (dptr, gbuf);
5295 }
5296
5297 t_stat assign_device (DEVICE *dptr, const char *cptr)
5298 {
5299 dptr->lname = (char *) calloc (1 + strlen (cptr), sizeof (char));
5300 if (dptr->lname == NULL)
5301 return SCPE_MEM;
5302 strcpy (dptr->lname, cptr);
5303 return SCPE_OK;
5304 }
5305
5306 /* Deassign command
5307
5308 dea[ssign] device deassign logical name
5309 */
5310
5311 t_stat deassign_cmd (int32 flag, CONST char *cptr)
5312 {
5313 char gbuf[CBUFSIZE];
5314 DEVICE *dptr;
5315
5316 GET_SWITCHES (cptr); /* get switches */
5317 if (*cptr == 0) /* must be more */
5318 return SCPE_2FARG;
5319 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5320 if (*cptr != 0) /* now eol? */
5321 return SCPE_2MARG;
5322 dptr = find_dev (gbuf); /* locate device */
5323 if (dptr == NULL) /* found dev? */
5324 return SCPE_NXDEV;
5325 return deassign_device (dptr);
5326 }
5327
5328 t_stat deassign_device (DEVICE *dptr)
5329 {
5330 free (dptr->lname);
5331 dptr->lname = NULL;
5332 return SCPE_OK;
5333 }
5334
5335 /* Get device display name */
5336
5337 const char *sim_dname (DEVICE *dptr)
5338 {
5339 return (dptr ? (dptr->lname? dptr->lname: dptr->name) : "");
5340 }
5341
5342 /* Get unit display name */
5343
5344 const char *sim_uname (UNIT *uptr)
5345 {
5346 DEVICE *d = find_dev_from_unit(uptr);
5347 static char uname[CBUFSIZE];
5348
5349 if (!d)
5350 return "";
5351 if (d->numunits == 1)
5352 return sim_dname (d);
5353 sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
5354 return uname;
5355 }
5356
5357 /* Save command
5358
5359 sa[ve] filename save state to specified file
5360 */
5361
5362 t_stat save_cmd (int32 flag, CONST char *cptr)
5363 {
5364 FILE *sfile;
5365 t_stat r;
5366 char gbuf[4*CBUFSIZE];
5367
5368 GET_SWITCHES (cptr); /* get switches */
5369 if (*cptr == 0) /* must be more */
5370 return SCPE_2FARG;
5371 gbuf[sizeof(gbuf)-1] = '\0';
5372 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5373 sim_trim_endspc (gbuf);
5374 if ((sfile = sim_fopen (gbuf, "wb")) == NULL)
5375 return SCPE_OPENERR;
5376 r = sim_save (sfile);
5377 fclose (sfile);
5378 return r;
5379 }
5380
5381 t_stat sim_save (FILE *sfile)
5382 {
5383 void *mbuf;
5384 int32 l, t;
5385 uint32 i, j, device_count;
5386 t_addr k, high;
5387 t_value val;
5388 t_stat r;
5389 t_bool zeroflg;
5390 size_t sz;
5391 DEVICE *dptr;
5392 UNIT *uptr;
5393 REG *rptr;
5394
5395 #define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile)
5396
5397 /* Don't make changes below without also changing save_vercur above */
5398
5399 fprintf (sfile, "%s\n%s\n%s\n%s\n%.0f\n",
5400 save_vercur, /* [V2.5] save format */
5401 sim_savename, /* sim name */
5402 sim_si64, sim_sa64, /* [V3.5] options */
5403 sim_time); /* [V3.2] sim time */
5404 WRITE_I (sim_rtime); /* [V2.6] sim rel time */
5405 fprintf (sfile, "simh git commit id: unknown\n");
5406
5407 for (device_count = 0; sim_devices[device_count]; device_count++);/* count devices */
5408 for (i = 0; i < (device_count + sim_internal_device_count); i++) {/* loop thru devices */
5409 if (i < device_count)
5410 dptr = sim_devices[i];
5411 else
5412 dptr = sim_internal_devices[i - device_count];
5413 if (dptr->flags & DEV_NOSAVE)
5414 continue;
5415 fputs (dptr->name, sfile); /* device name */
5416 fputc ('\n', sfile);
5417 if (dptr->lname) /* [V3.0] logical name */
5418 fputs (dptr->lname, sfile);
5419 fputc ('\n', sfile);
5420 WRITE_I (dptr->flags); /* [V2.10] flags */
5421 for (j = 0; j < dptr->numunits; j++) {
5422 uptr = dptr->units + j;
5423 t = sim_activate_time (uptr);
5424 WRITE_I (j); /* unit number */
5425 WRITE_I (t); /* activation time */
5426 WRITE_I (uptr->u3); /* unit specific */
5427 WRITE_I (uptr->u4);
5428 WRITE_I (uptr->u5); /* [V3.0] more unit */
5429 WRITE_I (uptr->u6);
5430 WRITE_I (uptr->flags); /* [V2.10] flags */
5431 WRITE_I (uptr->dynflags);
5432 WRITE_I (uptr->wait);
5433 WRITE_I (uptr->buf);
5434 WRITE_I (uptr->capac); /* [V3.5] capacity */
5435 if (uptr->flags & UNIT_ATT) {
5436 fputs (uptr->filename, sfile);
5437 if ((uptr->flags & UNIT_BUF) && /* writable buffered */
5438 uptr->hwmark && /* files need to be */
5439 ((uptr->flags & UNIT_RO) == 0)) { /* written on save */
5440 uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;
5441 rewind (uptr->fileref);
5442 sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);
5443 fclose (uptr->fileref); /* flush data and state */
5444 uptr->fileref = sim_fopen (uptr->filename, "rb+");/* reopen r/w */
5445 }
5446 }
5447 fputc ('\n', sfile);
5448 if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&
5449 (dptr->examine != NULL) &&
5450 ((high = uptr->capac) != 0)) { /* memory-like unit? */
5451 WRITE_I (high); /* [V2.5] write size */
5452 sz = SZ_D (dptr);
5453 if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {
5454 fclose (sfile);
5455 return SCPE_MEM;
5456 }
5457 for (k = 0; k < high; ) { /* loop thru mem */
5458 zeroflg = TRUE;
5459 for (l = 0; (l < SRBSIZ) && (k < high); l++,
5460 k = k + (dptr->aincr)) { /* check for 0 block */
5461 r = dptr->examine (&val, k, uptr, SIM_SW_REST);
5462 if (r != SCPE_OK) {
5463 free (mbuf);
5464 return r;
5465 }
5466 if (val) zeroflg = FALSE;
5467 SZ_STORE (sz, val, mbuf, l);
5468 } /* end for l */
5469 if (zeroflg) { /* all zero's? */
5470 l = -l; /* invert block count */
5471 WRITE_I (l); /* write only count */
5472 }
5473 else {
5474 WRITE_I (l); /* block count */
5475 sim_fwrite (mbuf, sz, l, sfile);
5476 }
5477 } /* end for k */
5478 free (mbuf); /* dealloc buffer */
5479 } /* end if mem */
5480 else { /* no memory */
5481 high = 0; /* write 0 */
5482 WRITE_I (high);
5483 } /* end else mem */
5484 } /* end unit loop */
5485 t = -1; /* end units */
5486 WRITE_I (t); /* write marker */
5487 for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */
5488 (rptr->name != NULL); rptr++) {
5489 fputs (rptr->name, sfile); /* name */
5490 fputc ('\n', sfile);
5491 WRITE_I (rptr->depth); /* [V2.10] depth */
5492 for (j = 0; j < rptr->depth; j++) { /* loop thru values */
5493 val = get_rval (rptr, j); /* get value */
5494 WRITE_I (val); /* store */
5495 }
5496 }
5497 fputc ('\n', sfile); /* end registers */
5498 }
5499 fputc ('\n', sfile); /* end devices */
5500 return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */
5501 }
5502
5503 /* Restore command
5504
5505 re[store] filename restore state from specified file
5506 */
5507
5508 t_stat restore_cmd (int32 flag, CONST char *cptr)
5509 {
5510 FILE *rfile;
5511 t_stat r;
5512 char gbuf[4*CBUFSIZE];
5513
5514 GET_SWITCHES (cptr); /* get switches */
5515 if (*cptr == 0) /* must be more */
5516 return SCPE_2FARG;
5517 gbuf[sizeof(gbuf)-1] = '\0';
5518 strncpy (gbuf, cptr, sizeof(gbuf)-1);
5519 sim_trim_endspc (gbuf);
5520 if ((rfile = sim_fopen (gbuf, "rb")) == NULL)
5521 return SCPE_OPENERR;
5522 r = sim_rest (rfile);
5523 fclose (rfile);
5524 return r;
5525 }
5526
5527 t_stat sim_rest (FILE *rfile)
5528 {
5529 char buf[CBUFSIZE];
5530 char **attnames = NULL;
5531 UNIT **attunits = NULL;
5532 int32 *attswitches = NULL;
5533 int32 attcnt = 0;
5534 void *mbuf;
5535 int32 j, blkcnt, limit, unitno, time, flg;
5536 uint32 us, depth;
5537 t_addr k, high, old_capac;
5538 t_value val, mask;
5539 t_stat r;
5540 size_t sz;
5541 t_bool v40, v35, v32;
5542 DEVICE *dptr;
5543 UNIT *uptr;
5544 REG *rptr;
5545 struct stat rstat;
5546 t_bool force_restore = ((sim_switches & SWMASK ('F')) != 0);
5547 t_bool dont_detach_attach = ((sim_switches & SWMASK ('D')) != 0);
5548 t_bool suppress_warning = ((sim_switches & SWMASK ('Q')) != 0);
5549 t_bool warned = FALSE;
5550
5551 sim_switches &= ~(SWMASK ('F') | SWMASK ('D') | SWMASK ('Q')); /* remove digested switches */
5552 #define READ_S(xx) if (read_line ((xx), sizeof(xx), rfile) == NULL) \
5553 return SCPE_IOERR;
5554 #define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
5555 return SCPE_IOERR;
5556
5557 fstat (fileno (rfile), &rstat);
5558 READ_S (buf); /* [V2.5+] read version */
5559 v40 = v35 = v32 = FALSE;
5560 if (strcmp (buf, save_ver40) == 0) /* version 4.0? */
5561 v40 = v35 = v32 = TRUE;
5562 else if (strcmp (buf, save_ver35) == 0) /* version 3.5? */
5563 v35 = v32 = TRUE;
5564 else if (strcmp (buf, save_ver32) == 0) /* version 3.2? */
5565 v32 = TRUE;
5566 else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */
5567 sim_printf ("Invalid file version: %s\n", buf);
5568 return SCPE_INCOMP;
5569 }
5570 if ((strcmp (buf, save_ver40) != 0) && (!sim_quiet) && (!suppress_warning)) {
5571 sim_printf ("warning - attempting to restore a saved simulator image in %s image format.\n", buf);
5572 warned = TRUE;
5573 }
5574 READ_S (buf); /* read sim name */
5575 if (strcmp (buf, sim_savename)) { /* name match? */
5576 sim_printf ("Wrong system type: %s\n", buf);
5577 return SCPE_INCOMP;
5578 }
5579 if (v35) { /* [V3.5+] options */
5580 READ_S (buf); /* integer size */
5581 if (strcmp (buf, sim_si64) != 0) {
5582 sim_printf ("Incompatible integer size, save file = %s\n", buf);
5583 return SCPE_INCOMP;
5584 }
5585 READ_S (buf); /* address size */
5586 if (strcmp (buf, sim_sa64) != 0) {
5587 sim_printf ("Incompatible address size, save file = %s\n", buf);
5588 return SCPE_INCOMP;
5589 }
5590 READ_S (buf);
5591 }
5592 if (v32) { /* [V3.2+] time as string */
5593 READ_S (buf);
5594 sscanf (buf, "%lf", &sim_time);
5595 }
5596 else READ_I (sim_time); /* sim time */
5597 READ_I (sim_rtime); /* [V2.6+] sim rel time */
5598 if (v40) {
5599 READ_S (buf); /* read git commit id */
5600 }
5601 if (!dont_detach_attach)
5602 detach_all (0, 0); /* Detach everything to start from a consistent state */
5603 else {
5604 if (!suppress_warning) {
5605 uint32 i, j;
5606
5607 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
5608 for (j = 0; j < dptr->numunits; j++) { /* loop thru units */
5609 uptr = (dptr->units) + j;
5610 if (uptr->flags & UNIT_ATT) { /* attached? */
5611 sim_printf ("warning - leaving %s attached to '%s'\n", sim_uname (uptr), uptr->filename);
5612 warned = TRUE;
5613 }
5614 }
5615 }
5616 }
5617 }
5618 for ( ;; ) { /* device loop */
5619 READ_S (buf); /* read device name */
5620 if (buf[0] == 0) /* last? */
5621 break;
5622 if ((dptr = find_dev (buf)) == NULL) { /* locate device */
5623 sim_printf ("Invalid device name: %s\n", buf);
5624 return SCPE_INCOMP;
5625 }
5626 READ_S (buf); /* [V3.0+] logical name */
5627 deassign_device (dptr); /* delete old name */
5628 if ((buf[0] != 0) &&
5629 ((r = assign_device (dptr, buf)) != SCPE_OK))
5630 return r;
5631 READ_I (flg); /* [V2.10+] ctlr flags */
5632 if (!v32)
5633 flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) |
5634 (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */
5635 dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */
5636 (flg & DEV_RFLAGS);
5637 for ( ;; ) { /* unit loop */
5638 sim_switches = SIM_SW_REST; /* flag rstr, clr RO */
5639 READ_I (unitno); /* unit number */
5640 if (unitno < 0) /* end units? */
5641 break;
5642 if ((uint32) unitno >= dptr->numunits) { /* too big? */
5643 sim_printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno);
5644 return SCPE_INCOMP;
5645 }
5646 READ_I (time); /* event time */
5647 uptr = (dptr->units) + unitno;
5648 sim_cancel (uptr);
5649 if (time > 0)
5650 sim_activate (uptr, time - 1);
5651 READ_I (uptr->u3); /* device specific */
5652 READ_I (uptr->u4);
5653 READ_I (uptr->u5); /* [V3.0+] more dev spec */
5654 READ_I (uptr->u6);
5655 READ_I (flg); /* [V2.10+] unit flags */
5656 if (v40) { /* [V4.0+] dynflags */
5657 READ_I (uptr->dynflags);
5658 READ_I (uptr->wait);
5659 READ_I (uptr->buf);
5660 }
5661 old_capac = uptr->capac; /* save current capacity */
5662 if (v35) { /* [V3.5+] capacity */
5663 READ_I (uptr->capac);
5664 }
5665 if (!v32)
5666 flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) |
5667 (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */
5668 uptr->flags = (uptr->flags & ~UNIT_RFLAGS) |
5669 (flg & UNIT_RFLAGS); /* restore */
5670 READ_S (buf); /* attached file */
5671 if ((uptr->flags & UNIT_ATT) && /* unit currently attached? */
5672 (!dont_detach_attach)) {
5673 r = scp_detach_unit (dptr, uptr); /* detach it */
5674 if (r != SCPE_OK)
5675 return r;
5676 }
5677 if ((buf[0] != '\0') && /* unit to be reattached? */
5678 ((uptr->flags & UNIT_ATTABLE) || /* and unit is attachable */
5679 (dptr->attach != NULL))) { /* or VM attach routine provided? */
5680 uptr->flags = uptr->flags & ~UNIT_DIS; /* ensure device is enabled */
5681 if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */
5682 sim_switches |= SWMASK ('R'); /* RO attach */
5683 /* add unit to list of units to attach after registers are read */
5684 attunits = (UNIT **)realloc (attunits, sizeof (*attunits)*(attcnt+1));
5685 attunits[attcnt] = uptr;
5686 attnames = (char **)realloc (attnames, sizeof (*attnames)*(attcnt+1));
5687 attnames[attcnt] = (char *)malloc(1+strlen(buf));
5688 strcpy (attnames[attcnt], buf);
5689 attswitches = (int32 *)realloc (attswitches, sizeof (*attswitches)*(attcnt+1));
5690 attswitches[attcnt] = sim_switches;
5691 ++attcnt;
5692 }
5693 READ_I (high); /* memory capacity */
5694 if (high > 0) { /* [V2.5+] any memory? */
5695 if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||
5696 (dptr->deposit == NULL)) {
5697 sim_printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno);
5698 return SCPE_INCOMP;
5699 }
5700 if (high != old_capac) { /* size change? */
5701 uptr->capac = old_capac; /* temp restore old */
5702 if ((dptr->flags & DEV_DYNM) &&
5703 ((dptr->msize == NULL) ||
5704 (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) {
5705 sim_printf ("Can't change memory size: %s%d\n",
5706 sim_dname (dptr), unitno);
5707 return SCPE_INCOMP;
5708 }
5709 uptr->capac = high; /* new memory size */
5710 sim_printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno);
5711 fprint_capac (stdout, dptr, uptr);
5712 if (sim_log)
5713 fprint_capac (sim_log, dptr, uptr);
5714 sim_printf ("\n");
5715 }
5716 sz = SZ_D (dptr); /* allocate buffer */
5717 if ((mbuf = calloc (SRBSIZ, sz)) == NULL)
5718 return SCPE_MEM;
5719 for (k = 0; k < high; ) { /* loop thru mem */
5720 if (sim_fread (&blkcnt, sizeof (blkcnt), 1, rfile) == 0) {/* block count */
5721 free (mbuf);
5722 return SCPE_IOERR;
5723 }
5724 if (blkcnt < 0) /* compressed? */
5725 limit = -blkcnt;
5726 else limit = (int32)sim_fread (mbuf, sz, blkcnt, rfile);
5727 if (limit <= 0) { /* invalid or err? */
5728 free (mbuf);
5729 return SCPE_IOERR;
5730 }
5731 for (j = 0; j < limit; j++, k = k + (dptr->aincr)) {
5732 if (blkcnt < 0) /* compressed? */
5733 val = 0;
5734 else SZ_LOAD (sz, val, mbuf, j); /* saved value */
5735 r = dptr->deposit (val, k, uptr, SIM_SW_REST);
5736 if (r != SCPE_OK) {
5737 free (mbuf);
5738 return r;
5739 }
5740 } /* end for j */
5741 } /* end for k */
5742 free (mbuf); /* dealloc buffer */
5743 } /* end if high */
5744 } /* end unit loop */
5745 for ( ;; ) { /* register loop */
5746 READ_S (buf); /* read reg name */
5747 if (buf[0] == 0) /* last? */
5748 break;
5749 READ_I (depth); /* [V2.10+] depth */
5750 if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {
5751 sim_printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf);
5752 for (us = 0; us < depth; us++) { /* skip values */
5753 READ_I (val);
5754 }
5755 continue;
5756 }
5757 if (depth != rptr->depth) { /* [V2.10+] mismatch? */
5758 sim_printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n",
5759 sim_dname (dptr), buf, depth, rptr->depth);
5760 }
5761 mask = width_mask[rptr->width]; /* get mask */
5762 for (us = 0; us < depth; us++) { /* loop thru values */
5763 READ_I (val); /* read value */
5764 if (val > mask) { /* value ok? */
5765 sim_printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf);
5766 }
5767 else if (us < rptr->depth) /* in range? */
5768 put_rval (rptr, us, val);
5769 }
5770 } /* end register loop */
5771 } /* end device loop */
5772 /* Now that all of the register state has been imported, we can attach
5773 units which were originally attached. Some of these attach operations
5774 may depend on the state of the device (in registers) to work correctly */
5775 for (j=0, r = SCPE_OK; j<attcnt; j++) {
5776 if ((r == SCPE_OK) && (!dont_detach_attach)) {
5777 struct stat fstat;
5778 t_addr saved_pos;
5779
5780 dptr = find_dev_from_unit (attunits[j]);
5781 if ((!force_restore) &&
5782 (!stat(attnames[j], &fstat)))
5783 if (fstat.st_mtime > rstat.st_mtime + 30) {
5784 r = SCPE_INCOMP;
5785 sim_printf ("Error Attaching %s to %s - the restore state is %d seconds older than the attach file\n", sim_dname (dptr), attnames[j], (int)(fstat.st_mtime - rstat.st_mtime));
5786 sim_printf ("restore with the -F switch to override this sanity check\n");
5787 continue;
5788 }
5789 saved_pos = attunits[j]->pos;
5790 sim_switches = attswitches[j];
5791 r = scp_attach_unit (dptr, attunits[j], attnames[j]);/* reattach unit */
5792 attunits[j]->pos = saved_pos;
5793 if (r != SCPE_OK)
5794 sim_printf ("Error Attaching %s to %s\n", sim_dname (dptr), attnames[j]);
5795 }
5796 else {
5797 if ((r == SCPE_OK) && (dont_detach_attach)) {
5798 if ((!suppress_warning) &&
5799 ((!attunits[j]->filename) || (strcmp (attunits[j]->filename, attnames[j]) != 0))) {
5800 warned = TRUE;
5801 sim_printf ("warning - %s was attached to '%s'", sim_uname (attunits[j]), attnames[j]);
5802 if (attunits[j]->filename)
5803 sim_printf (", now attached to '%s'\n", attunits[j]->filename);
5804 else
5805 sim_printf (", now unattached\n");
5806 }
5807 }
5808 }
5809 free (attnames[j]);
5810 }
5811 free (attnames);
5812 free (attunits);
5813 free (attswitches);
5814 if (warned)
5815 sim_printf ("restore with the -Q switch to suppress warning messages\n");
5816 return r;
5817 }
5818
5819 /* Run, go, boot, cont, step, next commands
5820
5821 ru[n] [new PC] reset and start simulation
5822 go [new PC] start simulation
5823 co[nt] start simulation
5824 s[tep] [step limit] start simulation for 'limit' instructions
5825 next start simulation for 1 instruction
5826 stepping over subroutine calls
5827 b[oot] device bootstrap from device and start simulation
5828
5829 switches:
5830 -Q quiet return status
5831 -T (only for step), causes the step limit to
5832 be a number of microseconds to run for
5833 */
5834
5835 t_stat run_cmd (int32 flag, CONST char *cptr)
5836 {
5837 char gbuf[CBUFSIZE] = "";
5838 CONST char *tptr;
5839 uint32 i, j;
5840 int32 sim_next = 0;
5841 int32 unitno;
5842 t_value pcv, orig_pcv;
5843 t_stat r;
5844 DEVICE *dptr;
5845 UNIT *uptr;
5846
5847 GET_SWITCHES (cptr); /* get switches */
5848 sim_step = 0;
5849 if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */
5850 orig_pcv = get_rval (sim_PC, 0); /* get current PC value */
5851 if (*cptr != 0) { /* argument? */
5852 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5853 if (MATCH_CMD (gbuf, "UNTIL") != 0) {
5854 if (sim_dflt_dev && sim_vm_parse_addr) /* address parser? */
5855 pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);
5856 else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */
5857 if ((tptr == gbuf) || (*tptr != 0) || /* error? */
5858 (pcv > width_mask[sim_PC->width]))
5859 return SCPE_ARG;
5860 put_rval (sim_PC, 0, pcv); /* Save in PC */
5861 }
5862 }
5863 if ((flag == RU_RUN) && /* run? */
5864 ((r = sim_run_boot_prep (flag)) != SCPE_OK)) { /* reset sim */
5865 put_rval (sim_PC, 0, orig_pcv); /* restore original PC */
5866 return r;
5867 }
5868 if ((*cptr) || (MATCH_CMD (gbuf, "UNTIL") == 0)) { /* should be end */
5869 int32 saved_switches = sim_switches;
5870
5871 if (MATCH_CMD (gbuf, "UNTIL") != 0)
5872 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5873 if (MATCH_CMD (gbuf, "UNTIL") != 0)
5874 return sim_messagef (SCPE_2MARG, "Unexpected %s command argument: %s %s\n",
5875 (flag == RU_RUN) ? "RUN" : "GO", gbuf, cptr);
5876 sim_switches = 0;
5877 GET_SWITCHES (cptr);
5878 if ((*cptr == '\'') || (*cptr == '"')) { /* Expect UNTIL condition */
5879 r = expect_cmd (1, cptr);
5880 if (r != SCPE_OK)
5881 return r;
5882 }
5883 else { /* BREAK UNTIL condition */
5884 if (sim_switches == 0)
5885 sim_switches = sim_brk_dflt;
5886 sim_switches |= BRK_TYP_TEMP; /* make this a one-shot breakpoint */
5887 sim_brk_types |= BRK_TYP_TEMP;
5888 r = ssh_break (NULL, cptr, SSH_ST);
5889 if (r != SCPE_OK)
5890 return sim_messagef (r, "Unable to establish breakpoint at: %s\n", cptr);
5891 }
5892 sim_switches = saved_switches;
5893 }
5894 }
5895
5896 else if ((flag == RU_STEP) ||
5897 ((flag == RU_NEXT) && !sim_vm_is_subroutine_call)) { /* step */
5898 static t_bool not_implemented_message = FALSE;
5899
5900 if ((!not_implemented_message) && (flag == RU_NEXT)) {
5901 sim_printf ("This simulator does not have subroutine call detection.\nPerforming a STEP instead\n");
5902 not_implemented_message = TRUE;
5903 flag = RU_STEP;
5904 }
5905 if (*cptr != 0) { /* argument? */
5906 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5907 if (*cptr != 0) /* should be end */
5908 return SCPE_2MARG;
5909 sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5910 if ((r != SCPE_OK) || (sim_step <= 0)) /* error? */
5911 return SCPE_ARG;
5912 }
5913 else sim_step = 1;
5914 if ((flag == RU_STEP) && (sim_switches & SWMASK ('T')))
5915 sim_step = (int32)((sim_timer_inst_per_sec ()*sim_step)/1000000.0);
5916 }
5917 else if (flag == RU_NEXT) { /* next */
5918 t_addr *addrs;
5919
5920 if (*cptr != 0) { /* argument? */
5921 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5922 if (*cptr != 0) /* should be end */
5923 return SCPE_2MARG;
5924 sim_next = (int32) get_uint (gbuf, 10, INT_MAX, &r);
5925 if ((r != SCPE_OK) || (sim_next <= 0)) /* error? */
5926 return SCPE_ARG;
5927 }
5928 else sim_next = 1;
5929 if (sim_vm_is_subroutine_call(&addrs)) {
5930 sim_brk_types |= BRK_TYP_DYN_STEPOVER;
5931 for (i=0; addrs[i]; i++)
5932 sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
5933 }
5934 else
5935 sim_step = 1;
5936 }
5937 else if (flag == RU_BOOT) { /* boot */
5938 if (*cptr == 0) /* must be more */
5939 return SCPE_2FARG;
5940 cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
5941 if (*cptr != 0) /* should be end */
5942 return SCPE_2MARG;
5943 dptr = find_unit (gbuf, &uptr); /* locate unit */
5944 if (dptr == NULL) /* found dev? */
5945 return SCPE_NXDEV;
5946 if (uptr == NULL) /* valid unit? */
5947 return SCPE_NXUN;
5948 if (dptr->boot == NULL) /* can it boot? */
5949 return SCPE_NOFNC;
5950 if (uptr->flags & UNIT_DIS) /* disabled? */
5951 return SCPE_UDIS;
5952 if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */
5953 !(uptr->flags & UNIT_ATT))
5954 return SCPE_UNATT;
5955 unitno = (int32) (uptr - dptr->units); /* recover unit# */
5956 if ((r = sim_run_boot_prep (flag)) != SCPE_OK) /* reset sim */
5957 return r;
5958 if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */
5959 return r;
5960 }
5961
5962 else
5963 if (flag != RU_CONT) /* must be cont */
5964 return SCPE_IERR;
5965 else /* CONTINUE command */
5966 if (*cptr != 0) /* should be end (no arguments allowed) */
5967 return sim_messagef (SCPE_2MARG, "CONTINUE command takes no arguments\n");
5968
5969 if (sim_switches & SIM_SW_HIDE) /* Setup only for Remote Console Mode */
5970 return SCPE_OK;
5971
5972 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */
5973 for (j = 0; j < dptr->numunits; j++) { /* seq devices */
5974 uptr = dptr->units + j;
5975 if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == (UNIT_ATT + UNIT_SEQ))
5976 sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);
5977 }
5978 }
5979 stop_cpu = 0;
5980 sim_is_running = 1; /* flag running */
5981 if (sim_ttrun () != SCPE_OK) { /* set console mode */
5982 sim_is_running = 0; /* flag idle */
5983 sim_ttcmd ();
5984 return SCPE_TTYERR;
5985 }
5986 if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */
5987 sim_is_running = 0; /* flag idle */
5988 sim_ttcmd ();
5989 return r;
5990 }
5991 if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */
5992 sim_is_running = 0; /* flag idle */
5993 sim_ttcmd ();
5994 return SCPE_SIGERR;
5995 }
5996 #ifdef SIGHUP
5997 if (signal (SIGHUP, int_handler) == SIG_ERR) { /* set WRU */
5998 sim_is_running = 0; /* flag idle */
5999 sim_ttcmd ();
6000 return SCPE_SIGERR;
6001 }
6002 #endif
6003 if (signal (SIGTERM, int_handler) == SIG_ERR) { /* set WRU */
6004 sim_is_running = 0; /* flag idle */
6005 sim_ttcmd ();
6006 return SCPE_SIGERR;
6007 }
6008 if (sim_step) /* set step timer */
6009 sim_activate (&sim_step_unit, sim_step);
6010 fflush(stdout); /* flush stdout */
6011 if (sim_log) /* flush log if enabled */
6012 fflush (sim_log);
6013 sim_rtcn_init_all (); /* re-init clocks */
6014 sim_start_timer_services (); /* enable wall clock timing */
6015
6016 do {
6017 t_addr *addrs;
6018
6019 while (1) {
6020 r = sim_instr();
6021 if (r != SCPE_REMOTE)
6022 break;
6023 sim_remote_process_command (); /* Process the command and resume processing */
6024 }
6025 if ((flag != RU_NEXT) || /* done if not doing NEXT */
6026 (--sim_next <=0))
6027 break;
6028 if (sim_step == 0) { /* doing a NEXT? */
6029 t_addr val;
6030 BRKTAB *bp;
6031
6032 if (SCPE_BARE_STATUS(r) >= SCPE_BASE) /* done if an error occurred */
6033 break;
6034 if (sim_vm_pc_value) /* done if didn't stop at a dynamic breakpoint */
6035 val = (t_addr)(*sim_vm_pc_value)();
6036 else
6037 val = (t_addr)get_rval (sim_PC, 0);
6038 if ((!(bp = sim_brk_fnd (val))) || (!(bp->typ & BRK_TYP_DYN_STEPOVER)))
6039 break;
6040 sim_brk_clrall (BRK_TYP_DYN_STEPOVER); /* cancel any step/over subroutine breakpoints */
6041 }
6042 else {
6043 if (r != SCPE_STEP) /* done if step didn't complete with step expired */
6044 break;
6045 }
6046 /* setup another next/step */
6047 sim_step = 0;
6048 if (sim_vm_is_subroutine_call(&addrs)) {
6049 sim_brk_types |= BRK_TYP_DYN_STEPOVER;
6050 for (i=0; addrs[i]; i++)
6051 sim_brk_set (addrs[i], BRK_TYP_DYN_STEPOVER, 0, NULL);
6052 }
6053 else
6054 sim_step = 1;
6055 if (sim_step) /* set step timer */
6056 sim_activate (&sim_step_unit, sim_step);
6057 } while (1);
6058
6059 sim_is_running = 0; /* flag idle */
6060 sim_stop_timer_services (); /* disable wall clock timing */
6061 sim_ttcmd (); /* restore console */
6062 sim_brk_clrall (BRK_TYP_DYN_STEPOVER); /* cancel any step/over subroutine breakpoints */
6063 signal (SIGINT, SIG_DFL); /* cancel WRU */
6064 #ifdef SIGHUP
6065 signal (SIGHUP, SIG_DFL); /* cancel WRU */
6066 #endif
6067 signal (SIGTERM, SIG_DFL); /* cancel WRU */
6068 if (sim_log) /* flush console log */
6069 fflush (sim_log);
6070 if (sim_deb) /* flush debug log */
6071 sim_debug_flush ();
6072 for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */
6073 for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */
6074 uptr = dptr->units + j;
6075 if (uptr->flags & UNIT_ATT) { /* attached, */
6076 if (uptr->io_flush) /* unit specific flush routine */
6077 uptr->io_flush (uptr); /* call it */
6078 else {
6079 if (!(uptr->flags & UNIT_BUF) && /* not buffered, */
6080 (uptr->fileref) && /* real file, */
6081 !(uptr->dynflags & UNIT_NO_FIO) && /* is FILE *, */
6082 !(uptr->flags & UNIT_RO)) /* not read only? */
6083 fflush (uptr->fileref);
6084 }
6085 }
6086 }
6087 }
6088 sim_cancel (&sim_step_unit); /* cancel step timer */
6089 UPDATE_SIM_TIME; /* update sim time */
6090 return r | ((sim_switches & SWMASK ('Q')) ? SCPE_NOMESSAGE : 0);
6091 }
6092
6093 /* run command message handler */
6094
6095 void
6096 run_cmd_message (const char *unechoed_cmdline, t_stat r)
6097 {
6098 if (unechoed_cmdline && (r >= SCPE_BASE) && (r != SCPE_STEP) && (r != SCPE_STOP) && (r != SCPE_EXPECT))
6099 sim_printf("%s> %s\n", do_position(), unechoed_cmdline);
6100 fprint_stopped (stdout, r); /* print msg */
6101 if (sim_log && (sim_log != stdout)) /* log if enabled */
6102 fprint_stopped (sim_log, r);
6103 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))/* debug if enabled */
6104 fprint_stopped (sim_deb, r);
6105 }
6106
6107 /* Common setup for RUN or BOOT */
6108
6109 t_stat sim_run_boot_prep (int32 flag)
6110 {
6111 UNIT *uptr;
6112 t_stat r;
6113
6114 sim_interval = 0; /* reset queue */
6115 sim_time = sim_rtime = 0;
6116 noqueue_time = 0;
6117 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
6118 sim_clock_queue = uptr->next;
6119 uptr->next = NULL;
6120 }
6121 r = reset_all (0);
6122 if ((r == SCPE_OK) && (flag == RU_RUN)) {
6123 if ((run_cmd_did_reset) && (0 == (sim_switches & SWMASK ('Q')))) {
6124 sim_printf ("Resetting all devices... This may not have been your intention.\n");
6125 sim_printf ("The GO and CONTINUE commands do not reset devices.\n");
6126 }
6127 run_cmd_did_reset = TRUE;
6128 }
6129 return r;
6130 }
6131
6132 /* Print stopped message
6133 * For VM stops, if a VM-specific "sim_vm_fprint_stopped" pointer is defined,
6134 * call the indicated routine to print additional information after the message
6135 * and before the PC value is printed. If the routine returns FALSE, skip
6136 * printing the PC and its related instruction.
6137 */
6138
6139 void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)
6140 {
6141 int32 i;
6142 t_stat r = 0;
6143 t_addr k;
6144 t_value pcval;
6145
6146 fputc ('\n', st); /* start on a new line */
6147
6148 if (v >= SCPE_BASE) /* SCP error? */
6149 fputs (sim_error_text (v), st); /* print it from the SCP list */
6150 else { /* VM error */
6151 fputs (sim_stop_messages [v], st); /* print the VM-specific message */
6152
6153 if ((sim_vm_fprint_stopped != NULL) && /* if a VM-specific stop handler is defined */
6154 (!sim_vm_fprint_stopped (st, v))) /* call it; if it returned FALSE, */
6155 return; /* we're done */
6156 }
6157
6158 fprintf (st, ", %s: ", pc->name); /* print the name of the PC register */
6159
6160 pcval = get_rval (pc, 0);
6161 if ((pc->flags & REG_VMAD) && sim_vm_fprint_addr) /* if reg wants VM-specific printer */
6162 sim_vm_fprint_addr (st, dptr, (t_addr) pcval); /* call it to print the PC address */
6163 else fprint_val (st, pcval, pc->radix, pc->width, /* otherwise, print as a numeric value */
6164 pc->flags & REG_FMT); /* with the radix and formatting specified */
6165 if ((dptr != NULL) && (dptr->examine != NULL)) {
6166 for (i = 0; i < sim_emax; i++)
6167 sim_eval[i] = 0;
6168 for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {
6169 if ((r = dptr->examine (&sim_eval[i], k, dptr->units, SWMASK ('V')|SIM_SW_STOP)) != SCPE_OK)
6170 break;
6171 }
6172 if ((r == SCPE_OK) || (i > 0)) {
6173 fprintf (st, " (");
6174 if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)
6175 fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);
6176 fprintf (st, ")");
6177 }
6178 }
6179 fprintf (st, "\n");
6180 return;
6181 }
6182
6183 void fprint_stopped (FILE *st, t_stat v)
6184 {
6185 fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);
6186 return;
6187 }
6188
6189 /* Unit service for step timeout, originally scheduled by STEP n command
6190 Return step timeout SCP code, will cause simulation to stop */
6191
6192 t_stat step_svc (UNIT *uptr)
6193 {
6194 return SCPE_STEP;
6195 }
6196
6197 /* Unit service to facilitate expect matching to stop simulation.
6198 Return expect SCP code, will cause simulation to stop */
6199
6200 t_stat expect_svc (UNIT *uptr)
6201 {
6202 return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE);
6203 }
6204
6205 /* Signal handler for ^C signal - set stop simulation flag */
6206
6207 void int_handler (int sig)
6208 {
6209 stop_cpu = 1;
6210 return;
6211 }
6212
6213 /* Examine/deposit commands
6214
6215 ex[amine] [modifiers] list examine
6216 de[posit] [modifiers] list val deposit
6217 ie[xamine] [modifiers] list interactive examine
6218 id[eposit] [modifiers] list interactive deposit
6219
6220 modifiers
6221 @filename output file
6222 -letter(s) switches
6223 devname'n device name and unit number
6224 [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification
6225
6226 list list of addresses and registers
6227 addr[:addr|-addr] address range
6228 ALL all addresses
6229 register[:register|-register] register range
6230 register[index] register array element
6231 register[start:end] register array range
6232 STATE all registers
6233 */
6234
6235 t_stat exdep_cmd (int32 flag, CONST char *cptr)
6236 {
6237 char gbuf[CBUFSIZE];
6238 CONST char *gptr;
6239 CONST char *tptr = NULL;
6240 int32 opt;
6241 t_addr low, high;
6242 t_stat reason;
6243 DEVICE *tdptr;
6244 REG *lowr, *highr;
6245 FILE *ofile;
6246
6247 opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */
6248 if (flag == EX_E) /* extra for EX */
6249 opt = opt | CMD_OPT_OF;
6250 cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */
6251 if (!cptr) /* error? */
6252 return reason;
6253 if (*cptr == 0) /* must be more */
6254 return SCPE_2FARG;
6255 if (sim_dfunit == NULL) /* got a unit? */
6256 return SCPE_NXUN;
6257 cptr = get_glyph (cptr, gbuf, 0); /* get list */
6258 if ((flag == EX_D) && (*cptr == 0)) /* deposit needs more */
6259
6260 return SCPE_2FARG;
6261 ofile = sim_ofile? sim_ofile: stdout; /* no ofile? use stdout */
6262
6263 for (gptr = gbuf, reason = SCPE_OK;
6264 (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
6265 tdptr = sim_dfdev; /* working dptr */
6266 if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
6267 tptr = gptr + strlen ("STATE");
6268 if (*tptr && (*tptr++ != ','))
6269 return SCPE_ARG;
6270 if ((lowr = sim_dfdev->registers) == NULL)
6271 return SCPE_NXREG;
6272 for (highr = lowr; highr->name != NULL; highr++) ;
6273 sim_switches = sim_switches | SIM_SW_HIDE;
6274 reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6275 lowr, --highr, 0, 0);
6276 continue;
6277 }
6278
6279 if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */
6280 (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */
6281 (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {
6282 low = high = 0;
6283 if ((*tptr == '-') || (*tptr == ':')) {
6284 highr = find_reg (tptr + 1, &tptr, tdptr);
6285 if (highr == NULL)
6286 return SCPE_NXREG;
6287 }
6288 else {
6289 highr = lowr;
6290 if (*tptr == '[') {
6291 if (lowr->depth <= 1)
6292 return SCPE_ARG;
6293 tptr = get_range (NULL, tptr + 1, &low, &high,
6294 10, lowr->depth - 1, ']');
6295 if (tptr == NULL)
6296 return SCPE_ARG;
6297 }
6298 }
6299 if (*tptr && (*tptr++ != ','))
6300 return SCPE_ARG;
6301 reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr,
6302 lowr, highr, (uint32) low, (uint32) high);
6303 continue;
6304 }
6305
6306 tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,
6307 (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:
6308 sim_dfunit->capac - sim_dfdev->aincr), 0);
6309 if (tptr == NULL)
6310 return SCPE_ARG;
6311 if (*tptr && (*tptr++ != ','))
6312 return SCPE_ARG;
6313 reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high,
6314 sim_dfdev, sim_dfunit);
6315 } /* end for */
6316 if (sim_ofile) /* close output file */
6317 fclose (sim_ofile);
6318 return reason;
6319 }
6320
6321 /* Loop controllers for examine/deposit
6322
6323 exdep_reg_loop examine/deposit range of registers
6324 exdep_addr_loop examine/deposit range of addresses
6325 */
6326
6327 t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr,
6328 REG *lowr, REG *highr, uint32 lows, uint32 highs)
6329 {
6330 t_stat reason;
6331 uint32 idx, val_start=lows;
6332 t_value val, last_val;
6333 REG *rptr;
6334
6335 if ((lowr == NULL) || (highr == NULL))
6336 return SCPE_IERR;
6337 if (lowr > highr)
6338 return SCPE_ARG;
6339 for (rptr = lowr; rptr <= highr; rptr++) {
6340 if ((sim_switches & SIM_SW_HIDE) &&
6341 (rptr->flags & REG_HIDDEN))
6342 continue;
6343 val = last_val = 0;
6344 for (idx = lows; idx <= highs; idx++) {
6345 if (idx >= rptr->depth)
6346 return SCPE_SUB;
6347 val = get_rval (rptr, idx);
6348 if (schptr && !test_search (&val, schptr))
6349 continue;
6350 if (flag == EX_E) {
6351 if ((idx > lows) && (val == last_val))
6352 continue;
6353 if (idx > val_start+1) {
6354 if (idx-1 == val_start+1) {
6355 reason = ex_reg (ofile, val, flag, rptr, idx-1);
6356 if (reason != SCPE_OK)
6357 return reason;
6358 if (sim_log && (ofile == stdout))
6359 ex_reg (sim_log, val, flag, rptr, idx-1);
6360 }
6361 else {
6362 if (val_start+1 != idx-1) {
6363 Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6364 if (sim_log && (ofile == stdout))
6365 Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, idx-1);
6366 }
6367 else {
6368 Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6369 if (sim_log && (ofile == stdout))
6370 Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6371 }
6372 }
6373 }
6374 sim_last_val = last_val = val;
6375 val_start = idx;
6376 reason = ex_reg (ofile, val, flag, rptr, idx);
6377 if (reason != SCPE_OK)
6378 return reason;
6379 if (sim_log && (ofile == stdout))
6380 ex_reg (sim_log, val, flag, rptr, idx);
6381 }
6382 if (flag != EX_E) {
6383 reason = dep_reg (flag, cptr, rptr, idx);
6384 if (reason != SCPE_OK)
6385 return reason;
6386 }
6387 }
6388 if ((flag == EX_E) && (val_start != highs)) {
6389 if (highs == val_start+1) {
6390 reason = ex_reg (ofile, val, flag, rptr, highs);
6391 if (reason != SCPE_OK)
6392 return reason;
6393 if (sim_log && (ofile == stdout))
6394 ex_reg (sim_log, val, flag, rptr, highs);
6395 }
6396 else {
6397 if (val_start+1 != highs) {
6398 Fprintf (ofile, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6399 if (sim_log && (ofile == stdout))
6400 Fprintf (sim_log, "%s[%d]-%s[%d]: same as above\n", rptr->name, val_start+1, rptr->name, highs);
6401 }
6402 else {
6403 Fprintf (ofile, "%s[%d]: same as above\n", rptr->name, val_start+1);
6404 if (sim_log && (ofile == stdout))
6405 Fprintf (sim_log, "%s[%d]: same as above\n", rptr->name, val_start+1);
6406 }
6407 }
6408 }
6409 }
6410 return SCPE_OK;
6411 }
6412
6413 t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr,
6414 t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
6415 {
6416 t_addr i, mask;
6417 t_stat reason;
6418
6419 if (uptr->flags & UNIT_DIS) /* disabled? */
6420 return SCPE_UDIS;
6421 mask = (t_addr) width_mask[dptr->awidth];
6422 if ((low > mask) || (high > mask) || (low > high))
6423 return SCPE_ARG;
6424 for (i = low; i <= high; ) { /* all paths must incr!! */
6425 reason = get_aval (i, dptr, uptr); /* get data */
6426 if (reason != SCPE_OK) /* return if error */
6427 return reason;
6428 if (schptr && !test_search (sim_eval, schptr))
6429 i = i + dptr->aincr; /* sch fails, incr */
6430 else { /* no sch or success */
6431 if (flag != EX_D) { /* ex, ie, or id? */
6432 reason = ex_addr (ofile, flag, i, dptr, uptr);
6433 if (reason > SCPE_OK)
6434 return reason;
6435 if (sim_log && (ofile == stdout))
6436 ex_addr (sim_log, flag, i, dptr, uptr);
6437 }
6438 else reason = 1 - dptr->aincr; /* no, dflt incr */
6439 if (flag != EX_E) { /* ie, id, or d? */
6440 reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
6441 if (reason > SCPE_OK)
6442 return reason;
6443 }
6444 i = i + (1 - reason); /* incr */
6445 }
6446 }
6447 return SCPE_OK;
6448 }
6449
6450 /* Examine register routine
6451
6452 Inputs:
6453 ofile = output stream
6454 val = current register value
6455 flag = type of ex/mod command (ex, iex, idep)
6456 rptr = pointer to register descriptor
6457 idx = index
6458 Outputs:
6459 return = error status
6460 */
6461
6462 t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)
6463 {
6464 int32 rdx;
6465
6466 if (rptr == NULL)
6467 return SCPE_IERR;
6468 if (rptr->depth > 1)
6469 Fprintf (ofile, "%s[%d]:\t", rptr->name, idx);
6470 else Fprintf (ofile, "%s:\t", rptr->name);
6471 if (!(flag & EX_E))
6472 return SCPE_OK;
6473 GET_RADIX (rdx, rptr->radix);
6474 if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr && sim_dflt_dev)
6475 sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);
6476 else if (!(rptr->flags & REG_VMFLAGS) ||
6477 (fprint_sym (ofile, (rptr->flags & REG_UFMASK) | rdx, &val,
6478 NULL, sim_switches | SIM_SW_REG) > 0)) {
6479 fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);
6480 if (rptr->fields) {
6481 Fprintf (ofile, "\t");
6482 fprint_fields (ofile, val, val, rptr->fields);
6483 }
6484 }
6485 if (flag & EX_I)
6486 Fprintf (ofile, "\t");
6487 else Fprintf (ofile, "\n");
6488 return SCPE_OK;
6489 }
6490
6491 /* Get register value
6492
6493 Inputs:
6494 rptr = pointer to register descriptor
6495 idx = index
6496 Outputs:
6497 return = register value
6498 */
6499
6500 t_value get_rval (REG *rptr, uint32 idx)
6501 {
6502 size_t sz;
6503 t_value val;
6504 uint32 *ptr;
6505
6506 sz = SZ_R (rptr);
6507 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6508 idx = idx + rptr->qptr;
6509 if (idx >= rptr->depth) idx = idx - rptr->depth;
6510 }
6511 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6512 ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6513 if (sz <= sizeof (uint32))
6514 val = *ptr;
6515 else val = *((t_uint64 *) ptr);
6516 }
6517 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6518 ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6519 if (sz <= sizeof (uint32))
6520 val = *ptr;
6521 else val = *((t_uint64 *) ptr);
6522 }
6523 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6524 (sz == sizeof (uint8)))
6525 val = *(((uint8 *) rptr->loc) + idx);
6526 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6527 (sz == sizeof (uint16)))
6528 val = *(((uint16 *) rptr->loc) + idx);
6529 else if (sz <= sizeof (uint32))
6530 val = *(((uint32 *) rptr->loc) + idx);
6531 else val = *(((t_uint64 *) rptr->loc) + idx);
6532 val = (val >> rptr->offset) & width_mask[rptr->width];
6533 return val;
6534 }
6535
6536 /* Deposit register routine
6537
6538 Inputs:
6539 flag = type of deposit (normal/interactive)
6540 cptr = pointer to input string
6541 rptr = pointer to register descriptor
6542 idx = index
6543 Outputs:
6544 return = error status
6545 */
6546
6547 t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx)
6548 {
6549 t_stat r;
6550 t_value val, mask;
6551 int32 rdx;
6552 CONST char *tptr;
6553 char gbuf[CBUFSIZE];
6554
6555 if ((cptr == NULL) || (rptr == NULL))
6556 return SCPE_IERR;
6557 if (rptr->flags & REG_RO)
6558 return SCPE_RO;
6559 if (flag & EX_I) {
6560 cptr = read_line (gbuf, sizeof(gbuf), stdin);
6561 if (sim_log)
6562 fprintf (sim_log, "%s\n", cptr? cptr: "");
6563 if (cptr == NULL) /* force exit */
6564 return 1;
6565 if (*cptr == 0) /* success */
6566 return SCPE_OK;
6567 }
6568 mask = width_mask[rptr->width];
6569 GET_RADIX (rdx, rptr->radix);
6570 if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr && sim_dflt_dev) { /* address form? */
6571 val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);
6572 if ((tptr == cptr) || (*tptr != 0) || (val > mask))
6573 return SCPE_ARG;
6574 }
6575 else
6576 if (!(rptr->flags & REG_VMFLAGS) || /* dont use sym? */
6577 (parse_sym ((CONST char *)cptr, (rptr->flags & REG_UFMASK) | rdx, NULL,
6578 &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {
6579 val = get_uint (cptr, rdx, mask, &r);
6580 if (r != SCPE_OK)
6581 return SCPE_ARG;
6582 }
6583 if ((rptr->flags & REG_NZ) && (val == 0))
6584 return SCPE_ARG;
6585 put_rval (rptr, idx, val);
6586 return SCPE_OK;
6587 }
6588
6589 /* Put register value
6590
6591 Inputs:
6592 rptr = pointer to register descriptor
6593 idx = index
6594 val = new value
6595 mask = mask
6596 Outputs:
6597 none
6598 */
6599
6600 void put_rval (REG *rptr, uint32 idx, t_value val)
6601 {
6602 size_t sz;
6603 t_value mask;
6604 uint32 *ptr;
6605
6606 #define PUT_RVAL(sz,rp,id,v,m) \
6607 *(((sz *) rp->loc) + id) = \
6608 (sz)((*(((sz *) rp->loc) + id) & \
6609 ~((m) << (rp)->offset)) | ((v) << (rp)->offset))
6610
6611 if (rptr == sim_PC)
6612 sim_brk_npc (0);
6613 sz = SZ_R (rptr);
6614 mask = width_mask[rptr->width];
6615 if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {
6616 idx = idx + rptr->qptr;
6617 if (idx >= rptr->depth)
6618 idx = idx - rptr->depth;
6619 }
6620 if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {
6621 ptr = (uint32 *)(((UNIT *) rptr->loc) + idx);
6622 if (sz <= sizeof (uint32))
6623 *ptr = (*ptr &
6624 ~(((uint32) mask) << rptr->offset)) |
6625 (((uint32) val) << rptr->offset);
6626 else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6627 & ~(mask << rptr->offset)) | (val << rptr->offset);
6628 }
6629 else if ((rptr->depth > 1) && (rptr->flags & REG_STRUCT)) {
6630 ptr = (uint32 *)(((size_t) rptr->loc) + (idx * rptr->str_size));
6631 if (sz <= sizeof (uint32))
6632 *((uint32 *) ptr) = (*((uint32 *) ptr) &
6633 ~(((uint32) mask) << rptr->offset)) |
6634 (((uint32) val) << rptr->offset);
6635 else *((t_uint64 *) ptr) = (*((t_uint64 *) ptr)
6636 & ~(mask << rptr->offset)) | (val << rptr->offset);
6637 }
6638 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6639 (sz == sizeof (uint8)))
6640 PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
6641 else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&
6642 (sz == sizeof (uint16)))
6643 PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
6644 else if (sz <= sizeof (uint32))
6645 PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
6646 else PUT_RVAL (t_uint64, rptr, idx, val, mask);
6647 return;
6648 }
6649
6650 /* Examine address routine
6651
6652 Inputs: (sim_eval is an implicit argument)
6653 ofile = output stream
6654 flag = type of ex/mod command (ex, iex, idep)
6655 addr = address to examine
6656 dptr = pointer to device
6657 uptr = pointer to unit
6658 Outputs:
6659 return = if > 0, error status
6660 if <= 0,-number of extra addr units retired
6661 */
6662
6663 t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
6664 {
6665 t_stat reason;
6666 int32 rdx;
6667
6668 if (sim_vm_fprint_addr)
6669 sim_vm_fprint_addr (ofile, dptr, addr);
6670 else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);
6671 Fprintf (ofile, ":\t");
6672 if (!(flag & EX_E))
6673 return (1 - dptr->aincr);
6674
6675 GET_RADIX (rdx, dptr->dradix);
6676 if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {
6677 fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);
6678 reason = 1 - dptr->aincr;
6679 }
6680 if (flag & EX_I)
6681 Fprintf (ofile, "\t");
6682 else Fprintf (ofile, "\n");
6683 return reason;
6684 }
6685
6686 /* Get address routine
6687
6688 Inputs:
6689 flag = type of ex/mod command (ex, iex, idep)
6690 addr = address to examine
6691 dptr = pointer to device
6692 uptr = pointer to unit
6693 Outputs: (sim_eval is an implicit output)
6694 return = error status
6695 */
6696
6697 t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
6698 {
6699 int32 i;
6700 t_value mask;
6701 t_addr j, loc;
6702 size_t sz;
6703 t_stat reason = SCPE_OK;
6704
6705 if ((dptr == NULL) || (uptr == NULL))
6706 return SCPE_IERR;
6707 mask = width_mask[dptr->dwidth];
6708 for (i = 0; i < sim_emax; i++)
6709 sim_eval[i] = 0;
6710 for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {
6711 if (dptr->examine != NULL) {
6712 reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);
6713 if (reason != SCPE_OK)
6714 break;
6715 }
6716 else {
6717 if (!(uptr->flags & UNIT_ATT))
6718 return SCPE_UNATT;
6719 if (uptr->dynflags & UNIT_NO_FIO)
6720 return SCPE_NOFNC;
6721 if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {
6722 reason = SCPE_NXM;
6723 break;
6724 }
6725 sz = SZ_D (dptr);
6726 loc = j / dptr->aincr;
6727 if (uptr->flags & UNIT_BUF) {
6728 SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);
6729 }
6730 else {
6731 sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6732 sim_fread (&sim_eval[i], sz, 1, uptr->fileref);
6733 if ((feof (uptr->fileref)) &&
6734 !(uptr->flags & UNIT_FIX)) {
6735 reason = SCPE_EOF;
6736 break;
6737 }
6738 else if (ferror (uptr->fileref)) {
6739 clearerr (uptr->fileref);
6740 reason = SCPE_IOERR;
6741 break;
6742 }
6743 }
6744 }
6745 sim_last_val = sim_eval[i] = sim_eval[i] & mask;
6746 }
6747 if ((reason != SCPE_OK) && (i == 0))
6748 return reason;
6749 return SCPE_OK;
6750 }
6751
6752 /* Deposit address routine
6753
6754 Inputs:
6755 flag = type of deposit (normal/interactive)
6756 cptr = pointer to input string
6757 addr = address to examine
6758 dptr = pointer to device
6759 uptr = pointer to unit
6760 dfltinc = value to return on cr input
6761 Outputs:
6762 return = if > 0, error status
6763 if <= 0, -number of extra address units retired
6764 */
6765
6766 t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
6767 UNIT *uptr, int32 dfltinc)
6768 {
6769 int32 i, count, rdx;
6770 t_addr j, loc;
6771 t_stat r, reason;
6772 t_value mask;
6773 size_t sz;
6774 char gbuf[CBUFSIZE];
6775
6776 if (dptr == NULL)
6777 return SCPE_IERR;
6778 if (flag & EX_I) {
6779 cptr = read_line (gbuf, sizeof(gbuf), stdin);
6780 if (sim_log)
6781 fprintf (sim_log, "%s\n", cptr? cptr: "");
6782 if (cptr == NULL) /* force exit */
6783 return 1;
6784 if (*cptr == 0) /* success */
6785 return dfltinc;
6786 }
6787 if (uptr->flags & UNIT_RO) /* read only? */
6788 return SCPE_RO;
6789 mask = width_mask[dptr->dwidth];
6790
6791 GET_RADIX (rdx, dptr->dradix);
6792 if ((reason = parse_sym ((CONST char *)cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
6793 sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
6794 if (reason != SCPE_OK)
6795 return reason;
6796 reason = dfltinc;
6797 }
6798 count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;
6799
6800 for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {
6801 sim_eval[i] = sim_eval[i] & mask;
6802 if (dptr->deposit != NULL) {
6803 r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);
6804 if (r != SCPE_OK)
6805 return r;
6806 }
6807 else {
6808 if (!(uptr->flags & UNIT_ATT))
6809 return SCPE_UNATT;
6810 if (uptr->dynflags & UNIT_NO_FIO)
6811 return SCPE_NOFNC;
6812 if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))
6813 return SCPE_NXM;
6814 sz = SZ_D (dptr);
6815 loc = j / dptr->aincr;
6816 if (uptr->flags & UNIT_BUF) {
6817 SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);
6818 if (loc >= uptr->hwmark)
6819 uptr->hwmark = (uint32) loc + 1;
6820 }
6821 else {
6822 sim_fseek (uptr->fileref, (t_addr)(sz * loc), SEEK_SET);
6823 sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);
6824 if (ferror (uptr->fileref)) {
6825 clearerr (uptr->fileref);
6826 return SCPE_IOERR;
6827 }
6828 }
6829 }
6830 }
6831 return reason;
6832 }
6833
6834 /* Evaluate command */
6835
6836 t_stat eval_cmd (int32 flg, CONST char *cptr)
6837 {
6838 if (!sim_dflt_dev)
6839 return SCPE_ARG;
6840 DEVICE *dptr = sim_dflt_dev;
6841 int32 i, rdx, a, lim;
6842 t_stat r;
6843
6844 GET_SWITCHES (cptr);
6845 GET_RADIX (rdx, dptr->dradix);
6846 for (i = 0; i < sim_emax; i++)
6847 sim_eval[i] = 0;
6848 if (*cptr == 0)
6849 return SCPE_2FARG;
6850 if ((r = parse_sym ((CONST char *)cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {
6851 sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);
6852 if (r != SCPE_OK)
6853 return r;
6854 }
6855 lim = 1 - r;
6856 for (i = a = 0; a < lim; ) {
6857 sim_printf ("%d:\t", a);
6858 if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6859 r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6860 if (sim_log) {
6861 if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)
6862 r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);
6863 }
6864 sim_printf ("\n");
6865 if (r < 0)
6866 a = a + 1 - r;
6867 else a = a + dptr->aincr;
6868 i = a / dptr->aincr;
6869 }
6870 return SCPE_OK;
6871 }
6872
6873 /* String processing routines
6874
6875 read_line read line
6876
6877 Inputs:
6878 cptr = pointer to buffer
6879 size = maximum size
6880 stream = pointer to input stream
6881 Outputs:
6882 optr = pointer to first non-blank character
6883 NULL if EOF
6884 */
6885
6886 char *read_line (char *cptr, int32 size, FILE *stream)
6887 {
6888 return read_line_p (NULL, cptr, size, stream);
6889 }
6890
6891 /* read_line_p read line with prompt
6892
6893 Inputs:
6894 prompt = pointer to prompt string
6895 cptr = pointer to buffer
6896 size = maximum size
6897 stream = pointer to input stream
6898 Outputs:
6899 optr = pointer to first non-blank character
6900 NULL if EOF
6901 */
6902
6903 char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream)
6904 {
6905 char *tptr;
6906
6907 if (prompt) { /* interactive? */
6908 #ifdef HAVE_LINEHISTORY
6909 char *tmpc = linenoise (prompt); /* get cmd line */
6910 if (tmpc == NULL) /* bad result? */
6911 cptr = NULL;
6912 else {
6913 strncpy (cptr, tmpc, size-1); /* copy result */
6914 linenoiseHistoryAdd (tmpc); /* add to history */
6915 free (tmpc); /* free temp */
6916 }
6917 }
6918 #else
6919 fflush (stdout); /* flush output */
6920 printf ("%s", prompt); /* display prompt */
6921 fflush (stdout); /* flush output */
6922 cptr = fgets (cptr, size, stream); /* get cmd line */
6923 }
6924 #endif /* ifdef HAVE_LINEHISTORY */
6925 else cptr = fgets (cptr, size, stream); /* get cmd line */
6926
6927 if (cptr == NULL) {
6928 clearerr (stream); /* clear error */
6929 return NULL; /* ignore EOF */
6930 }
6931 for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */
6932 if ((*tptr == '\n') || (*tptr == '\r') ||
6933 (tptr == (cptr + size - 1))) { /* str max length? */
6934 *tptr = 0; /* terminate */
6935 break;
6936 }
6937 }
6938 if (0 == memcmp (cptr, "\xEF\xBB\xBF", 3)) /* Skip/ignore UTF8_BOM */
6939 memmove (cptr, cptr + 3, strlen (cptr + 3));
6940 while (sim_isspace (*cptr)) /* trim leading spc */
6941 cptr++;
6942 if ((*cptr == ';') || (*cptr == '#')) { /* ignore comment */
6943 if (sim_do_echo) /* echo comments if -v */
6944 sim_printf("%s> %s\n", do_position(), cptr);
6945 *cptr = 0;
6946 }
6947
6948 return cptr;
6949 }
6950
6951 /* get_glyph get next glyph (force upper case)
6952 get_glyph_nc get next glyph (no conversion)
6953 get_glyph_quoted get next glyph (potentially enclosed in quotes, no conversion)
6954 get_glyph_cmd get command glyph (force upper case, extract leading !)
6955 get_glyph_gen get next glyph (general case)
6956
6957 Inputs:
6958 iptr = pointer to input string
6959 optr = pointer to output string
6960 mchar = optional end of glyph character
6961 uc = TRUE for convert to upper case (_gen only)
6962 quote = TRUE to allow quote enclosing values (_gen only)
6963 escape_char = optional escape character within quoted strings (_gen only)
6964
6965 Outputs
6966 result = pointer to next character in input string
6967 */
6968
6969 static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char)
6970 {
6971 t_bool quoting = FALSE;
6972 t_bool escaping = FALSE;
6973 char quote_char = 0;
6974
6975 while ((*iptr != 0) &&
6976 ((quote && quoting) || ((sim_isspace (*iptr) == 0) && (*iptr != mchar)))) {
6977 if (quote) {
6978 if (quoting) {
6979 if (!escaping) {
6980 if (*iptr == escape_char)
6981 escaping = TRUE;
6982 else
6983 if (*iptr == quote_char)
6984 quoting = FALSE;
6985 }
6986 else
6987 escaping = FALSE;
6988 }
6989 else {
6990 if ((*iptr == '"') || (*iptr == '\'')) {
6991 quoting = TRUE;
6992 quote_char = *iptr;
6993 }
6994 }
6995 }
6996 if (sim_islower (*iptr) && uc)
6997 *optr = (char)toupper (*iptr);
6998 else *optr = *iptr;
6999 iptr++; optr++;
7000 }
7001 if (mchar && (*iptr == mchar)) /* skip input terminator */
7002 iptr++;
7003 *optr = 0; /* terminate result string */
7004 while (sim_isspace (*iptr)) /* absorb additional input spaces */
7005 iptr++;
7006 return iptr;
7007 }
7008
7009 CONST char *get_glyph (const char *iptr, char *optr, char mchar)
7010 {
7011 return (CONST char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0);
7012 }
7013
7014 CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar)
7015 {
7016 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0);
7017 }
7018
7019 CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar)
7020 {
7021 return (CONST char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\');
7022 }
7023
7024 CONST char *get_glyph_cmd (const char *iptr, char *optr)
7025 {
7026 /* Tolerate "!subprocess" vs. requiring "! subprocess" */
7027 if ((iptr[0] == '!') && (!sim_isspace(iptr[1]))) {
7028 strcpy (optr, "!"); /* return ! as command glyph */
7029 return (CONST char *)(iptr + 1); /* and skip over the leading ! */
7030 }
7031 return (CONST char *)get_glyph_gen (iptr, optr, 0, TRUE, FALSE, 0);
7032 }
7033
7034 /* Trim trailing spaces from a string
7035
7036 Inputs:
7037 cptr = pointer to string
7038 Outputs:
7039 cptr = pointer to string
7040 */
7041
7042 char *sim_trim_endspc (char *cptr)
7043 {
7044 char *tptr;
7045
7046 tptr = cptr + strlen (cptr);
7047 while ((--tptr >= cptr) && sim_isspace (*tptr))
7048 *tptr = 0;
7049 return cptr;
7050 }
7051
7052 int sim_isspace (char c)
7053 {
7054 return (c & 0x80) ? 0 : isspace (c);
7055 }
7056
7057 int sim_islower (char c)
7058 {
7059 return (c & 0x80) ? 0 : islower (c);
7060 }
7061
7062 int sim_isalpha (char c)
7063 {
7064 return (c & 0x80) ? 0 : isalpha (c);
7065 }
7066
7067 int sim_isprint (char c)
7068 {
7069 return (c & 0x80) ? 0 : isprint (c);
7070 }
7071
7072 int sim_isdigit (char c)
7073 {
7074 return (c & 0x80) ? 0 : isdigit (c);
7075 }
7076
7077 int sim_isgraph (char c)
7078 {
7079 return (c & 0x80) ? 0 : isgraph (c);
7080 }
7081
7082 int sim_isalnum (char c)
7083 {
7084 return (c & 0x80) ? 0 : isalnum (c);
7085 }
7086
7087 /* get_uint unsigned number
7088
7089 Inputs:
7090 cptr = pointer to input string
7091 radix = input radix
7092 max = maximum acceptable value
7093 *status = pointer to error status
7094 Outputs:
7095 val = value
7096 */
7097
7098 t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status)
7099 {
7100 t_value val;
7101 CONST char *tptr;
7102
7103 *status = SCPE_OK;
7104 val = strtotv ((CONST char *)cptr, &tptr, radix);
7105 if ((cptr == tptr) || (val > max))
7106 *status = SCPE_ARG;
7107 else {
7108 while (sim_isspace (*tptr)) tptr++;
7109 if (*tptr != 0)
7110 *status = SCPE_ARG;
7111 }
7112 return val;
7113 }
7114
7115 /* get_range range specification
7116
7117 Inputs:
7118 dptr = pointer to device (NULL if none)
7119 cptr = pointer to input string
7120 *lo = pointer to low result
7121 *hi = pointer to high result
7122 aradix = radix
7123 max = default high value
7124 term = terminating character, 0 if none
7125 Outputs:
7126 tptr = input pointer after processing
7127 NULL if error
7128 */
7129
7130 CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi,
7131 uint32 rdx, t_addr max, char term)
7132 {
7133 CONST char *tptr;
7134
7135 if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */
7136 tptr = cptr + strlen ("ALL");
7137 *lo = 0;
7138 *hi = max;
7139 }
7140 else {
7141 if ((strncmp (cptr, ".", strlen (".")) == 0) && /* .? */
7142 ((cptr[1] == '\0') ||
7143 (cptr[1] == '-') ||
7144 (cptr[1] == ':') ||
7145 (cptr[1] == '/'))) {
7146 tptr = cptr + strlen (".");
7147 *lo = *hi = sim_last_addr;
7148 }
7149 else {
7150 if (strncmp (cptr, "$", strlen ("$")) == 0) { /* $? */
7151 tptr = cptr + strlen ("$");
7152 *hi = *lo = (t_addr)sim_last_val;
7153 }
7154 else {
7155 if (dptr && sim_vm_parse_addr) /* get low */
7156 *lo = sim_vm_parse_addr (dptr, cptr, &tptr);
7157 else
7158 *lo = (t_addr) strtotv (cptr, &tptr, rdx);
7159 if (cptr == tptr) /* error? */
7160 return NULL;
7161 }
7162 }
7163 if ((*tptr == '-') || (*tptr == ':')) { /* range? */
7164 cptr = tptr + 1;
7165 if (dptr && sim_vm_parse_addr) /* get high */
7166 *hi = sim_vm_parse_addr (dptr, cptr, &tptr);
7167 else *hi = (t_addr) strtotv (cptr, &tptr, rdx);
7168 if (cptr == tptr)
7169 return NULL;
7170 if (*lo > *hi)
7171 return NULL;
7172 }
7173 else if (*tptr == '/') { /* relative? */
7174 cptr = tptr + 1;
7175 *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */
7176 if ((cptr == tptr) || (*hi == 0))
7177 return NULL;
7178 *hi = *lo + *hi - 1;
7179 }
7180 else *hi = *lo;
7181 }
7182 sim_last_addr = *hi;
7183 if (term && (*tptr++ != term))
7184 return NULL;
7185 return tptr;
7186 }
7187
7188 /* sim_decode_quoted_string
7189
7190 Inputs:
7191 iptr = pointer to input string
7192 optr = pointer to output buffer
7193 the output buffer must be allocated by the caller
7194 and to avoid overrunat it must be at least as big
7195 as the input string.
7196
7197 Outputs
7198 result = status of decode SCPE_OK when good, SCPE_ARG otherwise
7199 osize = size of the data in the optr buffer
7200
7201 The input string must be quoted. Quotes may be either single or
7202 double but the opening anc closing quote characters must match.
7203 Within quotes C style character escapes are allowed.
7204
7205 The following character escapes are explicitly supported:
7206 \r ASCII Carriage Return character (Decimal value 13)
7207 \n ASCII Linefeed character (Decimal value 10)
7208 \f ASCII Formfeed character (Decimal value 12)
7209 \t ASCII Horizontal Tab character (Decimal value 9)
7210 \v ASCII Vertical Tab character (Decimal value 11)
7211 \b ASCII Backspace character (Decimal value 8)
7212 \\ ASCII Backslash character (Decimal value 92)
7213 \' ASCII Single Quote character (Decimal value 39)
7214 \" ASCII Double Quote character (Decimal value 34)
7215 \? ASCII Question Mark character (Decimal value 63)
7216 \e ASCII Escape character (Decimal value 27)
7217 as well as octal character values of the form:
7218 \n{n{n}} where each n is an octal digit (0-7)
7219 and hext character values of the form:
7220 \xh{h} where each h is a hex digit (0-9A-Fa-f)
7221 */
7222
7223 t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize)
7224 {
7225 char quote_char;
7226 uint8 *ostart = optr;
7227
7228 *osize = 0;
7229 if ((strlen(iptr) == 1) ||
7230 (iptr[0] != iptr[strlen(iptr)-1]) ||
7231 ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\'')))
7232 return SCPE_ARG; /* String must be quote delimited */
7233 quote_char = *iptr++; /* Save quote character */
7234 while (iptr[1]) { /* Skip trailing quote */
7235 if (*iptr != '\\') {
7236 if (*iptr == quote_char)
7237 return SCPE_ARG; /* Imbedded quotes must be escaped */
7238 *(optr++) = (uint8)(*(iptr++));
7239 continue;
7240 }
7241 ++iptr; /* Skip backslash */
7242 switch (*iptr) {
7243 case 'r': /* ASCII Carriage Return character (Decimal value 13) */
7244 *(optr++) = 13; ++iptr;
7245 break;
7246 case 'n': /* ASCII Linefeed character (Decimal value 10) */
7247 *(optr++) = 10; ++iptr;
7248 break;
7249 case 'f': /* ASCII Formfeed character (Decimal value 12) */
7250 *(optr++) = 12; ++iptr;
7251 break;
7252 case 't': /* ASCII Horizontal Tab character (Decimal value 9) */
7253 *(optr++) = 9; ++iptr;
7254 break;
7255 case 'v': /* ASCII Vertical Tab character (Decimal value 11) */
7256 *(optr++) = 11; ++iptr;
7257 break;
7258 case 'b': /* ASCII Backspace character (Decimal value 8) */
7259 *(optr++) = 8; ++iptr;
7260 break;
7261 case '\\': /* ASCII Backslash character (Decimal value 92) */
7262 *(optr++) = 92; ++iptr;
7263 break;
7264 case 'e': /* ASCII Escape character (Decimal value 27) */
7265 *(optr++) = 27; ++iptr;
7266 break;
7267 case '\'': /* ASCII Single Quote character (Decimal value 39) */
7268 *(optr++) = 39; ++iptr;
7269 break;
7270 case '"': /* ASCII Double Quote character (Decimal value 34) */
7271 *(optr++) = 34; ++iptr;
7272 break;
7273 case '?': /* ASCII Question Mark character (Decimal value 63) */
7274 *(optr++) = 63; ++iptr;
7275 break;
7276 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
7277 *optr = *(iptr++) - '0';
7278 if ((*iptr >= '0') && (*iptr <= '7'))
7279 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7280 if ((*iptr >= '0') && (*iptr <= '7'))
7281 *optr = ((*optr)<<3) + (*(iptr++) - '0');
7282 ++optr;
7283 break;
7284 case 'x':
7285 if (1) {
7286 static const char *hex_digits = "0123456789ABCDEF";
7287 const char *c;
7288
7289 ++iptr;
7290 *optr = 0;
7291 c = strchr (hex_digits, toupper(*iptr));
7292 if (c) {
7293 *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7294 ++iptr;
7295 }
7296 c = strchr (hex_digits, toupper(*iptr));
7297 if (c) {
7298 *optr = ((*optr)<<4) + (uint8)(c-hex_digits);
7299 ++iptr;
7300 }
7301 ++optr;
7302 }
7303 break;
7304 default:
7305 return SCPE_ARG; /* Invalid escape */
7306 }
7307 }
7308 *optr = '\0';
7309 *osize = (uint32)(optr-ostart);
7310 return SCPE_OK;
7311 }
7312
7313 /* sim_encode_quoted_string
7314
7315 Inputs:
7316 iptr = pointer to input buffer
7317 size = number of bytes of data in the buffer
7318
7319 Outputs
7320 optr = pointer to output buffer
7321 the output buffer must be freed by the caller
7322
7323 The input data will be encoded into a simply printable form.
7324 Control and other non-printable data will be escaped using the
7325 following rules:
7326
7327 The following character escapes are explicitly supported:
7328 \r ASCII Carriage Return character (Decimal value 13)
7329 \n ASCII Linefeed character (Decimal value 10)
7330 \f ASCII Formfeed character (Decimal value 12)
7331 \t ASCII Horizontal Tab character (Decimal value 9)
7332 \v ASCII Vertical Tab character (Decimal value 11)
7333 \b ASCII Backspace character (Decimal value 8)
7334 \\ ASCII Backslash character (Decimal value 92)
7335 \' ASCII Single Quote character (Decimal value 39)
7336 \" ASCII Double Quote character (Decimal value 34)
7337 \? ASCII Question Mark character (Decimal value 63)
7338 \e ASCII Escape character (Decimal value 27)
7339 as well as octal character values of the form:
7340 \n{n{n}} where each n is an octal digit (0-7)
7341 and hext character values of the form:
7342 \xh{h} where each h is a hex digit (0-9A-Fa-f)
7343 */
7344
7345 char *sim_encode_quoted_string (const uint8 *iptr, uint32 size)
7346 {
7347 uint32 i;
7348 t_bool double_quote_found = FALSE;
7349 t_bool single_quote_found = FALSE;
7350 char quote = '"';
7351 char *tptr, *optr;
7352
7353 optr = (char *)malloc (4*size + 3);
7354 if (optr == NULL)
7355 return NULL;
7356 tptr = optr;
7357 for (i=0; i<size; i++)
7358 switch ((char)iptr[i]) {
7359 case '"':
7360 double_quote_found = TRUE;
7361 break;
7362 case '\'':
7363 single_quote_found = TRUE;
7364 break;
7365 }
7366 if (double_quote_found && (!single_quote_found))
7367 quote = '\'';
7368 *tptr++ = quote;
7369 while (size--) {
7370 switch (*iptr) {
7371 case '\r':
7372 *tptr++ = '\\'; *tptr++ = 'r'; break;
7373 case '\n':
7374 *tptr++ = '\\'; *tptr++ = 'n'; break;
7375 case '\f':
7376 *tptr++ = '\\'; *tptr++ = 'f'; break;
7377 case '\t':
7378 *tptr++ = '\\'; *tptr++ = 't'; break;
7379 case '\v':
7380 *tptr++ = '\\'; *tptr++ = 'v'; break;
7381 case '\b':
7382 *tptr++ = '\\'; *tptr++ = 'b'; break;
7383 case '\\':
7384 *tptr++ = '\\'; *tptr++ = '\\'; break;
7385 case '"':
7386 case '\'':
7387 if (quote == *iptr)
7388 *tptr++ = '\\';
7389 default:
7390 if (sim_isprint (*iptr))
7391 *tptr++ = *iptr;
7392 else {
7393 sprintf (tptr, "\\%03o", *iptr);
7394 tptr += 4;
7395 }
7396 break;
7397 }
7398 ++iptr;
7399 }
7400 *tptr++ = quote;
7401 *tptr++ = '\0';
7402 return optr;
7403 }
7404
7405 void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size)
7406 {
7407 char *string;
7408
7409 string = sim_encode_quoted_string (buf, size);
7410 fprintf (st, "%s", string);
7411 free (string);
7412 }
7413
7414 /* Find_device find device matching input string
7415
7416 Inputs:
7417 cptr = pointer to input string
7418 Outputs:
7419 result = pointer to device
7420 */
7421
7422 DEVICE *find_dev (const char *cptr)
7423 {
7424 int32 i;
7425 DEVICE *dptr;
7426
7427 if (cptr == NULL)
7428 return NULL;
7429 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7430 if ((strcmp (cptr, dptr->name) == 0) ||
7431 (dptr->lname &&
7432 (strcmp (cptr, dptr->lname) == 0)))
7433 return dptr;
7434 }
7435 for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
7436 if ((strcmp (cptr, dptr->name) == 0) ||
7437 (dptr->lname &&
7438 (strcmp (cptr, dptr->lname) == 0)))
7439 return dptr;
7440 }
7441 return NULL;
7442 }
7443
7444 /* Find_unit find unit matching input string
7445
7446 Inputs:
7447 cptr = pointer to input string
7448 uptr = pointer to unit pointer
7449 Outputs:
7450 result = pointer to device (null if no dev)
7451 *iptr = pointer to unit (null if nx unit)
7452
7453 */
7454
7455 DEVICE *find_unit (const char *cptr, UNIT **uptr)
7456 {
7457 uint32 i, u;
7458 const char *nptr;
7459 const char *tptr;
7460 t_stat r;
7461 DEVICE *dptr;
7462
7463 if (uptr == NULL) /* arg error? */
7464 return NULL;
7465 *uptr = NULL;
7466 if ((dptr = find_dev (cptr))) { /* exact match? */
7467 if (qdisable (dptr)) /* disabled? */
7468 return NULL;
7469 *uptr = dptr->units; /* unit 0 */
7470 return dptr;
7471 }
7472
7473 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */
7474 if (qdisable (dptr)) /* device disabled? */
7475 continue;
7476 if (dptr->numunits && /* any units? */
7477 (((nptr = dptr->name) &&
7478 (strncmp (cptr, nptr, strlen (nptr)) == 0)) ||
7479 ((nptr = dptr->lname) &&
7480 (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {
7481 tptr = cptr + strlen (nptr);
7482 if (sim_isdigit (*tptr)) {
7483 if (qdisable (dptr)) /* disabled? */
7484 return NULL;
7485 u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);
7486 if (r != SCPE_OK) /* error? */
7487 *uptr = NULL;
7488 else
7489 *uptr = dptr->units + u;
7490 return dptr;
7491 }
7492 }
7493 for (u = 0; u < dptr->numunits; u++) {
7494 if (0 == strcmp (cptr, sim_uname (&dptr->units[u]))) {
7495 *uptr = &dptr->units[u];
7496 return dptr;
7497 }
7498 }
7499 }
7500 return NULL;
7501 }
7502
7503 /* sim_register_internal_device Add device to internal device list
7504
7505 Inputs:
7506 dptr = pointer to device
7507 */
7508
7509 t_stat sim_register_internal_device (DEVICE *dptr)
7510 {
7511 uint32 i;
7512
7513 for (i = 0; (sim_devices[i] != NULL); i++)
7514 if (sim_devices[i] == dptr)
7515 return SCPE_OK;
7516 for (i = 0; i < sim_internal_device_count; i++)
7517 if (sim_internal_devices[i] == dptr)
7518 return SCPE_OK;
7519 ++sim_internal_device_count;
7520 sim_internal_devices = (DEVICE **)realloc(sim_internal_devices, (sim_internal_device_count+1)*sizeof(*sim_internal_devices));
7521 sim_internal_devices[sim_internal_device_count-1] = dptr;
7522 sim_internal_devices[sim_internal_device_count] = NULL;
7523 return SCPE_OK;
7524 }
7525
7526 /* Find_dev_from_unit find device for unit
7527
7528 Inputs:
7529 uptr = pointer to unit
7530 Outputs:
7531 result = pointer to device
7532 */
7533
7534 DEVICE *find_dev_from_unit (UNIT *uptr)
7535 {
7536 DEVICE *dptr;
7537 uint32 i, j;
7538
7539 if (uptr == NULL)
7540 return NULL;
7541 for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
7542 for (j = 0; j < dptr->numunits; j++) {
7543 if (uptr == (dptr->units + j))
7544 return dptr;
7545 }
7546 }
7547 for (i = 0; i<sim_internal_device_count; i++) {
7548 dptr = sim_internal_devices[i];
7549 for (j = 0; j < dptr->numunits; j++) {
7550 if (uptr == (dptr->units + j))
7551 return dptr;
7552 }
7553 }
7554 return NULL;
7555 }
7556
7557 /* Test for disabled device */
7558
7559 t_bool qdisable (DEVICE *dptr)
7560 {
7561 return (dptr->flags & DEV_DIS? TRUE: FALSE);
7562 }
7563
7564 /* find_reg_glob find globally unique register
7565
7566 Inputs:
7567 cptr = pointer to input string
7568 optr = pointer to output pointer (can be null)
7569 gdptr = pointer to global device
7570 Outputs:
7571 result = pointer to register, NULL if error
7572 *optr = pointer to next character in input string
7573 *gdptr = pointer to device where found
7574 */
7575
7576 REG *find_reg_glob (CONST char *cptr, CONST char **optr, DEVICE **gdptr)
7577 {
7578 int32 i;
7579 DEVICE *dptr;
7580 REG *rptr, *srptr = NULL;
7581
7582 *gdptr = NULL;
7583 for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */
7584 if (dptr->flags & DEV_DIS) /* skip disabled */
7585 continue;
7586 if ((rptr = find_reg (cptr, optr, dptr))) { /* found? */
7587 if (srptr) /* ambig? err */
7588 return NULL;
7589 srptr = rptr; /* save reg */
7590 *gdptr = dptr; /* save unit */
7591 }
7592 }
7593 return srptr;
7594 }
7595
7596 /* find_reg find register matching input string
7597
7598 Inputs:
7599 cptr = pointer to input string
7600 optr = pointer to output pointer (can be null)
7601 dptr = pointer to device
7602 Outputs:
7603 result = pointer to register, NULL if error
7604 *optr = pointer to next character in input string
7605 */
7606
7607 REG *find_reg (CONST char *cptr, CONST char **optr, DEVICE *dptr)
7608 {
7609 CONST char *tptr;
7610 REG *rptr;
7611 size_t slnt;
7612
7613 if ((cptr == NULL) || (dptr == NULL) || (dptr->registers == NULL))
7614 return NULL;
7615 tptr = cptr;
7616 do {
7617 tptr++;
7618 } while (sim_isalnum (*tptr) || (*tptr == '*') || (*tptr == '_') || (*tptr == '.'));
7619 slnt = tptr - cptr;
7620 for (rptr = dptr->registers; rptr->name != NULL; rptr++) {
7621 if ((slnt == strlen (rptr->name)) &&
7622 (strncmp (cptr, rptr->name, slnt) == 0)) {
7623 if (optr != NULL)
7624 *optr = tptr;
7625 return rptr;
7626 }
7627 }
7628 return NULL;
7629 }
7630
7631 /* get_switches get switches from input string
7632
7633 Inputs:
7634 cptr = pointer to input string
7635 Outputs:
7636 sw = switch bit mask
7637 0 if no switches, -1 if error
7638 */
7639
7640 int32 get_switches (const char *cptr)
7641 {
7642 int32 sw;
7643
7644 if (*cptr != '-')
7645 return 0;
7646 sw = 0;
7647 for (cptr++; (sim_isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
7648 if (sim_isalpha (*cptr) == 0)
7649 return -1;
7650 sw = sw | SWMASK (toupper (*cptr));
7651 }
7652 return sw;
7653 }
7654
7655 /* get_sim_sw accumulate sim_switches
7656
7657 Inputs:
7658 cptr = pointer to input string
7659 Outputs:
7660 ptr = pointer to first non-string glyph
7661 NULL if error
7662 */
7663
7664 CONST char *get_sim_sw (CONST char *cptr)
7665 {
7666 int32 lsw;
7667 char gbuf[CBUFSIZE];
7668
7669 while (*cptr == '-') { /* while switches */
7670 cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */
7671 lsw = get_switches (gbuf); /* parse */
7672 if (lsw <= 0) /* invalid? */
7673 return NULL;
7674 sim_switches = sim_switches | lsw; /* accumulate */
7675 }
7676 return cptr;
7677 }
7678
7679 /* get_sim_opt get simulator command options
7680
7681 Inputs:
7682 opt = command options
7683 cptr = pointer to input string
7684 Outputs:
7685 ptr = pointer to next glypsh, NULL if error
7686 *stat = error status
7687 */
7688
7689 CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st)
7690 {
7691 int32 t;
7692 char gbuf[CBUFSIZE];
7693 CONST char *svptr;
7694 DEVICE *tdptr;
7695 UNIT *tuptr;
7696
7697 sim_switches = 0; /* no switches */
7698 sim_ofile = NULL; /* no output file */
7699 sim_schrptr = NULL; /* no search */
7700 sim_schaptr = NULL; /* no search */
7701 sim_stabr.logic = sim_staba.logic = SCH_OR; /* default search params */
7702 sim_stabr.boolop = sim_staba.boolop = SCH_GE;
7703 sim_stabr.count = 1;
7704 sim_stabr.mask = (t_value *)realloc (sim_stabr.mask, sim_emax * sizeof(*sim_stabr.mask));
7705 memset (sim_stabr.mask, 0, sim_emax * sizeof(*sim_stabr.mask));
7706 sim_stabr.comp = (t_value *)realloc (sim_stabr.comp, sim_emax * sizeof(*sim_stabr.comp));
7707 memset (sim_stabr.comp, 0, sim_emax * sizeof(*sim_stabr.comp));
7708 sim_staba.count = sim_emax;
7709 sim_staba.mask = (t_value *)realloc (sim_staba.mask, sim_emax * sizeof(*sim_staba.mask));
7710 memset (sim_staba.mask, 0, sim_emax * sizeof(*sim_staba.mask));
7711 sim_staba.comp = (t_value *)realloc (sim_staba.comp, sim_emax * sizeof(*sim_staba.comp));
7712 memset (sim_staba.comp, 0, sim_emax * sizeof(*sim_staba.comp));
7713 if (! sim_dflt_dev)
7714 return NULL;
7715 sim_dfdev = sim_dflt_dev;
7716 sim_dfunit = sim_dfdev->units;
7717 sim_opt_out = 0; /* no options yet */
7718 *st = SCPE_OK;
7719 while (*cptr) { /* loop through modifiers */
7720 svptr = cptr; /* save current position */
7721 if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */
7722 if (sim_ofile) { /* already got one? */
7723 fclose (sim_ofile); /* one per customer */
7724 *st = SCPE_ARG;
7725 return NULL;
7726 }
7727 cptr = get_glyph (cptr + 1, gbuf, 0);
7728 sim_ofile = sim_fopen (gbuf, "a"); /* open for append */
7729 if (sim_ofile == NULL) { /* open failed? */
7730 *st = SCPE_OPENERR;
7731 return NULL;
7732 }
7733 sim_opt_out |= CMD_OPT_OF; /* got output file */
7734 continue;
7735 }
7736 cptr = get_glyph (cptr, gbuf, 0);
7737 if ((t = get_switches (gbuf)) != 0) { /* try for switches */
7738 if (t < 0) { /* err if bad switch */
7739 *st = SCPE_INVSW;
7740 return NULL;
7741 }
7742 sim_switches = sim_switches | t; /* or in new switches */
7743 }
7744 else if ((opt & CMD_OPT_SCH) && /* if allowed, */
7745 get_rsearch (gbuf, sim_dfdev->dradix, &sim_stabr)) { /* try for search */
7746 sim_schrptr = &sim_stabr; /* set search */
7747 sim_schaptr = get_asearch (gbuf, sim_dfdev->dradix, &sim_staba);/* populate memory version of the same expression */
7748 sim_opt_out |= CMD_OPT_SCH; /* got search */
7749 }
7750 else if ((opt & CMD_OPT_DFT) && /* default allowed? */
7751 ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */
7752 (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */
7753 (tuptr != NULL)) {
7754 sim_dfdev = tdptr; /* set as default */
7755 sim_dfunit = tuptr;
7756 sim_opt_out |= CMD_OPT_DFT; /* got default */
7757 }
7758 else return svptr; /* not rec, break out */
7759 }
7760 return cptr;
7761 }
7762
7763 /* put_switches put switches into string
7764
7765 Inputs:
7766 buf = pointer to string buffer
7767 bufsize = size of string buffer
7768 sw = switch bit mask
7769 Outputs:
7770 buf = buffer with switches converted to text
7771 */
7772
7773 const char *put_switches (char *buf, size_t bufsize, uint32 sw)
7774 {
7775 char *optr = buf;
7776 int32 bit;
7777
7778 memset (buf, 0, bufsize);
7779 if ((sw == 0) || (bufsize < 3))
7780 return buf;
7781 --bufsize; /* leave room for terminating NUL */
7782 *optr++ = '-';
7783 for (bit=0; bit <= ('Z'-'A'); bit++)
7784 if (sw & (1 << bit))
7785 if ((size_t)(optr - buf) < bufsize)
7786 *optr++ = 'A' + bit;
7787 return buf;
7788 }
7789
7790 /* Get register search specification
7791
7792 Inputs:
7793 cptr = pointer to input string
7794 radix = radix for numbers
7795 schptr = pointer to search table
7796 Outputs:
7797 return = NULL if error
7798 schptr if valid search specification
7799 */
7800
7801 SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
7802 {
7803 int32 c, logop, cmpop;
7804 t_value logval, cmpval;
7805 const char *sptr;
7806 CONST char *tptr;
7807 const char logstr[] = "|&^", cmpstr[] = "=!><";
7808
7809 logval = cmpval = 0;
7810 if (*cptr == 0) /* check for clause */
7811 return NULL;
7812 for (logop = cmpop = -1; (c = *cptr++); ) { /* loop thru clauses */
7813 if ((sptr = strchr (logstr, c))) { /* check for mask */
7814 logop = (int32)(sptr - logstr);
7815 logval = strtotv (cptr, &tptr, radix);
7816 if (cptr == tptr)
7817 return NULL;
7818 cptr = tptr;
7819 }
7820 else if ((sptr = strchr (cmpstr, c))) { /* check for boolop */
7821 cmpop = (int32)(sptr - cmpstr);
7822 if (*cptr == '=') {
7823 cmpop = cmpop + strlen (cmpstr);
7824 cptr++;
7825 }
7826 cmpval = strtotv (cptr, &tptr, radix);
7827 if (cptr == tptr)
7828 return NULL;
7829 cptr = tptr;
7830 }
7831 else return NULL;
7832 } /* end for */
7833 if (schptr->count != 1) {
7834 free (schptr->mask);
7835 schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7836 free (schptr->comp);
7837 schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7838 }
7839 if (logop >= 0) {
7840 schptr->logic = logop;
7841 schptr->mask[0] = logval;
7842 }
7843 if (cmpop >= 0) {
7844 schptr->boolop = cmpop;
7845 schptr->comp[0] = cmpval;
7846 }
7847 schptr->count = 1;
7848 return schptr;
7849 }
7850
7851 /* Get memory search specification
7852
7853 Inputs:
7854 cptr = pointer to input string
7855 radix = radix for numbers
7856 schptr = pointer to search table
7857 Outputs:
7858 return = NULL if error
7859 schptr if valid search specification
7860 */
7861
7862 SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr)
7863 {
7864 int32 c, logop, cmpop;
7865 t_value *logval, *cmpval;
7866 t_stat reason = SCPE_OK;
7867 CONST char *ocptr = cptr;
7868 const char *sptr;
7869 char gbuf[CBUFSIZE];
7870 const char logstr[] = "|&^", cmpstr[] = "=!><";
7871
7872 if (*cptr == 0) /* check for clause */
7873 return NULL;
7874 logval = (t_value *)calloc (sim_emax, sizeof(*logval));
7875 cmpval = (t_value *)calloc (sim_emax, sizeof(*cmpval));
7876 for (logop = cmpop = -1; (c = *cptr++); ) { /* loop thru clauses */
7877 if ((sptr = strchr (logstr, c))) { /* check for mask */
7878 logop = (int32)(sptr - logstr);
7879 cptr = get_glyph (cptr, gbuf, 0);
7880 reason = parse_sym (gbuf, 0, sim_dfunit, logval, sim_switches);
7881 if (reason > 0) {
7882 free (logval);
7883 free (cmpval);
7884 return get_rsearch (ocptr, radix, schptr);
7885 }
7886 }
7887 else if ((sptr = strchr (cmpstr, c))) { /* check for boolop */
7888 cmpop = (int32)(sptr - cmpstr);
7889 if (*cptr == '=') {
7890 cmpop = cmpop + strlen (cmpstr);
7891 cptr++;
7892 }
7893 cptr = get_glyph (cptr, gbuf, 0);
7894 reason = parse_sym (gbuf, 0, sim_dfunit, cmpval, sim_switches);
7895 if (reason > 0) {
7896 free (logval);
7897 free (cmpval);
7898 return get_rsearch (ocptr, radix, schptr);
7899 }
7900 }
7901 else {
7902 free (logval);
7903 free (cmpval);
7904 return NULL;
7905 }
7906 } /* end for */
7907 if (schptr->count != (uint32)(1 - reason)) {
7908 schptr->count = 1 - reason;
7909 free (schptr->mask);
7910 schptr->mask = (t_value *)calloc (sim_emax, sizeof(*schptr->mask));
7911 free (schptr->comp);
7912 schptr->comp = (t_value *)calloc (sim_emax, sizeof(*schptr->comp));
7913 }
7914 if (logop >= 0) {
7915 schptr->logic = logop;
7916 free (schptr->mask);
7917 schptr->mask = logval;
7918 }
7919 else {
7920 free (logval);
7921 }
7922 if (cmpop >= 0) {
7923 schptr->boolop = cmpop;
7924 free (schptr->comp);
7925 schptr->comp = cmpval;
7926 }
7927 else {
7928 free (cmpval);
7929 }
7930 return schptr;
7931 }
7932
7933 /* Test value against search specification
7934
7935 Inputs:
7936 val = value list to test
7937 schptr = pointer to search table
7938 Outputs:
7939 return = 1 if value passes search criteria, 0 if not
7940 */
7941
7942 int32 test_search (t_value *values, SCHTAB *schptr)
7943 {
7944 t_value *val = NULL;
7945 int32 i, updown;
7946 int32 ret = 0;
7947
7948 if (schptr == NULL)
7949 return ret;
7950
7951 val = (t_value *)malloc (schptr->count * sizeof (*values));
7952
7953 for (i=0; i<(int32)schptr->count; i++) {
7954 val[i] = values[i];
7955 switch (schptr->logic) { /* case on logical */
7956
7957 case SCH_OR:
7958 val[i] = val[i] | schptr->mask[i];
7959 break;
7960
7961 case SCH_AND:
7962 val[i] = val[i] & schptr->mask[i];
7963 break;
7964
7965 case SCH_XOR:
7966 val[i] = val[i] ^ schptr->mask[i];
7967 break;
7968 }
7969 }
7970
7971 ret = 1;
7972 if (1) { /* Little Endian VM */
7973 updown = -1;
7974 i=schptr->count-1;
7975 }
7976 else { /* Big Endian VM */
7977 updown = 1;
7978 i=0;
7979 }
7980 for (; (i>=0) && (i<(int32)schptr->count) && ret; i += updown) {
7981 switch (schptr->boolop) { /* case on comparison */
7982
7983 case SCH_E: case SCH_EE:
7984 if (val[i] != schptr->comp[i])
7985 ret = 0;
7986 break;
7987
7988 case SCH_N: case SCH_NE:
7989 if (val[i] == schptr->comp[i])
7990 ret = 0;
7991 break;
7992
7993 case SCH_G:
7994 if (val[i] <= schptr->comp[i])
7995 ret = 0;
7996 break;
7997
7998 case SCH_GE:
7999 if (val[i] < schptr->comp[i])
8000 ret = 0;
8001 break;
8002
8003 case SCH_L:
8004 if (val[i] >= schptr->comp[i])
8005 ret = 0;
8006 break;
8007
8008 case SCH_LE:
8009 if (val[i] > schptr->comp[i])
8010 ret = 0;
8011 break;
8012 }
8013 }
8014 free (val);
8015 return ret;
8016 }
8017
8018 /* Radix independent input/output package
8019
8020 strtotv - general radix input routine
8021
8022 Inputs:
8023 inptr = string to convert
8024 endptr = pointer to first unconverted character
8025 radix = radix for input
8026 Outputs:
8027 value = converted value
8028
8029 On an error, the endptr will equal the inptr.
8030 */
8031
8032 t_value strtotv (CONST char *inptr, CONST char **endptr, uint32 radix)
8033 {
8034 int32 nodigit;
8035 t_value val;
8036 uint32 c, digit;
8037
8038 *endptr = inptr; /* assume fails */
8039 if ((radix < 2) || (radix > 36))
8040 return 0;
8041 while (sim_isspace (*inptr)) /* bypass white space */
8042 inptr++;
8043 val = 0;
8044 nodigit = 1;
8045 for (c = *inptr; sim_isalnum(c); c = *++inptr) { /* loop through char */
8046 if (sim_islower (c))
8047 c = toupper (c);
8048 if (sim_isdigit (c)) /* digit? */
8049 digit = c - (uint32) '0';
8050 else if (radix <= 10) /* stop if not expected */
8051 break;
8052 else digit = c + 10 - (uint32) 'A'; /* convert letter */
8053 if (digit >= radix) /* valid in radix? */
8054 return 0;
8055 val = (val * radix) + digit; /* add to value */
8056 nodigit = 0;
8057 }
8058 if (nodigit) /* no digits? */
8059 return 0;
8060 *endptr = inptr; /* result pointer */
8061 return val;
8062 }
8063
8064 /* fprint_val - general radix printing routine
8065
8066 Inputs:
8067 stream = stream designator
8068 val = value to print
8069 radix = radix to print
8070 width = width to print
8071 format = leading zeroes format
8072 Outputs:
8073 status = error status
8074 if stream is NULL, returns length of output that would
8075 have been generated.
8076 */
8077
8078 t_stat sprint_val (char *buffer, t_value val, uint32 radix,
8079 uint32 width, uint32 format)
8080 {
8081 #define MAX_WIDTH ((int) ((CHAR_BIT * sizeof (t_value) * 4 + 3)/3))
8082 t_value owtest, wtest;
8083 int32 d, digit, ndigits, commas = 0;
8084 char dbuf[MAX_WIDTH + 1];
8085
8086 for (d = 0; d < MAX_WIDTH; d++)
8087 dbuf[d] = (format == PV_RZRO)? '0': ' ';
8088 dbuf[MAX_WIDTH] = 0;
8089 d = MAX_WIDTH;
8090 do {
8091 d = d - 1;
8092 digit = (int32) (val % radix);
8093 val = val / radix;
8094 dbuf[d] = (char)((digit <= 9)? '0' + digit: 'A' + (digit - 10));
8095 } while ((d > 0) && (val != 0));
8096
8097 switch (format) {
8098 case PV_LEFT:
8099 break;
8100 case PV_RCOMMA:
8101 for (digit = 0; digit < MAX_WIDTH; digit++)
8102 if (dbuf[digit] != ' ')
8103 break;
8104 ndigits = MAX_WIDTH - digit;
8105 commas = (ndigits - 1)/3;
8106 for (digit=0; digit<ndigits-3; digit++)
8107 dbuf[MAX_WIDTH + (digit - ndigits) - (ndigits - digit - 1)/3] = dbuf[MAX_WIDTH + (digit - ndigits)];
8108 for (digit=1; digit<=commas; digit++)
8109 dbuf[MAX_WIDTH - (digit * 4)] = ',';
8110 d = d - commas;
8111 if (width > MAX_WIDTH) {
8112 if (!buffer)
8113 return width;
8114 sprintf (buffer, "%*s", -((int)width), dbuf);
8115 return SCPE_OK;
8116 }
8117 else
8118 if (width > 0)
8119 d = MAX_WIDTH - width;
8120 break;
8121 case PV_RZRO:
8122 case PV_RSPC:
8123 wtest = owtest = radix;
8124 ndigits = 1;
8125 while ((wtest < width_mask[width]) && (wtest >= owtest)) {
8126 owtest = wtest;
8127 wtest = wtest * radix;
8128 ndigits = ndigits + 1;
8129 }
8130 if ((MAX_WIDTH - (ndigits + commas)) < d)
8131 d = MAX_WIDTH - (ndigits + commas);
8132 break;
8133 }
8134 if (!buffer)
8135 return strlen(dbuf+d);
8136 *buffer = '\0';
8137 if (width < strlen(dbuf+d))
8138 return SCPE_IOERR;
8139 strcpy(buffer, dbuf+d);
8140 return SCPE_OK;
8141 }
8142
8143 t_stat fprint_val (FILE *stream, t_value val, uint32 radix,
8144 uint32 width, uint32 format)
8145 {
8146 char dbuf[MAX_WIDTH + 1];
8147
8148 if (!stream)
8149 return sprint_val (NULL, val, radix, width, format);
8150 if (width > MAX_WIDTH)
8151 width = MAX_WIDTH;
8152 sprint_val (dbuf, val, radix, width, format);
8153 if (Fprintf (stream, "%s", dbuf) < 0)
8154 return SCPE_IOERR;
8155 return SCPE_OK;
8156 }
8157
8158 const char *sim_fmt_secs (double seconds)
8159 {
8160 static char buf[60];
8161 char frac[16] = "";
8162 const char *sign = "";
8163 double val = seconds;
8164 double days, hours, mins, secs, msecs, usecs;
8165
8166 if (val == 0.0)
8167 return "";
8168 if (val < 0.0) {
8169 sign = "-";
8170 val = -val;
8171 }
8172 days = floor (val / (24.0*60.0*60.0));
8173 val -= (days * 24.0*60.0*60.0);
8174 hours = floor (val / (60.0*60.0));
8175 val -= (hours * 60.0 * 60.0);
8176 mins = floor (val / 60.0);
8177 val -= (mins * 60.0);
8178 secs = floor (val);
8179 val -= secs;
8180 val *= 1000.0;
8181 msecs = floor (val);
8182 val -= msecs;
8183 val *= 1000.0;
8184 usecs = floor (val+0.5);
8185 if (usecs == 1000.0) {
8186 usecs = 0.0;
8187 msecs += 1;
8188 }
8189 if ((msecs > 0.0) || (usecs > 0.0)) {
8190 sprintf (frac, ".%03.0f%03.0f", msecs, usecs);
8191 while (frac[strlen (frac) - 1] == '0')
8192 frac[strlen (frac) - 1] = '\0';
8193 if (strlen (frac) == 1)
8194 frac[0] = '\0';
8195 }
8196 if (days > 0)
8197 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" : "");
8198 else
8199 if (hours > 0)
8200 sprintf (buf, "%s%.0f:%02.0f:%02.0f%s hour", sign, hours, mins, secs, frac);
8201 else
8202 if (mins > 0)
8203 sprintf (buf, "%s%.0f:%02.0f%s minute", sign, mins, secs, frac);
8204 else
8205 if (secs > 0)
8206 sprintf (buf, "%s%.0f%s second", sign, secs, frac);
8207 else
8208 if (msecs > 0) {
8209 if (usecs > 0)
8210 sprintf (buf, "%s%.0f.%s msec", sign, msecs, frac+4);
8211 else
8212 sprintf (buf, "%s%.0f msec", sign, msecs);
8213 }
8214 else
8215 sprintf (buf, "%s%.0f usec", sign, usecs);
8216 if (0 != strncmp ("1 ", buf, 2))
8217 strcpy (&buf[strlen (buf)], "s");
8218 return buf;
8219 }
8220
8221 /* Event queue package
8222
8223 sim_activate add entry to event queue
8224 sim_activate_abs add entry to event queue even if event already scheduled
8225 sim_activate_after add entry to event queue after a specified amount of wall time
8226 sim_cancel remove entry from event queue
8227 sim_process_event process entries on event queue
8228 sim_is_active see if entry is on event queue
8229 sim_activate_time return time until activation
8230 sim_atime return absolute time for an entry
8231 sim_gtime return global time
8232 sim_qcount return event queue entry count
8233
8234 Asynchronous events are set up by queueing a unit data structure
8235 to the event queue with a timeout (in simulator units, relative
8236 to the current time). Each simulator 'times' these events by
8237 counting down interval counter sim_interval. When this reaches
8238 zero the simulator calls sim_process_event to process the event
8239 and to see if further events need to be processed, or sim_interval
8240 reset to count the next one.
8241
8242 The event queue is maintained in clock order; entry timeouts are
8243 RELATIVE to the time in the previous entry.
8244
8245 sim_process_event - process event
8246
8247 Inputs:
8248 none
8249 Outputs:
8250 reason = reason code returned by any event processor,
8251 or 0 (SCPE_OK) if no exceptions
8252 */
8253
8254 t_stat sim_process_event (void)
8255 {
8256 UNIT *uptr;
8257 t_stat reason;
8258
8259 if (stop_cpu) /* stop CPU? */
8260 return SCPE_STOP;
8261 UPDATE_SIM_TIME; /* update sim time */
8262
8263 if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
8264 sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
8265 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Empty New Interval = %d\n", sim_interval);
8266 return SCPE_OK;
8267 }
8268 sim_processing_event = TRUE;
8269 do {
8270 uptr = sim_clock_queue; /* get first */
8271 sim_clock_queue = uptr->next; /* remove first */
8272 uptr->next = NULL; /* hygiene */
8273 sim_interval -= uptr->time;
8274 uptr->time = 0;
8275 if (sim_clock_queue != QUEUE_LIST_END)
8276 sim_interval = sim_clock_queue->time;
8277 else
8278 sim_interval = noqueue_time = NOQUEUE_WAIT;
8279 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Event for %s\n", sim_uname (uptr));
8280 if (uptr->action != NULL)
8281 reason = uptr->action (uptr);
8282 else
8283 reason = SCPE_OK;
8284 } while ((reason == SCPE_OK) &&
8285 (sim_interval <= 0) &&
8286 (sim_clock_queue != QUEUE_LIST_END) &&
8287 (!stop_cpu));
8288
8289 if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
8290 sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
8291 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d\n", sim_interval);
8292 }
8293 else
8294 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue));
8295
8296 if ((reason == SCPE_OK) && stop_cpu)
8297 reason = SCPE_STOP;
8298 sim_processing_event = FALSE;
8299 return reason;
8300 }
8301
8302 /* sim_activate - activate (queue) event
8303
8304 Inputs:
8305 uptr = pointer to unit
8306 event_time = relative timeout
8307 Outputs:
8308 reason = result (SCPE_OK if ok)
8309 */
8310
8311 t_stat sim_activate (UNIT *uptr, int32 event_time)
8312 {
8313 if (uptr->dynflags & UNIT_TMR_UNIT)
8314 return sim_timer_activate (uptr, event_time);
8315 return _sim_activate (uptr, event_time);
8316 }
8317
8318 t_stat _sim_activate (UNIT *uptr, int32 event_time)
8319 {
8320 UNIT *cptr, *prvptr;
8321 int32 accum;
8322
8323 if (sim_is_active (uptr)) /* already active? */
8324 return SCPE_OK;
8325 UPDATE_SIM_TIME; /* update sim time */
8326
8327 sim_debug (SIM_DBG_ACTIVATE, sim_dflt_dev, "Activating %s delay=%d\n", sim_uname (uptr), event_time);
8328
8329 prvptr = NULL;
8330 accum = 0;
8331 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8332 if (event_time < (accum + cptr->time))
8333 break;
8334 accum = accum + cptr->time;
8335 prvptr = cptr;
8336 }
8337 if (prvptr == NULL) { /* insert at head */
8338 cptr = uptr->next = sim_clock_queue;
8339 sim_clock_queue = uptr;
8340 }
8341 else {
8342 cptr = uptr->next = prvptr->next; /* insert at prvptr */
8343 prvptr->next = uptr;
8344 }
8345 uptr->time = event_time - accum;
8346 if (cptr != QUEUE_LIST_END)
8347 cptr->time = cptr->time - uptr->time;
8348 sim_interval = sim_clock_queue->time;
8349 return SCPE_OK;
8350 }
8351
8352 /* sim_activate_abs - activate (queue) event even if event already scheduled
8353
8354 Inputs:
8355 uptr = pointer to unit
8356 event_time = relative timeout
8357 Outputs:
8358 reason = result (SCPE_OK if ok)
8359 */
8360
8361 t_stat sim_activate_abs (UNIT *uptr, int32 event_time)
8362 {
8363 sim_cancel (uptr);
8364 return _sim_activate (uptr, event_time);
8365 }
8366
8367 /* sim_activate_after - activate (queue) event
8368
8369 Inputs:
8370 uptr = pointer to unit
8371 usec_delay = relative timeout (in microseconds)
8372 Outputs:
8373 reason = result (SCPE_OK if ok)
8374 */
8375
8376 t_stat sim_activate_after (UNIT *uptr, uint32 usec_delay)
8377 {
8378 return _sim_activate_after (uptr, usec_delay);
8379 }
8380
8381 t_stat _sim_activate_after (UNIT *uptr, uint32 usec_delay)
8382 {
8383 if (sim_is_active (uptr)) /* already active? */
8384 return SCPE_OK;
8385 return sim_timer_activate_after (uptr, usec_delay);
8386 }
8387
8388 /* sim_cancel - cancel (dequeue) event
8389
8390 Inputs:
8391 uptr = pointer to unit
8392 Outputs:
8393 reason = result (SCPE_OK if ok)
8394
8395 */
8396
8397 t_stat sim_cancel (UNIT *uptr)
8398 {
8399 UNIT *cptr, *nptr;
8400
8401 if (sim_clock_queue == QUEUE_LIST_END)
8402 return SCPE_OK;
8403 if (!sim_is_active (uptr))
8404 return SCPE_OK;
8405 UPDATE_SIM_TIME; /* update sim time */
8406 sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr));
8407 nptr = QUEUE_LIST_END;
8408
8409 if (sim_clock_queue == uptr) {
8410 nptr = sim_clock_queue = uptr->next;
8411 uptr->next = NULL; /* hygiene */
8412 }
8413 else {
8414 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8415 if (cptr->next == uptr) {
8416 nptr = cptr->next = uptr->next;
8417 uptr->next = NULL; /* hygiene */
8418 break; /* end queue scan */
8419 }
8420 }
8421 }
8422 if (nptr != QUEUE_LIST_END)
8423 nptr->time += (uptr->next) ? 0 : uptr->time;
8424 if (!uptr->next)
8425 uptr->time = 0;
8426 if (sim_clock_queue != QUEUE_LIST_END)
8427 sim_interval = sim_clock_queue->time;
8428 else sim_interval = noqueue_time = NOQUEUE_WAIT;
8429 if (uptr->next) {
8430 sim_printf ("Cancel failed for %s\n", sim_uname(uptr));
8431 if (sim_deb)
8432 fclose(sim_deb);
8433 abort ();
8434 }
8435 return SCPE_OK;
8436 }
8437
8438 /* sim_is_active - test for entry in queue
8439
8440 Inputs:
8441 uptr = pointer to unit
8442 Outputs:
8443 result = TRUE if unit is busy, FALSE inactive
8444 */
8445
8446 t_bool sim_is_active (UNIT *uptr)
8447 {
8448 if (uptr->next == NULL)
8449 return FALSE;
8450 else
8451 return TRUE;
8452 }
8453
8454 /* sim_activate_time - return activation time
8455
8456 Inputs:
8457 uptr = pointer to unit
8458 Outputs:
8459 result = absolute activation time + 1, 0 if inactive
8460 */
8461
8462 int32 sim_activate_time (UNIT *uptr)
8463 {
8464 UNIT *cptr;
8465 int32 accum = 0;
8466
8467 for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
8468 if (cptr == sim_clock_queue) {
8469 if (sim_interval > 0)
8470 accum = accum + sim_interval;
8471 }
8472 else
8473 accum = accum + cptr->time;
8474 if (cptr == uptr)
8475 return accum + 1;
8476 }
8477 return 0;
8478 }
8479
8480 /* sim_gtime - return global time
8481
8482 Inputs: none
8483 Outputs:
8484 time = global time
8485 */
8486
8487 double sim_gtime (void)
8488 {
8489 return sim_time;
8490 }
8491
8492 /* sim_qcount - return queue entry count
8493
8494 Inputs: none
8495 Outputs:
8496 count = number of entries on the queue
8497 */
8498
8499 int32 sim_qcount (void)
8500 {
8501 int32 cnt;
8502 UNIT *uptr;
8503
8504 cnt = 0;
8505 for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
8506 cnt++;
8507 return cnt;
8508 }
8509
8510 /* Breakpoint package. This module replaces the VM-implemented one
8511 instruction breakpoint capability.
8512
8513 Breakpoints are stored in table sim_brk_tab, which is ordered by address for
8514 efficient binary searching. A breakpoint consists of a six entry structure:
8515
8516 addr address of the breakpoint
8517 type types of breakpoints set on the address
8518 a bit mask representing letters A-Z
8519 cnt number of iterations before breakp is taken
8520 action pointer command string to be executed
8521 when break is taken
8522 next list of other breakpoints with the same addr specifier
8523 time_fired array of when this breakpoint was fired for each class
8524
8525 sim_brk_summ is a summary of the types of breakpoints that are currently set (it
8526 is the bitwise OR of all the type fields). A simulator need only check for
8527 a breakpoint of type X if bit SWMASK('X') is set in sim_brk_summ.
8528
8529 The package contains the following public routines:
8530
8531 sim_brk_init initialize
8532 sim_brk_set set breakpoint
8533 sim_brk_clr clear breakpoint
8534 sim_brk_clrall clear all breakpoints
8535 sim_brk_show show breakpoint
8536 sim_brk_showall show all breakpoints
8537 sim_brk_test test for breakpoint
8538 sim_brk_npc PC has been changed
8539 sim_brk_getact get next action
8540 sim_brk_clract clear pending actions
8541
8542 Initialize breakpoint system.
8543 */
8544
8545 t_stat sim_brk_init (void)
8546 {
8547 int32 i;
8548
8549 for (i=0; i<sim_brk_lnt; i++) {
8550 BRKTAB *bp = sim_brk_tab[i];
8551
8552 while (bp) {
8553 BRKTAB *bpt = bp->next;
8554
8555 free (bp->act);
8556 free (bp);
8557 bp = bpt;
8558 }
8559 }
8560 if (sim_brk_tab != NULL)
8561 memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8562 sim_brk_lnt = SIM_BRK_INILNT;
8563 sim_brk_tab = (BRKTAB **) realloc (sim_brk_tab, sim_brk_lnt*sizeof (BRKTAB*));
8564 if (sim_brk_tab == NULL)
8565 return SCPE_MEM;
8566 memset (sim_brk_tab, 0, sim_brk_lnt*sizeof (BRKTAB*));
8567 sim_brk_ent = sim_brk_ins = 0;
8568 sim_brk_clract ();
8569 sim_brk_npc (0);
8570 return SCPE_OK;
8571 }
8572
8573 /* Search for a breakpoint in the sorted breakpoint table */
8574
8575 BRKTAB *sim_brk_fnd (t_addr loc)
8576 {
8577 int32 lo, hi, p;
8578 BRKTAB *bp;
8579
8580 if (sim_brk_ent == 0) { /* table empty? */
8581 sim_brk_ins = 0; /* insrt at head */
8582 return NULL; /* sch fails */
8583 }
8584 lo = 0; /* initial bounds */
8585 hi = sim_brk_ent - 1;
8586 do {
8587 p = (lo + hi) >> 1; /* probe */
8588 bp = sim_brk_tab[p]; /* table addr */
8589 if (loc == bp->addr) { /* match? */
8590 sim_brk_ins = p;
8591 return bp;
8592 }
8593 else if (loc < bp->addr) /* go down? p is upper */
8594 hi = p - 1;
8595 else lo = p + 1; /* go up? p is lower */
8596 } while (lo <= hi);
8597 if (loc < bp->addr) /* insrt before or */
8598 sim_brk_ins = p;
8599 else sim_brk_ins = p + 1; /* after last sch */
8600 return NULL;
8601 }
8602
8603 BRKTAB *sim_brk_fnd_ex (t_addr loc, uint32 btyp, t_bool any_typ, uint32 spc)
8604 {
8605 BRKTAB *bp = sim_brk_fnd (loc);
8606
8607 while (bp) {
8608 if (any_typ ? ((bp->typ & btyp) && (bp->time_fired[spc] != sim_gtime())) :
8609 (bp->typ == btyp))
8610 return bp;
8611 bp = bp->next;
8612 }
8613 return bp;
8614 }
8615
8616 /* Insert a breakpoint */
8617
8618 BRKTAB *sim_brk_new (t_addr loc, uint32 btyp)
8619 {
8620 int32 i, t;
8621 BRKTAB *bp, **newp;
8622
8623 if (sim_brk_ins < 0)
8624 return NULL;
8625 if (sim_brk_ent >= sim_brk_lnt) { /* out of space? */
8626 t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */
8627 newp = (BRKTAB **) calloc (t, sizeof (BRKTAB*)); /* new table */
8628 if (newp == NULL) /* can't extend */
8629 return NULL;
8630 memcpy (newp, sim_brk_tab, sim_brk_lnt * sizeof (*sim_brk_tab));/* copy table */
8631 memset (newp + sim_brk_lnt, 0, SIM_BRK_INILNT * sizeof (*newp));/* zero new entries */
8632 free (sim_brk_tab); /* free old table */
8633 sim_brk_tab = newp; /* new base, lnt */
8634 sim_brk_lnt = t;
8635 }
8636 if ((sim_brk_ins == sim_brk_ent) ||
8637 ((sim_brk_ins != sim_brk_ent) &&
8638 (sim_brk_tab[sim_brk_ins]->addr != loc))) { /* need to open a hole? */
8639 for (i = sim_brk_ent; i > sim_brk_ins; --i)
8640 sim_brk_tab[i] = sim_brk_tab[i - 1];
8641 sim_brk_tab[sim_brk_ins] = NULL;
8642 }
8643 bp = (BRKTAB *)calloc (1, sizeof (*bp));
8644 bp->next = sim_brk_tab[sim_brk_ins];
8645 sim_brk_tab[sim_brk_ins] = bp;
8646 if (bp->next == NULL)
8647 sim_brk_ent += 1;
8648 bp->addr = loc;
8649 bp->typ = btyp;
8650 bp->cnt = 0;
8651 bp->act = NULL;
8652 for (i = 0; i < SIM_BKPT_N_SPC; i++)
8653 bp->time_fired[i] = -1.0;
8654 return bp;
8655 }
8656
8657 /* Set a breakpoint of type sw */
8658
8659 t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act)
8660 {
8661 BRKTAB *bp;
8662
8663 if ((sw == 0) || (sw == BRK_TYP_DYN_STEPOVER))
8664 sw |= sim_brk_dflt;
8665 if (~sim_brk_types & sw) {
8666 char gbuf[CBUFSIZE];
8667
8668 return sim_messagef (SCPE_NOFNC, "Unknown breakpoint type; %s\n", put_switches(gbuf, sizeof(gbuf), sw & ~sim_brk_types));
8669 }
8670 if ((sw & BRK_TYP_DYN_ALL) && act) /* can't specify an action with a dynamic breakpoint */
8671 return SCPE_ARG;
8672 bp = sim_brk_fnd (loc); /* loc present? */
8673 if (!bp) /* no, allocate */
8674 bp = sim_brk_new (loc, sw);
8675 else {
8676 while (bp && (bp->typ != (uint32)sw))
8677 bp = bp->next;
8678 if (!bp)
8679 bp = sim_brk_new (loc, sw);
8680 }
8681 if (!bp) /* still no? mem err */
8682 return SCPE_MEM;
8683 bp->cnt = ncnt; /* set count */
8684 if ((!(sw & BRK_TYP_DYN_ALL)) && /* Not Dynamic and */
8685 (bp->act != NULL) && (act != NULL)) { /* replace old action? */
8686 free (bp->act); /* deallocate */
8687 bp->act = NULL; /* now no action */
8688 }
8689 if ((act != NULL) && (*act != 0)) { /* new action? */
8690 char *newp = (char *) calloc (CBUFSIZE+1, sizeof (char)); /* alloc buf */
8691 if (newp == NULL) /* mem err? */
8692 return SCPE_MEM;
8693 strncpy (newp, act, CBUFSIZE); /* copy action */
8694 bp->act = newp; /* set pointer */
8695 }
8696 sim_brk_summ = sim_brk_summ | (sw & ~BRK_TYP_TEMP);
8697 return SCPE_OK;
8698 }
8699
8700 /* Clear a breakpoint */
8701
8702 t_stat sim_brk_clr (t_addr loc, int32 sw)
8703 {
8704 BRKTAB *bpl = NULL;
8705 BRKTAB *bp = sim_brk_fnd (loc);
8706 int32 i;
8707
8708 if (!bp) /* not there? ok */
8709 return SCPE_OK;
8710 if (sw == 0)
8711 sw = SIM_BRK_ALLTYP;
8712
8713 while (bp) {
8714 if (bp->typ == (bp->typ & sw)) {
8715 free (bp->act); /* deallocate action */
8716 if (bp == sim_brk_tab[sim_brk_ins])
8717 bpl = sim_brk_tab[sim_brk_ins] = bp->next;
8718 else
8719 bpl->next = bp->next;
8720 free (bp);
8721 bp = bpl;
8722 }
8723 else {
8724 bpl = bp;
8725 bp = bp->next;
8726 }
8727 }
8728 if (sim_brk_tab[sim_brk_ins] == NULL) { /* erased entry */
8729 sim_brk_ent = sim_brk_ent - 1; /* decrement count */
8730 for (i = sim_brk_ins; i < sim_brk_ent; i++) /* shuffle remaining entries */
8731 sim_brk_tab[i] = sim_brk_tab[i+1];
8732 }
8733 sim_brk_summ = 0; /* recalc summary */
8734 for (i = 0; i < sim_brk_ent; i++) {
8735 bp = sim_brk_tab[i];
8736 while (bp) {
8737 sim_brk_summ |= (bp->typ & ~BRK_TYP_TEMP);
8738 bp = bp->next;
8739 }
8740 }
8741 return SCPE_OK;
8742 }
8743
8744 /* Clear all breakpoints */
8745
8746 t_stat sim_brk_clrall (int32 sw)
8747 {
8748 int32 i;
8749
8750 if (sw == 0)
8751 sw = SIM_BRK_ALLTYP;
8752 for (i = 0; i < sim_brk_ent;) {
8753 t_addr loc = sim_brk_tab[i]->addr;
8754 sim_brk_clr (loc, sw);
8755 if ((i < sim_brk_ent) &&
8756 (loc == sim_brk_tab[i]->addr))
8757 ++i;
8758 }
8759 return SCPE_OK;
8760 }
8761
8762 /* Show a breakpoint */
8763
8764 t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
8765 {
8766 BRKTAB *bp = sim_brk_fnd_ex (loc, sw & (~SWMASK ('C')), FALSE, 0);
8767 DEVICE *dptr;
8768 int32 i, any;
8769
8770 if ((sw == 0) || (sw == SWMASK ('C')))
8771 sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8772 if (!bp || (!(bp->typ & sw)))
8773 return SCPE_OK;
8774 dptr = sim_dflt_dev;
8775 if (dptr == NULL)
8776 return SCPE_OK;
8777 if (sw & SWMASK ('C'))
8778 fprintf (st, "SET BREAK ");
8779 else {
8780 if (sim_vm_fprint_addr)
8781 sim_vm_fprint_addr (st, dptr, loc);
8782 else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8783 fprintf (st, ":\t");
8784 }
8785 for (i = any = 0; i < 26; i++) {
8786 if ((bp->typ >> i) & 1) {
8787 if ((sw & SWMASK ('C')) == 0) {
8788 if (any)
8789 fprintf (st, ", ");
8790 fputc (i + 'A', st);
8791 }
8792 else
8793 fprintf (st, "-%c", i + 'A');
8794 any = 1;
8795 }
8796 }
8797 if (sw & SWMASK ('C')) {
8798 fprintf (st, " ");
8799 if (sim_vm_fprint_addr)
8800 sim_vm_fprint_addr (st, dptr, loc);
8801 else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);
8802 }
8803 if (bp->cnt > 0)
8804 fprintf (st, "[%d]", bp->cnt);
8805 if (bp->act != NULL)
8806 fprintf (st, "; %s", bp->act);
8807 fprintf (st, "\n");
8808 return SCPE_OK;
8809 }
8810
8811 /* Show all breakpoints */
8812
8813 t_stat sim_brk_showall (FILE *st, int32 sw)
8814 {
8815 int32 bit, mask, types;
8816 BRKTAB **bpt;
8817
8818 if ((sw == 0) || (sw == SWMASK ('C')))
8819 sw = SIM_BRK_ALLTYP | ((sw == SWMASK ('C')) ? SWMASK ('C') : 0);
8820 for (types=bit=0; bit <= ('Z'-'A'); bit++)
8821 if (sim_brk_types & (1 << bit))
8822 ++types;
8823 if ((!(sw & SWMASK ('C'))) && sim_brk_types && (types > 1)) {
8824 fprintf (st, "Supported Breakpoint Types:");
8825 for (bit=0; bit <= ('Z'-'A'); bit++)
8826 if (sim_brk_types & (1 << bit))
8827 fprintf (st, " -%c", 'A' + bit);
8828 fprintf (st, "\n");
8829 }
8830 if (((sw & sim_brk_types) != sim_brk_types) && (types > 1)) {
8831 mask = (sw & sim_brk_types);
8832 fprintf (st, "Displaying Breakpoint Types:");
8833 for (bit=0; bit <= ('Z'-'A'); bit++)
8834 if (mask & (1 << bit))
8835 fprintf (st, " -%c", 'A' + bit);
8836 fprintf (st, "\n");
8837 }
8838 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8839 BRKTAB *prev = NULL;
8840 BRKTAB *cur = *bpt;
8841 BRKTAB *next;
8842 /* First reverse the list */
8843 while (cur) {
8844 next = cur->next;
8845 cur->next = prev;
8846 prev = cur;
8847 cur = next;
8848 }
8849 /* save reversed list in the head pointer so lookups work */
8850 *bpt = prev;
8851 /* Walk the reversed list and print it in the order it was defined in */
8852 cur = prev;
8853 while (cur) {
8854 if (cur->typ & sw)
8855 sim_brk_show (st, cur->addr, cur->typ | ((sw & SWMASK ('C')) ? SWMASK ('C') : 0));
8856 cur = cur->next;
8857 }
8858 /* reversing the list again */
8859 cur = prev;
8860 prev = NULL;
8861 while (cur) {
8862 next = cur->next;
8863 cur->next = prev;
8864 prev = cur;
8865 cur = next;
8866 }
8867 /* restore original list */
8868 *bpt = prev;
8869 }
8870 return SCPE_OK;
8871 }
8872
8873 /* Test for breakpoint */
8874
8875 uint32 sim_brk_test (t_addr loc, uint32 btyp)
8876 {
8877 BRKTAB *bp;
8878 uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);
8879
8880 if (sim_brk_summ & BRK_TYP_DYN_ALL)
8881 btyp |= BRK_TYP_DYN_ALL;
8882
8883 if ((bp = sim_brk_fnd_ex (loc, btyp, TRUE, spc))) { /* in table, and type match? */
8884 double s_gtime = sim_gtime (); /* get time now */
8885
8886 if (bp->time_fired[spc] == s_gtime) /* already taken? */
8887 return 0;
8888 bp->time_fired[spc] = s_gtime; /* remember match time */
8889 if (--bp->cnt > 0) /* count > 0? */
8890 return 0;
8891 bp->cnt = 0; /* reset count */
8892 sim_brk_setact (bp->act); /* set up actions */
8893 sim_brk_match_type = btyp & bp->typ; /* set return value */
8894 if (bp->typ & BRK_TYP_TEMP)
8895 sim_brk_clr (loc, bp->typ); /* delete one-shot breakpoint */
8896 sim_brk_match_addr = loc;
8897 return sim_brk_match_type;
8898 }
8899 return 0;
8900 }
8901
8902 /* Get next pending action, if any */
8903
8904 CONST char *sim_brk_getact (char *buf, int32 size)
8905 {
8906 char *ep;
8907 size_t lnt;
8908
8909 if (sim_brk_act[sim_do_depth] == NULL) /* any action? */
8910 return NULL;
8911 while (sim_isspace (*sim_brk_act[sim_do_depth])) /* skip spaces */
8912 sim_brk_act[sim_do_depth]++;
8913 if (*sim_brk_act[sim_do_depth] == 0) { /* now empty? */
8914 return sim_brk_clract ();
8915 }
8916 if ((ep = strchr (sim_brk_act[sim_do_depth], ';'))) { /* cmd delimiter? */
8917 lnt = ep - sim_brk_act[sim_do_depth]; /* cmd length */
8918 memcpy (buf, sim_brk_act[sim_do_depth], lnt + 1); /* copy with ; */
8919 buf[lnt] = 0; /* erase ; */
8920 sim_brk_act[sim_do_depth] += lnt + 1; /* adv ptr */
8921 }
8922 else {
8923 strncpy (buf, sim_brk_act[sim_do_depth], size); /* copy action */
8924 sim_brk_clract (); /* no more */
8925 }
8926 return buf;
8927 }
8928
8929 /* Clear pending actions */
8930
8931 char *sim_brk_clract (void)
8932 {
8933 free (sim_brk_act_buf[sim_do_depth]);
8934 return sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth] = NULL;
8935 }
8936
8937 /* Set up pending actions */
8938
8939 void sim_brk_setact (const char *action)
8940 {
8941 if (action) {
8942 sim_brk_act_buf[sim_do_depth] = (char *)realloc (sim_brk_act_buf[sim_do_depth], strlen (action) + 1);
8943 strcpy (sim_brk_act_buf[sim_do_depth], action);
8944 sim_brk_act[sim_do_depth] = sim_brk_act_buf[sim_do_depth];
8945 }
8946 else
8947 sim_brk_clract ();
8948 }
8949
8950 /* New PC */
8951
8952 void sim_brk_npc (uint32 cnt)
8953 {
8954 uint32 spc;
8955 BRKTAB **bpt, *bp;
8956
8957 if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC))
8958 cnt = SIM_BKPT_N_SPC;
8959 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8960 for (bp = *bpt; bp; bp = bp->next) {
8961 for (spc = 0; spc < cnt; spc++)
8962 bp->time_fired[spc] = -1.0;
8963 }
8964 }
8965 }
8966
8967 /* Clear breakpoint space */
8968
8969 void sim_brk_clrspc (uint32 spc, uint32 btyp)
8970 {
8971 BRKTAB **bpt, *bp;
8972
8973 if (spc < SIM_BKPT_N_SPC) {
8974 for (bpt = sim_brk_tab; bpt < (sim_brk_tab + sim_brk_ent); bpt++) {
8975 for (bp = *bpt; bp; bp = bp->next) {
8976 if (bp->typ & btyp)
8977 bp->time_fired[spc] = -1.0;
8978 }
8979 }
8980 }
8981 }
8982
8983 const char *sim_brk_message(void)
8984 {
8985 static char msg[256];
8986 char addr[65];
8987 char buf[32];
8988
8989 msg[0] = '\0';
8990 if (sim_dflt_dev) {
8991 if (sim_vm_sprint_addr)
8992 sim_vm_sprint_addr (addr, sim_dflt_dev, (t_value)sim_brk_match_addr);
8993 else sprint_val (addr, (t_value)sim_brk_match_addr, sim_dflt_dev->aradix, sim_dflt_dev->awidth, PV_LEFT);
8994 }
8995 if (sim_brk_type_desc) {
8996 BRKTYPTAB *brk = sim_brk_type_desc;
8997
8998 while (2 == strlen (put_switches (buf, sizeof(buf), brk->btyp))) {
8999 if (brk->btyp == sim_brk_match_type) {
9000 sprintf (msg, "%s: %s", brk->desc, addr);
9001 break;
9002 }
9003 brk++;
9004 }
9005 }
9006 if (!msg[0])
9007 sprintf (msg, "%s Breakpoint at: %s\n", put_switches (buf, sizeof(buf), sim_brk_match_type), addr);
9008
9009 return msg;
9010 }
9011
9012 /* Expect package. This code provides a mechanism to stop and control simulator
9013 execution based on traffic coming out of simulated ports and as well as a means
9014 to inject data into those ports. It can conceptually viewed as a string
9015 breakpoint package.
9016
9017 Expect rules are stored in tables associated with each port which can use this
9018 facility. An expect rule consists of a five entry structure:
9019
9020 match the expect match string
9021 size the number of bytes in the match string
9022 match_pattern the expect match string in display format
9023 cnt number of iterations before match is declared
9024 action command string to be executed when match occurs
9025
9026 All active expect rules are contained in an expect match context structure.
9027
9028 rules the match rules
9029 size the count of match rules
9030 buf the buffer of output data which has been produced
9031 buf_ins the buffer insertion point for the next output data
9032 buf_size the buffer size
9033
9034 The package contains the following public routines:
9035
9036 sim_set_expect expect command parser and intializer
9037 sim_set_noexpect noexpect command parser
9038 sim_exp_set set or add an expect rule
9039 sim_exp_clr clear or delete an expect rule
9040 sim_exp_clrall clear all expect rules
9041 sim_exp_show show an expect rule
9042 sim_exp_showall show all expect rules
9043 sim_exp_check test for rule match
9044 */
9045
9046 /* Set expect */
9047
9048 t_stat sim_set_expect (EXPECT *exp, CONST char *cptr)
9049 {
9050 char gbuf[CBUFSIZE];
9051 CONST char *tptr;
9052 CONST char *c1ptr;
9053 t_bool after_set = FALSE;
9054 uint32 after = exp->after;
9055 int32 cnt = 0;
9056 t_stat r;
9057
9058 if ((cptr == NULL) || (*cptr == 0))
9059 return SCPE_2FARG;
9060 if (*cptr == '[') {
9061 cnt = (int32) strtotv (cptr + 1, &c1ptr, 10);
9062 if ((cptr == c1ptr) || (*c1ptr != ']'))
9063 return sim_messagef (SCPE_ARG, "Invalid Repeat count specification\n");
9064 cptr = c1ptr + 1;
9065 while (sim_isspace(*cptr))
9066 ++cptr;
9067 }
9068 tptr = get_glyph (cptr, gbuf, ',');
9069 if ((!strncmp(gbuf, "HALTAFTER=", 10)) && (gbuf[10])) {
9070 after = (uint32)get_uint (&gbuf[10], 10, 2000000000, &r);
9071 if (r != SCPE_OK)
9072 return sim_messagef (SCPE_ARG, "Invalid Halt After Value\n");
9073 after_set = TRUE;
9074 cptr = tptr;
9075 }
9076 if ((*cptr != '"') && (*cptr != '\''))
9077 return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9078 cptr = get_glyph_quoted (cptr, gbuf, 0);
9079
9080 return sim_exp_set (exp, gbuf, cnt, (after_set ? after : exp->after), sim_switches, cptr);
9081 }
9082
9083 /* Clear expect */
9084
9085 t_stat sim_set_noexpect (EXPECT *exp, const char *cptr)
9086 {
9087 char gbuf[CBUFSIZE];
9088
9089 if (!cptr || !*cptr)
9090 return sim_exp_clrall (exp); /* clear all rules */
9091 if ((*cptr != '"') && (*cptr != '\''))
9092 return sim_messagef (SCPE_ARG, "String must be quote delimited\n");
9093 cptr = get_glyph_quoted (cptr, gbuf, 0);
9094 if (*cptr != '\0')
9095 return SCPE_2MARG; /* No more arguments */
9096 return sim_exp_clr (exp, gbuf); /* clear one rule */
9097 }
9098
9099 /* Search for an expect rule in an expect context */
9100
9101 CONST EXPTAB *sim_exp_fnd (CONST EXPECT *exp, const char *match, int32 start_rule)
9102 {
9103 int32 i;
9104
9105 if (!exp->rules)
9106 return NULL;
9107 for (i=start_rule; i<exp->size; i++)
9108 if (!strcmp (exp->rules[i].match_pattern, match))
9109 return &exp->rules[i];
9110 return NULL;
9111 }
9112
9113 /* Clear (delete) an expect rule */
9114
9115 t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep)
9116 {
9117 int32 i;
9118
9119 if (!ep) /* not there? ok */
9120 return SCPE_OK;
9121 free (ep->match); /* deallocate match string */
9122 free (ep->match_pattern); /* deallocate the display format match string */
9123 free (ep->act); /* deallocate action */
9124 exp->size -= 1; /* decrement count */
9125 for (i=ep-exp->rules; i<exp->size; i++) /* shuffle up remaining rules */
9126 exp->rules[i] = exp->rules[i+1];
9127 if (exp->size == 0) { /* No rules left? */
9128 free (exp->rules);
9129 exp->rules = NULL;
9130 }
9131 return SCPE_OK;
9132 }
9133
9134 t_stat sim_exp_clr (EXPECT *exp, const char *match)
9135 {
9136 EXPTAB *ep = (EXPTAB *)sim_exp_fnd (exp, match, 0);
9137
9138 while (ep) {
9139 sim_exp_clr_tab (exp, ep);
9140 ep = (EXPTAB *)sim_exp_fnd (exp, match, ep - exp->rules);
9141 }
9142 return SCPE_OK;
9143 }
9144
9145 /* Clear all expect rules */
9146
9147 t_stat sim_exp_clrall (EXPECT *exp)
9148 {
9149 int32 i;
9150
9151 for (i=0; i<exp->size; i++) {
9152 free (exp->rules[i].match); /* deallocate match string */
9153 free (exp->rules[i].match_pattern); /* deallocate display format match string */
9154 free (exp->rules[i].act); /* deallocate action */
9155 }
9156 free (exp->rules);
9157 exp->rules = NULL;
9158 exp->size = 0;
9159 free (exp->buf);
9160 exp->buf = NULL;
9161 exp->buf_size = 0;
9162 exp->buf_ins = 0;
9163 return SCPE_OK;
9164 }
9165
9166 /* Set/Add an expect rule */
9167
9168 t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act)
9169 {
9170 EXPTAB *ep;
9171 uint8 *match_buf;
9172 uint32 match_size;
9173 int i;
9174
9175 /* Validate the match string */
9176 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9177 if (!match_buf)
9178 return SCPE_MEM;
9179 if (switches & EXP_TYP_REGEX) {
9180 free (match_buf);
9181 return sim_messagef (SCPE_ARG, "RegEx support not available\n");
9182 }
9183 else {
9184 if (switches & EXP_TYP_REGEX_I) {
9185 free (match_buf);
9186 return sim_messagef (SCPE_ARG, "Case independed matching is only valid for RegEx expect rules\n");
9187 }
9188 sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9189 if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) {
9190 free (match_buf);
9191 return sim_messagef (SCPE_ARG, "Invalid quoted string\n");
9192 }
9193 }
9194 free (match_buf);
9195 for (i=0; i<exp->size; i++) { /* Make sure this rule won't be occluded */
9196 if ((0 == strcmp (match, exp->rules[i].match_pattern)) &&
9197 (exp->rules[i].switches & EXP_TYP_PERSIST))
9198 return sim_messagef (SCPE_ARG, "Persistent Expect rule with identical match string already exists\n");
9199 }
9200 if (after && exp->size)
9201 return sim_messagef (SCPE_ARG, "Multiple concurrent EXPECT rules aren't valid when a HALTAFTER parameter is non-zero\n");
9202 exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1));
9203 ep = &exp->rules[exp->size];
9204 exp->size += 1;
9205 exp->after = after; /* set halt after value */
9206 memset (ep, 0, sizeof(*ep));
9207 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9208 if (ep->match_pattern)
9209 strcpy (ep->match_pattern, match);
9210 ep->cnt = cnt; /* set proceed count */
9211 ep->switches = switches; /* set switches */
9212 match_buf = (uint8 *)calloc (strlen (match) + 1, 1);
9213 if ((match_buf == NULL) || (ep->match_pattern == NULL)) {
9214 sim_exp_clr_tab (exp, ep); /* clear it */
9215 return SCPE_MEM;
9216 }
9217 if (switches & EXP_TYP_REGEX) {
9218 free (match_buf);
9219 match_buf = NULL;
9220 }
9221 else {
9222 sim_data_trace(exp->dptr, exp->dptr->units, (const uint8 *)match, "", strlen(match)+1, "Expect Match String", exp->dbit);
9223 sim_decode_quoted_string (match, match_buf, &match_size);
9224 ep->match = match_buf;
9225 ep->size = match_size;
9226 }
9227 ep->match_pattern = (char *)malloc (strlen (match) + 1);
9228 strcpy (ep->match_pattern, match);
9229 if (ep->act) { /* replace old action? */
9230 free (ep->act); /* deallocate */
9231 ep->act = NULL; /* now no action */
9232 }
9233 if (act) while (sim_isspace(*act)) ++act; /* skip leading spaces in action string */
9234 if ((act != NULL) && (*act != 0)) { /* new action? */
9235 char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */
9236 if (newp == NULL) /* mem err? */
9237 return SCPE_MEM;
9238 strcpy (newp, act); /* copy action */
9239 ep->act = newp; /* set pointer */
9240 }
9241 /* Make sure that the production buffer is large enough to detect a match for all rules including a NUL termination byte */
9242 for (i=0; i<exp->size; i++) {
9243 uint32 compare_size = (exp->rules[i].switches & EXP_TYP_REGEX) ? MAX(10 * strlen(ep->match_pattern), 1024) : exp->rules[i].size;
9244 if (compare_size >= exp->buf_size) {
9245 exp->buf = (uint8 *)realloc (exp->buf, compare_size + 2); /* Extra byte to null terminate regex compares */
9246 exp->buf_size = compare_size + 1;
9247 }
9248 }
9249 return SCPE_OK;
9250 }
9251
9252 /* Show an expect rule */
9253
9254 t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep)
9255 {
9256 if (!ep)
9257 return SCPE_OK;
9258 fprintf (st, "EXPECT");
9259 if (ep->switches & EXP_TYP_PERSIST)
9260 fprintf (st, " -p");
9261 if (ep->switches & EXP_TYP_CLEARALL)
9262 fprintf (st, " -c");
9263 if (ep->switches & EXP_TYP_REGEX)
9264 fprintf (st, " -r");
9265 if (ep->switches & EXP_TYP_REGEX_I)
9266 fprintf (st, " -i");
9267 fprintf (st, " %s", ep->match_pattern);
9268 if (ep->cnt > 0)
9269 fprintf (st, " [%d]", ep->cnt);
9270 if (ep->act)
9271 fprintf (st, " %s", ep->act);
9272 fprintf (st, "\n");
9273 return SCPE_OK;
9274 }
9275
9276 t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match)
9277 {
9278 CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0);
9279
9280 if (exp->buf_size) {
9281 char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9282
9283 fprintf (st, "Match Buffer Size: %d\n", exp->buf_size);
9284 fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins);
9285 fprintf (st, "Buffer Contents: %s\n", bstr);
9286 free (bstr);
9287 }
9288 if (exp->after)
9289 fprintf (st, "Halt After: %d instructions\n", exp->after);
9290 if (exp->dptr && exp->dbit)
9291 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) : "");
9292 fprintf (st, "Match Rules:\n");
9293 if (!*match)
9294 return sim_exp_showall (st, exp);
9295 if (!ep) {
9296 fprintf (st, "No Rules match '%s'\n", match);
9297 return SCPE_ARG;
9298 }
9299 do {
9300 sim_exp_show_tab (st, exp, ep);
9301 ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 1 + (ep - exp->rules));
9302 } while (ep);
9303 return SCPE_OK;
9304 }
9305
9306 /* Show all expect rules */
9307
9308 t_stat sim_exp_showall (FILE *st, const EXPECT *exp)
9309 {
9310 int32 i;
9311
9312 for (i=0; i < exp->size; i++)
9313 sim_exp_show_tab (st, exp, &exp->rules[i]);
9314 return SCPE_OK;
9315 }
9316
9317 /* Test for expect match */
9318
9319 t_stat sim_exp_check (EXPECT *exp, uint8 data)
9320 {
9321 int32 i;
9322 EXPTAB *ep = NULL;
9323 int regex_checks = 0;
9324 char *tstr = NULL;
9325
9326 if ((!exp) || (!exp->rules)) /* Anying to check? */
9327 return SCPE_OK;
9328
9329 exp->buf[exp->buf_ins++] = data; /* Save new data */
9330 exp->buf[exp->buf_ins] = '\0'; /* Nul terminate for RegEx match */
9331
9332 for (i=0; i < exp->size; i++) {
9333 ep = &exp->rules[i];
9334 if (ep->switches & EXP_TYP_REGEX) {
9335 }
9336 else {
9337 if (exp->buf_ins < ep->size) { /* Match stradle end of buffer */
9338 /*
9339 * First compare the newly deposited data at the beginning
9340 * of buffer with the end of the match string
9341 */
9342 if (exp->buf_ins) {
9343 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9344 char *estr = sim_encode_quoted_string (exp->buf, exp->buf_ins);
9345 char *mstr = sim_encode_quoted_string (&ep->match[ep->size-exp->buf_ins], exp->buf_ins);
9346
9347 sim_debug (exp->dbit, exp->dptr, "Checking String[0:%d]: %s\n", exp->buf_ins, estr);
9348 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9349 free (estr);
9350 free (mstr);
9351 }
9352 if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins))
9353 continue;
9354 }
9355 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9356 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->size-exp->buf_ins);
9357 char *mstr = sim_encode_quoted_string (ep->match, ep->size-exp->buf_ins);
9358
9359 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);
9360 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9361 free (estr);
9362 free (mstr);
9363 }
9364 if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins))
9365 continue;
9366 break;
9367 }
9368 else {
9369 if (sim_deb && exp->dptr && (exp->dptr->dctrl & exp->dbit)) {
9370 char *estr = sim_encode_quoted_string (&exp->buf[exp->buf_ins-ep->size], ep->size);
9371 char *mstr = sim_encode_quoted_string (ep->match, ep->size);
9372
9373 sim_debug (exp->dbit, exp->dptr, "Checking String[%d:%d]: %s\n", exp->buf_ins-ep->size, ep->size, estr);
9374 sim_debug (exp->dbit, exp->dptr, "Against Match Data: %s\n", mstr);
9375 free (estr);
9376 free (mstr);
9377 }
9378 if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size))
9379 continue;
9380 break;
9381 }
9382 }
9383 }
9384 if (exp->buf_ins == exp->buf_size) { /* At end of match buffer? */
9385 if (regex_checks) {
9386 /* When processing regular expressions, let the match buffer fill
9387 up and then shuffle the buffer contents down by half the buffer size
9388 so that the regular expression has a single contiguous buffer to
9389 match against instead of the wrapping buffer paradigm which is
9390 used when no regular expression rules are in effect */
9391 memmove (exp->buf, &exp->buf[exp->buf_size/2], exp->buf_size-(exp->buf_size/2));
9392 exp->buf_ins -= exp->buf_size/2;
9393 sim_debug (exp->dbit, exp->dptr, "Buffer Full - sliding the last %d bytes to start of buffer new insert at: %d\n", (exp->buf_size/2), exp->buf_ins);
9394 }
9395 else {
9396 exp->buf_ins = 0; /* wrap around to beginning */
9397 sim_debug (exp->dbit, exp->dptr, "Buffer wrapping\n");
9398 }
9399 }
9400 if (i != exp->size) { /* Found? */
9401 sim_debug (exp->dbit, exp->dptr, "Matched expect pattern: %s\n", ep->match_pattern);
9402 setenv ("_EXPECT_MATCH_PATTERN", ep->match_pattern, 1); /* Make the match detail available as an environment variable */
9403 if (ep->cnt > 0) {
9404 ep->cnt -= 1;
9405 sim_debug (exp->dbit, exp->dptr, "Waiting for %d more match%s before stopping\n",
9406 ep->cnt, (ep->cnt == 1) ? "" : "es");
9407 }
9408 else {
9409 if (ep->act && *ep->act) {
9410 sim_debug (exp->dbit, exp->dptr, "Initiating actions: %s\n", ep->act);
9411 }
9412 else {
9413 sim_debug (exp->dbit, exp->dptr, "No actions specified, stopping...\n");
9414 }
9415 sim_brk_setact (ep->act); /* set up actions */
9416 if (ep->switches & EXP_TYP_CLEARALL) /* Clear-all expect rule? */
9417 sim_exp_clrall (exp); /* delete all rules */
9418 else {
9419 if (!(ep->switches & EXP_TYP_PERSIST)) /* One shot expect rule? */
9420 sim_exp_clr_tab (exp, ep); /* delete it */
9421 }
9422 sim_activate (&sim_expect_unit, /* schedule simulation stop when indicated */
9423 (ep->switches & EXP_TYP_TIME) ?
9424 (int32)((sim_timer_inst_per_sec ()*exp->after)/1000000.0) :
9425 exp->after);
9426 }
9427 /* Matched data is no longer available for future matching */
9428 exp->buf_ins = 0;
9429 }
9430 free (tstr);
9431 return SCPE_OK;
9432 }
9433
9434 /* Queue input data for sending */
9435
9436 t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay)
9437 {
9438 if (snd->extoff != 0) {
9439 if (snd->insoff-snd->extoff > 0)
9440 memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9441 snd->insoff -= snd->extoff;
9442 snd->extoff -= snd->extoff;
9443 }
9444 if (snd->insoff+size > snd->bufsize) {
9445 snd->bufsize = snd->insoff+size;
9446 snd->buffer = (uint8 *)realloc(snd->buffer, snd->bufsize);
9447 }
9448 memcpy(snd->buffer+snd->insoff, data, size);
9449 snd->insoff += size;
9450 if (delay)
9451 snd->delay = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*delay)/1000000.0) : delay;
9452 if (after)
9453 snd->after = (sim_switches & SWMASK ('T')) ? (uint32)((sim_timer_inst_per_sec()*after)/1000000.0) : after;
9454 if (snd->after == 0)
9455 snd->after = snd->delay;
9456 snd->next_time = sim_gtime() + snd->after;
9457 return SCPE_OK;
9458 }
9459
9460 /* Cancel Queued input data */
9461 t_stat sim_send_clear (SEND *snd)
9462 {
9463 snd->insoff = 0;
9464 snd->extoff = 0;
9465 return SCPE_OK;
9466 }
9467
9468 /* Display console Queued input data status */
9469
9470 t_stat sim_show_send_input (FILE *st, const SEND *snd)
9471 {
9472 if (snd->extoff < snd->insoff) {
9473 fprintf (st, "%d bytes of pending input Data:\n ", snd->insoff-snd->extoff);
9474 fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff);
9475 fprintf (st, "\n");
9476 }
9477 else
9478 fprintf (st, "No Pending Input Data\n");
9479 if ((snd->next_time - sim_gtime()) > 0) {
9480 if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0))
9481 fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()),
9482 (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0)));
9483 else
9484 fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime()));
9485 }
9486 if (snd->delay > (sim_timer_inst_per_sec()/1000000.0))
9487 fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0)));
9488 else
9489 fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay);
9490 if (snd->dptr && snd->dbit)
9491 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) : "");
9492 return SCPE_OK;
9493 }
9494
9495 /* Poll for Queued input data */
9496
9497 t_bool sim_send_poll_data (SEND *snd, t_stat *stat)
9498 {
9499 if (snd && (snd->extoff < snd->insoff)) { /* pending input characters available? */
9500 if (sim_gtime() < snd->next_time) { /* too soon? */
9501 *stat = SCPE_OK;
9502 sim_debug (snd->dbit, snd->dptr, "Too soon to inject next byte\n");
9503 }
9504 else {
9505 char dstr[8] = "";
9506 *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */
9507 snd->next_time = sim_gtime() + snd->delay;
9508 if (sim_isgraph(*stat & 0xFF) || ((*stat & 0xFF) == ' '))
9509 sprintf (dstr, " '%c'", *stat & 0xFF);
9510 sim_debug (snd->dbit, snd->dptr, "Byte value: 0x%02X%s injected\n", *stat & 0xFF, dstr);
9511 }
9512 return TRUE;
9513 }
9514 return FALSE;
9515 }
9516
9517 /* Message Text */
9518
9519 const char *sim_error_text (t_stat stat)
9520 {
9521 static char msgbuf[64];
9522
9523 stat &= ~(SCPE_KFLAG|SCPE_BREAK|SCPE_NOMESSAGE); /* remove any flags */
9524 if (stat == SCPE_OK)
9525 return "No Error";
9526 if ((stat >= SCPE_BASE) && (stat <= SCPE_MAX_ERR))
9527 return scp_errors[stat-SCPE_BASE].message;
9528 sprintf(msgbuf, "Error %d", stat);
9529 return msgbuf;
9530 }
9531
9532 t_stat sim_string_to_stat (const char *cptr, t_stat *stat)
9533 {
9534 char gbuf[CBUFSIZE];
9535 int32 cond;
9536
9537 *stat = SCPE_ARG;
9538 cptr = get_glyph (cptr, gbuf, 0);
9539 if (0 == memcmp("SCPE_", gbuf, 5))
9540 strcpy (gbuf, gbuf+5); /* skip leading SCPE_ */
9541 for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++)
9542 if (0 == strcmp(scp_errors[cond].code, gbuf)) {
9543 cond += SCPE_BASE;
9544 break;
9545 }
9546 if (0 == strcmp(gbuf, "OK"))
9547 cond = SCPE_OK;
9548 if (cond == (SCPE_MAX_ERR-SCPE_BASE)) { /* not found? */
9549 if (0 == (cond = strtol(gbuf, NULL, 0))) /* try explicit number */
9550 return SCPE_ARG;
9551 }
9552 if (cond > SCPE_MAX_ERR)
9553 return SCPE_ARG;
9554 *stat = cond;
9555 return SCPE_OK;
9556 }
9557
9558 /* Debug printout routines, from Dave Hittner */
9559
9560 const char* debug_bstates = "01_^";
9561 char debug_line_prefix[256];
9562 int32 debug_unterm = 0;
9563
9564 /* Finds debug phrase matching bitmask from from device DEBTAB table */
9565
9566 static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr)
9567 {
9568 static const char *debtab_none = "DEBTAB_ISNULL";
9569 static const char *debtab_nomatch = "DEBTAB_NOMATCH";
9570 const char *some_match = NULL;
9571 int32 offset = 0;
9572
9573 if (dptr->debflags == 0)
9574 return debtab_none;
9575
9576 dbits &= dptr->dctrl; /* Look for just the bits tha matched */
9577
9578 /* Find matching words for bitmask */
9579
9580 while (dptr->debflags[offset].name && (offset < 32)) {
9581 if (dptr->debflags[offset].mask == dbits) /* All Bits Match */
9582 return dptr->debflags[offset].name;
9583 if (dptr->debflags[offset].mask & dbits)
9584 some_match = dptr->debflags[offset].name;
9585 offset++;
9586 }
9587 return some_match ? some_match : debtab_nomatch;
9588 }
9589
9590 /* Prints standard debug prefix unless previous call unterminated */
9591
9592 static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr)
9593 {
9594 const char* debug_type = get_dbg_verb (dbits, dptr);
9595 char tim_t[32] = "";
9596 char tim_a[32] = "";
9597 char pc_s[64] = "";
9598 struct timespec time_now;
9599
9600 if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) {
9601 clock_gettime(CLOCK_REALTIME, &time_now);
9602 if (sim_deb_switches & SWMASK ('R'))
9603 sim_timespec_diff (&time_now, &time_now, &sim_deb_basetime);
9604 if (sim_deb_switches & SWMASK ('T')) {
9605 time_t tnow = (time_t)time_now.tv_sec;
9606 struct tm *now = gmtime(&tnow);
9607 sprintf(tim_t, "%02d:%02d:%02d.%03d ", now->tm_hour, now->tm_min, now->tm_sec, (int)(time_now.tv_nsec/1000000));
9608 }
9609 if (sim_deb_switches & SWMASK ('A')) {
9610 sprintf(tim_t, "%" LL_FMT "d.%03d ", (long long)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000));
9611 }
9612 }
9613 if (sim_deb_switches & SWMASK ('P')) {
9614 t_value val;
9615
9616 /* Some simulators expose the PC as a register, some don't expose it or expose a register
9617 which is not a variable which is updated during instruction execution (i.e. only upon
9618 exit of sim_instr()). For the -P debug option to be effective, such a simulator should
9619 provide a routine which returns the value of the current PC and set the sim_vm_pc_value
9620 routine pointer to that routine.
9621 */
9622 if (sim_vm_pc_value)
9623 val = (*sim_vm_pc_value)();
9624 else
9625 val = get_rval (sim_PC, 0);
9626 sprintf(pc_s, "-%s:", sim_PC->name);
9627 sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT);
9628 }
9629 sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s, "", dptr->name, debug_type);
9630 return debug_line_prefix;
9631 }
9632
9633 void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs)
9634 {
9635 int32 i, fields, offset;
9636 uint32 value, beforevalue, mask;
9637
9638 for (fields=offset=0; bitdefs[fields].name; ++fields) {
9639 if (bitdefs[fields].offset == 0xffffffff) /* fixup uninitialized offsets */
9640 bitdefs[fields].offset = offset;
9641 offset += bitdefs[fields].width;
9642 }
9643 for (i = fields-1; i >= 0; i--) { /* print xlation, transition */
9644 if (bitdefs[i].name[0] == '\0')
9645 continue;
9646 if ((bitdefs[i].width == 1) && (bitdefs[i].valuenames == NULL)) {
9647 int off = ((after >> bitdefs[i].offset) & 1) + (((before ^ after) >> bitdefs[i].offset) & 1) * 2;
9648 Fprintf(stream, "%s%c ", bitdefs[i].name, debug_bstates[off]);
9649 }
9650 else {
9651 const char *delta = "";
9652 mask = 0xFFFFFFFF >> (32-bitdefs[i].width);
9653 value = (uint32)((after >> bitdefs[i].offset) & mask);
9654 beforevalue = (uint32)((before >> bitdefs[i].offset) & mask);
9655 if (value < beforevalue)
9656 delta = "_";
9657 if (value > beforevalue)
9658 delta = "^";
9659 if (bitdefs[i].valuenames)
9660 Fprintf(stream, "%s=%s%s ", bitdefs[i].name, delta, bitdefs[i].valuenames[value]);
9661 else
9662 if (bitdefs[i].format) {
9663 Fprintf(stream, "%s=%s", bitdefs[i].name, delta);
9664 Fprintf(stream, bitdefs[i].format, value);
9665 Fprintf(stream, " ");
9666 }
9667 else
9668 Fprintf(stream, "%s=%s0x%X ", bitdefs[i].name, delta, value);
9669 }
9670 }
9671 }
9672
9673 /* Prints state of a register: bit translation + state (0,1,_,^)
9674 indicating the state and transition of the bit and bitfields. States:
9675 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */
9676
9677 void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header,
9678 BITFIELD* bitdefs, uint32 before, uint32 after, int terminate)
9679 {
9680 if (sim_deb && dptr && (dptr->dctrl & dbits)) {
9681 if (!debug_unterm)
9682 fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr)); /* print prefix if required */
9683 if (header)
9684 fprintf(sim_deb, "%s: ", header);
9685 fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */
9686 if (terminate)
9687 fprintf(sim_deb, "\r\n");
9688 debug_unterm = terminate ? 0 : 1; /* set unterm for next */
9689 }
9690 }
9691 void sim_debug_bits(uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs,
9692 uint32 before, uint32 after, int terminate)
9693 {
9694 sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate);
9695 }
9696
9697 /* Print message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9698 void sim_printf (const char* fmt, ...)
9699 {
9700 char stackbuf[STACKBUFSIZE];
9701 int32 bufsize = sizeof(stackbuf);
9702 char *buf = stackbuf;
9703 int32 len;
9704 va_list arglist;
9705
9706 while (1) { /* format passed string, args */
9707 va_start (arglist, fmt);
9708 len = vsnprintf (buf, bufsize-1, fmt, arglist);
9709 va_end (arglist);
9710
9711 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9712
9713 if ((len < 0) || (len >= bufsize-1)) {
9714 if (buf != stackbuf)
9715 free (buf);
9716 bufsize = bufsize * 2;
9717 if (bufsize < len + 2)
9718 bufsize = len + 2;
9719 buf = (char *) malloc (bufsize);
9720 if (buf == NULL) /* out of memory */
9721 return;
9722 buf[bufsize-1] = '\0';
9723 continue;
9724 }
9725 break;
9726 }
9727
9728 if (sim_is_running) {
9729 char *c, *remnant = buf;
9730 while ((c = strchr(remnant, '\n'))) {
9731 if ((c != buf) && (*(c - 1) != '\r'))
9732 printf("%.*s\r\n", (int)(c-remnant), remnant);
9733 else
9734 printf("%.*s\n", (int)(c-remnant), remnant);
9735 remnant = c + 1;
9736 }
9737 printf("%s", remnant);
9738 }
9739 else
9740 printf("%s", buf);
9741 if (sim_log && (sim_log != stdout))
9742 fprintf (sim_log, "%s", buf);
9743 if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log))
9744 fprintf (sim_deb, "%s", buf);
9745
9746 if (buf != stackbuf)
9747 free (buf);
9748 }
9749
9750 /* Print command result message to stdout, sim_log (if enabled) and sim_deb (if enabled) */
9751 t_stat sim_messagef (t_stat stat, const char* fmt, ...)
9752 {
9753 char stackbuf[STACKBUFSIZE];
9754 int32 bufsize = sizeof(stackbuf);
9755 char *buf = stackbuf;
9756 int32 len;
9757 va_list arglist;
9758 t_bool inhibit_message = (!sim_show_message || (stat & SCPE_NOMESSAGE));
9759
9760 while (1) { /* format passed string, args */
9761 va_start (arglist, fmt);
9762 len = vsnprintf (buf, bufsize-1, fmt, arglist);
9763 va_end (arglist);
9764
9765 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9766
9767 if ((len < 0) || (len >= bufsize-1)) {
9768 if (buf != stackbuf)
9769 free (buf);
9770 bufsize = bufsize * 2;
9771 if (bufsize < len + 2)
9772 bufsize = len + 2;
9773 buf = (char *) malloc (bufsize);
9774 if (buf == NULL) /* out of memory */
9775 return SCPE_MEM;
9776 buf[bufsize-1] = '\0';
9777 continue;
9778 }
9779 break;
9780 }
9781
9782 if (sim_do_ocptr[sim_do_depth]) {
9783 if (!sim_do_echo && !sim_quiet && !inhibit_message)
9784 sim_printf("%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9785 else {
9786 if (sim_deb) /* Always put context in debug output */
9787 fprintf (sim_deb, "%s> %s\n", do_position(), sim_do_ocptr[sim_do_depth]);
9788 }
9789 }
9790 if (sim_is_running && !inhibit_message) {
9791 char *c, *remnant = buf;
9792 while ((c = strchr(remnant, '\n'))) {
9793 if ((c != buf) && (*(c - 1) != '\r'))
9794 printf("%.*s\r\n", (int)(c-remnant), remnant);
9795 else
9796 printf("%.*s\n", (int)(c-remnant), remnant);
9797 remnant = c + 1;
9798 }
9799 printf("%s", remnant);
9800 }
9801 else {
9802 if (!inhibit_message)
9803 printf("%s", buf);
9804 }
9805 if (sim_log && (sim_log != stdout) && !inhibit_message)
9806 fprintf (sim_log, "%s", buf);
9807 if (sim_deb && (((sim_deb != stdout) && (sim_deb != sim_log)) || inhibit_message))/* Always display messages in debug output */
9808 fprintf (sim_deb, "%s", buf);
9809
9810 if (buf != stackbuf)
9811 free (buf);
9812 return stat | SCPE_NOMESSAGE;
9813 }
9814
9815 /* Inline debugging - will print debug message if debug file is
9816 set and the bitmask matches the current device debug options.
9817 Extra returns are added for un*x systems, since the output
9818 device is set into 'raw' mode when the cpu is booted,
9819 and the extra returns don't hurt any other systems.
9820 Callers should be calling sim_debug() which is a macro
9821 defined in scp.h which evaluates the action condition before
9822 incurring call overhead. */
9823 void _sim_debug (uint32 dbits, DEVICE* vdptr, const char* fmt, ...)
9824 {
9825 DEVICE *dptr = (DEVICE *)vdptr;
9826 if (sim_deb && dptr && (dbits == 0 || (dptr->dctrl & dbits))) {
9827 char stackbuf[STACKBUFSIZE];
9828 int32 bufsize = sizeof(stackbuf);
9829 char *buf = stackbuf;
9830 va_list arglist;
9831 int32 i, j, len;
9832 const char* debug_prefix = sim_debug_prefix(dbits, dptr); /* prefix to print if required */
9833
9834 buf[bufsize-1] = '\0';
9835 while (1) { /* format passed string, args */
9836 va_start (arglist, fmt);
9837 len = vsnprintf (buf, bufsize-1, fmt, arglist);
9838 va_end (arglist);
9839
9840 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9841
9842 if ((len < 0) || (len >= bufsize-1)) {
9843 if (buf != stackbuf)
9844 free (buf);
9845 bufsize = bufsize * 2;
9846 if (bufsize < len + 2)
9847 bufsize = len + 2;
9848 buf = (char *) malloc (bufsize);
9849 if (buf == NULL) /* out of memory */
9850 return;
9851 buf[bufsize-1] = '\0';
9852 continue;
9853 }
9854 break;
9855 }
9856
9857 /* Output the formatted data expanding newlines where they exist */
9858
9859 for (i = j = 0; i < len; ++i) {
9860 if ('\n' == buf[i]) {
9861 if (i >= j) {
9862 if ((i != j) || (i == 0)) {
9863 if (debug_unterm)
9864 fprintf (sim_deb, "%.*s\r\n", i-j, &buf[j]);
9865 else /* print prefix when required */
9866 fprintf (sim_deb, "%s%.*s\r\n", debug_prefix, i-j, &buf[j]);
9867 }
9868 debug_unterm = 0;
9869 }
9870 j = i + 1;
9871 }
9872 }
9873 if (i > j) {
9874 if (debug_unterm)
9875 fprintf (sim_deb, "%.*s", i-j, &buf[j]);
9876 else /* print prefix when required */
9877 fprintf (sim_deb, "%s%.*s", debug_prefix, i-j, &buf[j]);
9878 }
9879
9880 /* Set unterminated flag for next time */
9881
9882 debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm;
9883 if (buf != stackbuf)
9884 free (buf);
9885 }
9886 return;
9887 }
9888 void _sim_err (const char* fmt, ...)
9889 {
9890
9891 char stackbuf[STACKBUFSIZE];
9892 int32 bufsize = sizeof(stackbuf);
9893 char *buf = stackbuf;
9894 va_list arglist;
9895 int32 i, j, len;
9896 //sprintf(debug_line_prefix, "DBG(%lld)> ERR ERR: ", sim_gtime());
9897 sprintf(debug_line_prefix, "DBG(%.0f)> ERR ERR: ", sim_gtime());
9898
9899 buf[bufsize-1] = '\0';
9900 while (1) { /* format passed string, args */
9901 va_start (arglist, fmt);
9902 len = vsnprintf (buf, bufsize-1, fmt, arglist);
9903 va_end (arglist);
9904
9905 /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */
9906
9907 if ((len < 0) || (len >= bufsize-1)) {
9908 if (buf != stackbuf)
9909 free (buf);
9910 bufsize = bufsize * 2;
9911 buf = (char *) malloc (bufsize);
9912 if (buf == NULL) /* out of memory */
9913 return;
9914 buf[bufsize-1] = '\0';
9915 continue;
9916 }
9917 break;
9918 }
9919
9920 for (i = j = 0; i < len; ++i) {
9921 if ('\n' == buf[i]) {
9922 if (i >= j) {
9923 if ((i != j) || (i == 0)) {
9924 if (debug_unterm)
9925 fprintf (sim_deb ? sim_deb : stderr, "%.*s\r\n", i-j, &buf[j]);
9926 else /* print prefix when required */
9927 fprintf (sim_deb ? sim_deb : stderr, "%s%.*s\r\n", debug_line_prefix, i-j, &buf[j]);
9928 }
9929 debug_unterm = 0;
9930 }
9931 j = i + 1;
9932 }
9933 }
9934 if (i > j) {
9935 if (debug_unterm)
9936 fprintf (sim_deb ? sim_deb : stderr, "%.*s", i-j, &buf[j]);
9937 else /* print prefix when required */
9938 fprintf (sim_deb ? sim_deb : stderr, "%s%.*s", debug_line_prefix, i-j, &buf[j]);
9939 }
9940
9941 /* Set unterminated flag for next time */
9942
9943 debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1;
9944 if (buf != stackbuf)
9945 free (buf);
9946 return;
9947 }
9948
9949 void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason)
9950 {
9951
9952 if (sim_deb && (dptr->dctrl & reason)) {
9953 sim_debug (reason, dptr, "%s %s %slen: %08X\n", sim_uname(uptr), txt, position, (unsigned int)len);
9954 if (data && len) {
9955 unsigned int i, same, group, sidx, oidx, ridx, eidx, soff;
9956 char outbuf[80], strbuf[28], rad50buf[36], ebcdicbuf[32];
9957 static char hex[] = "0123456789ABCDEF";
9958 static char rad50[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
9959 static unsigned char ebcdic2ascii[] = {
9960 0000,0001,0002,0003,0234,0011,0206,0177,
9961 0227,0215,0216,0013,0014,0015,0016,0017,
9962 0020,0021,0022,0023,0235,0205,0010,0207,
9963 0030,0031,0222,0217,0034,0035,0036,0037,
9964 0200,0201,0202,0203,0204,0012,0027,0033,
9965 0210,0211,0212,0213,0214,0005,0006,0007,
9966 0220,0221,0026,0223,0224,0225,0226,0004,
9967 0230,0231,0232,0233,0024,0025,0236,0032,
9968 0040,0240,0241,0242,0243,0244,0245,0246,
9969 0247,0250,0133,0056,0074,0050,0053,0041,
9970 0046,0251,0252,0253,0254,0255,0256,0257,
9971 0260,0261,0135,0044,0052,0051,0073,0136,
9972 0055,0057,0262,0263,0264,0265,0266,0267,
9973 0270,0271,0174,0054,0045,0137,0076,0077,
9974 0272,0273,0274,0275,0276,0277,0300,0301,
9975 0302,0140,0072,0043,0100,0047,0075,0042,
9976 0303,0141,0142,0143,0144,0145,0146,0147,
9977 0150,0151,0304,0305,0306,0307,0310,0311,
9978 0312,0152,0153,0154,0155,0156,0157,0160,
9979 0161,0162,0313,0314,0315,0316,0317,0320,
9980 0321,0176,0163,0164,0165,0166,0167,0170,
9981 0171,0172,0322,0323,0324,0325,0326,0327,
9982 0330,0331,0332,0333,0334,0335,0336,0337,
9983 0340,0341,0342,0343,0344,0345,0346,0347,
9984 0173,0101,0102,0103,0104,0105,0106,0107,
9985 0110,0111,0350,0351,0352,0353,0354,0355,
9986 0175,0112,0113,0114,0115,0116,0117,0120,
9987 0121,0122,0356,0357,0360,0361,0362,0363,
9988 0134,0237,0123,0124,0125,0126,0127,0130,
9989 0131,0132,0364,0365,0366,0367,0370,0371,
9990 0060,0061,0062,0063,0064,0065,0066,0067,
9991 0070,0071,0372,0373,0374,0375,0376,0377,
9992 };
9993
9994 for (i=same=0; i<len; i += 16) {
9995 if ((i > 0) && (0 == memcmp (&data[i], &data[i-16], 16))) {
9996 ++same;
9997 continue;
9998 }
9999 if (same > 0) {
10000 sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1);
10001 same = 0;
10002 }
10003 group = (((len - i) > 16) ? 16 : (len - i));
10004 strcpy (ebcdicbuf, (sim_deb_switches & SWMASK ('E')) ? " EBCDIC:" : "");
10005 eidx = strlen(ebcdicbuf);
10006 strcpy (rad50buf, (sim_deb_switches & SWMASK ('D')) ? " RAD50:" : "");
10007 ridx = strlen(rad50buf);
10008 strcpy (strbuf, (sim_deb_switches & (SWMASK ('E') | SWMASK ('D'))) ? "ASCII:" : "");
10009 soff = strlen(strbuf);
10010 for (sidx=oidx=0; sidx<group; ++sidx) {
10011 outbuf[oidx++] = ' ';
10012 outbuf[oidx++] = hex[(data[i+sidx]>>4)&0xf];
10013 outbuf[oidx++] = hex[data[i+sidx]&0xf];
10014 if (sim_isprint (data[i+sidx]))
10015 strbuf[soff+sidx] = data[i+sidx];
10016 else
10017 strbuf[soff+sidx] = '.';
10018 if (ridx && ((sidx&1) == 0)) {
10019 uint16 word = data[i+sidx] + (((uint16)data[i+sidx+1]) << 8);
10020
10021 if (word >= 64000) {
10022 rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10023 rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10024 rad50buf[ridx++] = '|'; /* Invalid RAD-50 character */
10025 }
10026 else {
10027 rad50buf[ridx++] = rad50[word/1600];
10028 rad50buf[ridx++] = rad50[(word/40)%40];
10029 rad50buf[ridx++] = rad50[word%40];
10030 }
10031 }
10032 if (eidx) {
10033 if (sim_isprint (ebcdic2ascii[data[i+sidx]]))
10034 ebcdicbuf[eidx++] = ebcdic2ascii[data[i+sidx]];
10035 else
10036 ebcdicbuf[eidx++] = '.';
10037 }
10038 }
10039 outbuf[oidx] = '\0';
10040 strbuf[soff+sidx] = '\0';
10041 ebcdicbuf[eidx] = '\0';
10042 rad50buf[ridx] = '\0';
10043 sim_debug (reason, dptr, "%04X%-48s %s%s%s\n", i, outbuf, strbuf, ebcdicbuf, rad50buf);
10044 }
10045 if (same > 0) {
10046 sim_debug (reason, dptr, "%04X thru %04X same as above\n", i-(16*same), (unsigned int)(len-1));
10047 }
10048 }
10049 }
10050 }
10051
10052 int Fprintf (FILE *f, const char* fmt, ...)
10053 {
10054 int ret = 0;
10055 va_list args;
10056
10057 va_start (args, fmt);
10058 ret = vfprintf (f, fmt, args);
10059 va_end (args);
10060 return ret;
10061 }
10062
10063 /* Hierarchical help presentation
10064 *
10065 * Device help can be presented hierarchically by calling
10066 *
10067 * t_stat scp_help (FILE *st, DEVICE *dptr,
10068 * UNIT *uptr, int flag, const char *help, char *cptr)
10069 *
10070 * or one of its three cousins from the device HELP routine.
10071 *
10072 * *help is the pointer to the structured help text to be displayed.
10073 *
10074 * The format and usage, and some helper macros can be found in scp_help.h
10075 * If you don't use the macros, it is not necessary to #include "scp_help.h".
10076 *
10077 * Actually, if you don't specify a DEVICE pointer and don't include
10078 * other device references, it can be used for non-device help.
10079 */
10080
10081 #define blankch(x) ((x) == ' ' || (x) == '\t')
10082
10083 typedef struct topic {
10084 uint32 level;
10085 char *title;
10086 char *label;
10087 struct topic *parent;
10088 struct topic **children;
10089 uint32 kids;
10090 char *text;
10091 size_t len;
10092 uint32 flags;
10093 uint32 kidwid;
10094 #define HLP_MAGIC_TOPIC 1
10095 } TOPIC;
10096
10097 static volatile struct {
10098 const char *error;
10099 const char *prox;
10100 size_t block;
10101 size_t line;
10102 } help_where = { "", NULL, 0, 0 };
10103 jmp_buf help_env;
10104 #define FAIL(why,text,here) { help_where.error = #text; help_where.prox = here; longjmp (help_env, (why)); }
10105
10106 /* Add to topic text.
10107 * Expands text buffer as necessary.
10108 */
10109
10110 static void appendText (TOPIC *topic, const char *text, size_t len)
10111 {
10112 char *newt;
10113
10114 if (!len)
10115 return;
10116
10117 newt = (char *)realloc (topic->text, topic->len + len +1);
10118 if (!newt) {
10119 FAIL (SCPE_MEM, No memory, NULL);
10120 }
10121 topic->text = newt;
10122 memcpy (newt + topic->len, text, len);
10123 topic->len +=len;
10124 newt[topic->len] = '\0';
10125 return;
10126 }
10127
10128 /* Release memory held by a topic and its children.
10129 */
10130 static void cleanHelp (TOPIC *topic)
10131 {
10132 TOPIC *child;
10133 size_t i;
10134
10135 free (topic->title);
10136 free (topic->text);
10137 free (topic->label);
10138 for (i = 0; i < topic->kids; i++) {
10139 child = topic->children[i];
10140 cleanHelp (child);
10141 free (child);
10142 }
10143 free (topic->children);
10144 return;
10145 }
10146
10147 /* Build a help tree from a string.
10148 * Handles substitutions, formatting.
10149 */
10150 static TOPIC *buildHelp (TOPIC *topic, DEVICE *dptr,
10151 UNIT *uptr, const char *htext, va_list ap)
10152 {
10153 char *end;
10154 size_t n, ilvl;
10155 #define VSMAX 100
10156 char *vstrings[VSMAX];
10157 size_t vsnum = 0;
10158 char * astrings[VSMAX+1];
10159 size_t asnum = 0;
10160 char *const *hblock;
10161 const char *ep;
10162 t_bool excluded = FALSE;
10163
10164 /* variable arguments consumed table.
10165 * The scheme used allows arguments to be accessed in random
10166 * order, but for portability, all arguments must be char *.
10167 * If you try to violate this, there ARE machines that WILL break.
10168 */
10169
10170 memset (vstrings, 0, sizeof (vstrings));
10171 memset (astrings, 0, sizeof (astrings));
10172 astrings[asnum++] = (char *) htext;
10173
10174 for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
10175 help_where.block = hblock - astrings;
10176 help_where.line = 0;
10177 while (*htext) {
10178 const char *start;
10179
10180 help_where.line++;
10181 if (sim_isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
10182 if (excluded) { /* Excluded topic text */
10183 while (*htext && *htext != '\n')
10184 htext++;
10185 if (*htext)
10186 ++htext;
10187 continue;
10188 }
10189 ilvl = 1;
10190 appendText (topic, " ", 4); /* Basic indentation */
10191 if (*htext == '+') { /* More for each + */
10192 while (*htext == '+') {
10193 ilvl++;
10194 appendText (topic, " ", 4);
10195 htext++;
10196 }
10197 }
10198 while (*htext && *htext != '\n' && sim_isspace (*htext))
10199 htext++;
10200 if (!*htext) /* Empty after removing leading spaces */
10201 break;
10202 start = htext;
10203 while (*htext) { /* Process line for substitutions */
10204 if (*htext == '%') {
10205 appendText (topic, start, htext - start); /* Flush up to escape */
10206 switch (*++htext) { /* Evaluate escape */
10207 case 'U':
10208 if (dptr) {
10209 char buf[129];
10210 n = uptr? uptr - dptr->units: 0;
10211 sprintf (buf, "%s%u", dptr->name, (int)n);
10212 appendText (topic, buf, strlen (buf));
10213 }
10214 break;
10215 case 'D':
10216 appendText (topic, dptr->name, strlen (dptr->name));
10217 break;
10218 case 'S':
10219 appendText (topic, sim_name, strlen (sim_name));
10220 break;
10221 case '%':
10222 appendText (topic, "%", 1);
10223 break;
10224 case '+':
10225 appendText (topic, "+", 1);
10226 break;
10227 default: /* Check for vararg # */
10228 if (sim_isdigit (*htext)) {
10229 n = 0;
10230 while (sim_isdigit (*htext))
10231 n += (n * 10) + (*htext++ - '0');
10232 if (( *htext != 'H' && *htext != 's') ||
10233 n == 0 || n >= VSMAX)
10234 FAIL (SCPE_ARG, Invalid escape, htext);
10235 while (n > vsnum) /* Get arg pointer if not cached */
10236 vstrings[vsnum++] = va_arg (ap, char *);
10237 start = vstrings[n-1]; /* Insert selected string */
10238 if (*htext == 'H') { /* Append as more input */
10239 if (asnum >= VSMAX) {
10240 FAIL (SCPE_ARG, Too many blocks, htext);
10241 }
10242 astrings[asnum++] = (char *)start;
10243 break;
10244 }
10245 ep = start;
10246 while (*ep) {
10247 if (*ep == '\n') {
10248 ep++; /* Segment to \n */
10249 appendText (topic, start, ep - start);
10250 if (*ep) { /* More past \n, indent */
10251 size_t i;
10252 for (i = 0; i < ilvl; i++)
10253 appendText (topic, " ", 4);
10254 }
10255 start = ep;
10256 }
10257 else
10258 ep++;
10259 }
10260 appendText (topic, start, ep-start);
10261 break;
10262 }
10263 FAIL (SCPE_ARG, Invalid escape, htext);
10264 } /* switch (escape) */
10265 start = ++htext;
10266 continue; /* Current line */
10267 } /* if (escape) */
10268 if (*htext == '\n') { /* End of line, append last segment */
10269 htext++;
10270 appendText (topic, start, htext - start);
10271 break; /* To next line */
10272 }
10273 htext++; /* Regular character */
10274 }
10275 continue;
10276 } /* topic text line */
10277 if (sim_isdigit (*htext)) { /* Topic heading */
10278 TOPIC **children;
10279 TOPIC *newt;
10280 char nbuf[100];
10281
10282 n = 0;
10283 start = htext;
10284 while (sim_isdigit (*htext))
10285 n += (n * 10) + (*htext++ - '0');
10286 if ((htext == start) || !n) {
10287 FAIL (SCPE_ARG, Invalid topic heading, htext);
10288 }
10289 if (n <= topic->level) { /* Find level for new topic */
10290 while (n <= topic->level)
10291 topic = topic->parent;
10292 }
10293 else {
10294 if (n > topic->level +1) { /* Skipping down more than 1 */
10295 FAIL (SCPE_ARG, Level not contiguous, htext); /* E.g. 1 3, not reasonable */
10296 }
10297 }
10298 while (*htext && (*htext != '\n') && sim_isspace (*htext))
10299 htext++;
10300 if (!*htext || (*htext == '\n')) { /* Name missing */
10301 FAIL (SCPE_ARG, Missing topic name, htext);
10302 }
10303 start = htext;
10304 while (*htext && (*htext != '\n'))
10305 htext++;
10306 if (start == htext) { /* Name NULL */
10307 FAIL (SCPE_ARG, Null topic name, htext);
10308 }
10309 excluded = FALSE;
10310 if (*start == '?') { /* Conditional topic? */
10311 size_t n = 0;
10312 start++;
10313 while (sim_isdigit (*start)) /* Get param # */
10314 n += (n * 10) + (*start++ - '0');
10315 if (!*start || *start == '\n'|| n == 0 || n >= VSMAX)
10316 FAIL (SCPE_ARG, Invalid parameter number, start);
10317 while (n > vsnum) /* Get arg pointer if not cached */
10318 vstrings[vsnum++] = va_arg (ap, char *);
10319 end = vstrings[n-1]; /* Check for True */
10320 if (!end || !(toupper (*end) == 'T' || *end == '1')) {
10321 excluded = TRUE; /* False, skip topic this time */
10322 if (*htext)
10323 htext++;
10324 continue;
10325 }
10326 }
10327 newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
10328 if (!newt) {
10329 FAIL (SCPE_MEM, No memory, NULL);
10330 }
10331 newt->title = (char *) malloc ((htext - start)+1);
10332 if (!newt->title) {
10333 free (newt);
10334 FAIL (SCPE_MEM, No memory, NULL);
10335 }
10336 memcpy (newt->title, start, htext - start);
10337 newt->title[htext - start] = '\0';
10338 if (*htext)
10339 htext++;
10340
10341 if (newt->title[0] == '$')
10342 newt->flags |= HLP_MAGIC_TOPIC;
10343
10344 children = (TOPIC **) realloc (topic->children,
10345 (topic->kids +1) * sizeof (TOPIC *));
10346 if (!children) {
10347 free (newt->title);
10348 free (newt);
10349 FAIL (SCPE_MEM, No memory, NULL);
10350 }
10351 topic->children = children;
10352 topic->children[topic->kids++] = newt;
10353 newt->level = n;
10354 newt->parent = topic;
10355 n = strlen (newt->title);
10356 if (n > topic->kidwid)
10357 topic->kidwid = n;
10358 sprintf (nbuf, ".%u", topic->kids);
10359 n = strlen (topic->label) + strlen (nbuf) + 1;
10360 newt->label = (char *) malloc (n);
10361 if (!newt->label) {
10362 free (newt->title);
10363 topic->children[topic->kids -1] = NULL;
10364 free (newt);
10365 FAIL (SCPE_MEM, No memory, NULL);
10366 }
10367 sprintf (newt->label, "%s%s", topic->label, nbuf);
10368 topic = newt;
10369 continue;
10370 } /* digits introducing a topic */
10371 if (*htext == ';') { /* Comment */
10372 while (*htext && *htext != '\n')
10373 htext++;
10374 continue;
10375 }
10376 FAIL (SCPE_ARG, Unknown line type, htext); /* Unknown line */
10377 } /* htext not at end */
10378 memset (vstrings, 0, VSMAX * sizeof (char *));
10379 vsnum = 0;
10380 } /* all strings */
10381
10382 return topic;
10383 }
10384
10385 /* Create prompt string - top thru current topic
10386 * Add prompt at end.
10387 */
10388 static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword )
10389 {
10390 char *prefix;
10391 char *newp, *newt;
10392
10393 if (topic->level == 0) {
10394 prefix = (char *) calloc (2,1);
10395 if (!prefix) {
10396 FAIL (SCPE_MEM, No memory, NULL);
10397 }
10398 prefix[0] = '\n';
10399 }
10400 else
10401 prefix = helpPrompt (topic->parent, "", oneword);
10402
10403 newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
10404 strlen (pstring) +1);
10405 if (!newp) {
10406 free (prefix);
10407 FAIL (SCPE_MEM, No memory, NULL);
10408 }
10409 strcpy (newp, prefix);
10410 if (topic->children) {
10411 if (topic->level != 0)
10412 strcat (newp, " ");
10413 newt = (topic->flags & HLP_MAGIC_TOPIC)?
10414 topic->title+1: topic->title;
10415 if (oneword) {
10416 char *np = newp + strlen (newp);
10417 while (*newt) {
10418 *np++ = blankch (*newt)? '_' : *newt;
10419 newt++;
10420 }
10421 *np = '\0';
10422 }
10423 else
10424 strcat (newp, newt);
10425 if (*pstring && *pstring != '?')
10426 strcat (newp, " ");
10427 }
10428 strcat (newp, pstring);
10429 free (prefix);
10430 return newp;
10431 }
10432
10433 static void displayMagicTopic (FILE *st, DEVICE *dptr, TOPIC *topic)
10434 {
10435 char tbuf[CBUFSIZE];
10436 size_t i, skiplines;
10437 #ifdef _WIN32
10438 FILE *tmp;
10439 char *tmpnam;
10440
10441 do {
10442 int fd;
10443 tmpnam = _tempnam (NULL, "simh");
10444 fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
10445 if (fd != -1) {
10446 tmp = _fdopen (fd, "w+");
10447 break;
10448 }
10449 } while (1);
10450 #else
10451 FILE *tmp = tmpfile();
10452 #endif
10453
10454 if (!tmp) {
10455 fprintf (st, "Unable to create temporary file: %s\n", strerror (errno));
10456 return;
10457 }
10458
10459 if (topic->title)
10460 fprintf (st, "%s\n", topic->title+1);
10461
10462 skiplines = 0;
10463 if (!strcmp (topic->title+1, "Registers")) {
10464 fprint_reg_help (tmp, dptr) ;
10465 skiplines = 1;
10466 }
10467 else
10468 if (!strcmp (topic->title+1, "Set commands")) {
10469 fprint_set_help (tmp, dptr);
10470 skiplines = 3;
10471 }
10472 else
10473 if (!strcmp (topic->title+1, "Show commands")) {
10474 fprint_show_help (tmp, dptr);
10475 skiplines = 3;
10476 }
10477 rewind (tmp);
10478
10479 /* Discard leading blank lines/redundant titles */
10480
10481 for (i =0; i < skiplines; i++)
10482 if (fgets (tbuf, sizeof (tbuf), tmp)) {};
10483
10484 while (fgets (tbuf, sizeof (tbuf), tmp)) {
10485 if (tbuf[0] != '\n')
10486 fputs (" ", st);
10487 fputs (tbuf, st);
10488 }
10489 fclose (tmp);
10490 #ifdef _WIN32
10491 remove (tmpnam);
10492 free (tmpnam);
10493 #endif
10494 return;
10495 }
10496 /* Flatten and display help for those who say they prefer it. */
10497
10498 static t_stat displayFlatHelp (FILE *st, DEVICE *dptr,
10499 UNIT *uptr, int32 flag,
10500 TOPIC *topic, va_list ap )
10501 {
10502 size_t i;
10503
10504 if (topic->flags & HLP_MAGIC_TOPIC) {
10505 fprintf (st, "\n%s ", topic->label);
10506 displayMagicTopic (st, dptr, topic);
10507 }
10508 else
10509 fprintf (st, "\n%s %s\n", topic->label, topic->title);
10510
10511 /* Topic text (for magic topics, follows for explanations)
10512 * It's possible/reasonable for a magic topic to have no text.
10513 */
10514
10515 if (topic->text)
10516 fputs (topic->text, st);
10517
10518 for (i = 0; i < topic->kids; i++)
10519 displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
10520
10521 return SCPE_OK;
10522 }
10523
10524 #define HLP_MATCH_AMBIGUOUS (~0u)
10525 #define HLP_MATCH_WILDCARD (~1U)
10526 #define HLP_MATCH_NONE 0
10527 static size_t matchHelpTopicName (TOPIC *topic, const char *token)
10528 {
10529 size_t i, match;
10530 char cbuf[CBUFSIZE], *cptr;
10531
10532 if (!strcmp (token, "*"))
10533 return HLP_MATCH_WILDCARD;
10534
10535 match = 0;
10536 for (i = 0; i < topic->kids; i++) {
10537 strcpy (cbuf,topic->children[i]->title +
10538 ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10539 cptr = cbuf;
10540 while (*cptr) {
10541 if (blankch (*cptr)) {
10542 *cptr++ = '_';
10543 }
10544 else {
10545 *cptr = (char)toupper (*cptr);
10546 cptr++;
10547 }
10548 }
10549 if (!strcmp (cbuf, token)) /* Exact Match */
10550 return i+1;
10551 if (!strncmp (cbuf, token, strlen (token))) {
10552 if (match)
10553 return HLP_MATCH_AMBIGUOUS;
10554 match = i+1;
10555 }
10556 }
10557 return match;
10558 }
10559
10560 /* Main help routine */
10561
10562 t_stat scp_vhelp (FILE *st, DEVICE *dptr,
10563 UNIT *uptr, int32 flag,
10564 const char *help, const char *cptr, va_list ap)
10565 {
10566
10567 TOPIC top;
10568 TOPIC *topic = ⊤
10569 int failed;
10570 size_t match;
10571 size_t i;
10572 const char *p;
10573 t_bool flat_help = FALSE;
10574 char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
10575
10576 static const char attach_help[] = { " ATTACH" };
10577 static const char brief_help[] = { "%s help. Type <CR> to exit, HELP for navigation help" };
10578 static const char onecmd_help[] = { "%s help." };
10579 static const char help_help[] = {
10580
10581 /****|***********************80 column width guide********************************/
10582 " This help command provides hierarchical help. To see more information,\n"
10583 " type an offered subtopic name. To move back a level, just type <CR>.\n"
10584 " To review the current topic/subtopic, type \"?\".\n"
10585 " To view all subtopics, type \"*\".\n"
10586 " To exit help at any time, type EXIT.\n"
10587 };
10588
10589 memset (&top, 0, sizeof(top));
10590 top.parent = ⊤
10591 if ((failed = setjmp (help_env)) != 0) {
10592 fprintf (stderr, "\nHelp was unable to process the help for this device.\n"
10593 "Error in block %u line %u: %s\n"
10594 "%s%*.*s%s"
10595 " Please contact the device maintainer.\n",
10596 (int)help_where.block, (int)help_where.line, help_where.error,
10597 help_where.prox ? "Near '" : "",
10598 help_where.prox ? 15 : 0, help_where.prox ? 15 : 0,
10599 help_where.prox ? help_where.prox : "",
10600 help_where.prox ? "'" : "");
10601 cleanHelp (&top);
10602 return failed;
10603 }
10604
10605 /* Compile string into navigation tree */
10606
10607 /* Root */
10608
10609 if (dptr) {
10610 p = dptr->name;
10611 flat_help = (dptr->flags & DEV_FLATHELP) != 0;
10612 }
10613 else
10614 p = sim_name;
10615 top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
10616 for (i = 0; p[i]; i++ )
10617 top.title[i] = (char)toupper (p[i]);
10618 top.title[i] = '\0';
10619 if (flag & SCP_HELP_ATTACH)
10620 strcpy (top.title+i, attach_help);
10621
10622 top.label = (char *) malloc (sizeof ("1"));
10623 strcpy (top.label, "1");
10624
10625 flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
10626
10627 if (flat_help) {
10628 flag |= SCP_HELP_FLAT;
10629 if (sim_ttisatty())
10630 fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
10631 else
10632 fprintf (st, "%s help.\n", top.title);
10633 }
10634 else
10635 fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
10636
10637 /* Add text and subtopics */
10638
10639 (void) buildHelp (&top, dptr, uptr, help, ap);
10640
10641 /* Go to initial topic if provided */
10642
10643 while (cptr && *cptr) {
10644 cptr = get_glyph (cptr, gbuf, 0);
10645 if (!gbuf[0])
10646 break;
10647 if (!strcmp (gbuf, "HELP")) { /* HELP (about help) */
10648 fprintf (st, "\n");
10649 fputs (help_help, st);
10650 break;
10651 }
10652 match = matchHelpTopicName (topic, gbuf);
10653 if (match == HLP_MATCH_WILDCARD) {
10654 displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10655 cleanHelp (&top);
10656 return SCPE_OK;
10657 }
10658 if (match == HLP_MATCH_AMBIGUOUS) {
10659 fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
10660 break;
10661 }
10662 if (match == HLP_MATCH_NONE) {
10663 fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
10664 break;
10665 }
10666 topic = topic->children[match-1];
10667 }
10668 cptr = NULL;
10669
10670 if (flat_help) {
10671 displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10672 cleanHelp (&top);
10673 return SCPE_OK;
10674 }
10675
10676 /* Interactive loop displaying help */
10677
10678 while (TRUE) {
10679 char *pstring;
10680 const char *prompt[2] = {"? ", "Subtopic? "};
10681
10682 /* Some magic topic names for help from data structures */
10683
10684 if (topic->flags & HLP_MAGIC_TOPIC) {
10685 fputc ('\n', st);
10686 displayMagicTopic (st, dptr, topic);
10687 }
10688 else
10689 fprintf (st, "\n%s\n", topic->title);
10690
10691 /* Topic text (for magic topics, follows for explanations)
10692 * It's possible/reasonable for a magic topic to have no text.
10693 */
10694
10695 if (topic->text)
10696 fputs (topic->text, st);
10697
10698 if (topic->kids) {
10699 size_t w = 0;
10700 char *p;
10701 char tbuf[CBUFSIZE];
10702
10703 fprintf (st, "\n Additional information available:\n\n");
10704 for (i = 0; i < topic->kids; i++) {
10705 strcpy (tbuf, topic->children[i]->title +
10706 ((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
10707 for (p = tbuf; *p; p++) {
10708 if (blankch (*p))
10709 *p = '_';
10710 }
10711 w += 4 + topic->kidwid;
10712 if (w > 80) {
10713 w = 4 + topic->kidwid;
10714 fputc ('\n', st);
10715 }
10716 fprintf (st, " %-*s", topic->kidwid, tbuf);
10717 }
10718 fprintf (st, "\n\n");
10719 if (flag & SCP_HELP_ONECMD) {
10720 pstring = helpPrompt (topic, "", TRUE);
10721 fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
10722 free (pstring);
10723 break;
10724 }
10725 }
10726
10727 if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
10728 break;
10729
10730 reprompt:
10731 if (!cptr || !*cptr) {
10732 if (topic->kids == 0)
10733 topic = topic->parent;
10734 pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
10735
10736 cptr = read_line_p (pstring, cbuf, sizeof (cbuf), stdin);
10737 free (pstring);
10738 if ((cptr != NULL) && /* Got something? */
10739 ((0 == strcmp (cptr, "\x04")) || /* was it a bare ^D? */
10740 (0 == strcmp (cptr, "\x1A")))) /* was it a bare ^Z? */
10741 cptr = NULL; /* These are EOF synonyms */
10742 }
10743
10744 if (!cptr) /* EOF, exit help */
10745 break;
10746
10747 cptr = get_glyph (cptr, gbuf, 0);
10748 if (!strcmp (gbuf, "*")) { /* Wildcard */
10749 displayFlatHelp (st, dptr, uptr, flag, topic, ap);
10750 gbuf[0] = '\0'; /* Displayed all subtopics, go up */
10751 }
10752 if (!gbuf[0]) { /* Blank, up a level */
10753 if (topic->level == 0)
10754 break;
10755 topic = topic->parent;
10756 continue;
10757 }
10758 if (!strcmp (gbuf, "?")) /* ?, repaint current topic */
10759 continue;
10760 if (!strcmp (gbuf, "HELP")) { /* HELP (about help) */
10761 fputs (help_help, st);
10762 goto reprompt;
10763 }
10764 if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT")) /* EXIT (help) */
10765 break;
10766
10767 /* String - look for that topic */
10768
10769 if (!topic->kids) {
10770 fprintf (st, "No additional help at this level.\n");
10771 cptr = NULL;
10772 goto reprompt;
10773 }
10774 match = matchHelpTopicName (topic, gbuf);
10775 if (match == HLP_MATCH_AMBIGUOUS) {
10776 fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
10777 cptr = NULL;
10778 goto reprompt;
10779 }
10780
10781 if (match == HLP_MATCH_NONE) {
10782 fprintf (st, "Help for %s is not available\n", gbuf);
10783 cptr = NULL;
10784 goto reprompt;
10785 }
10786 /* Found, display subtopic */
10787
10788 topic = topic->children[match-1];
10789 }
10790
10791 /* Free structures and return */
10792
10793 cleanHelp (&top);
10794
10795 return SCPE_OK;
10796 }
10797
10798 /* variable argument list shell - most commonly used */
10799
10800 t_stat scp_help (FILE *st, DEVICE *dptr,
10801 UNIT *uptr, int32 flag,
10802 const char *help, const char *cptr, ...)
10803 {
10804 t_stat r;
10805 va_list ap;
10806
10807 va_start (ap, cptr);
10808 r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
10809 va_end (ap);
10810
10811 return r;
10812 }
10813
10814 #if defined(_MSC_VER)
10815 # pragma warning(pop)
10816 #endif
10817