Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* scp.c: simulator control program\r |
2 | \r | |
3 | Copyright (c) 1993-2008, Robert M Supnik\r | |
4 | \r | |
5 | Permission is hereby granted, free of charge, to any person obtaining a\r | |
6 | copy of this software and associated documentation files (the "Software"),\r | |
7 | to deal in the Software without restriction, including without limitation\r | |
8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r | |
9 | and/or sell copies of the Software, and to permit persons to whom the\r | |
10 | Software is furnished to do so, subject to the following conditions:\r | |
11 | \r | |
12 | The above copyright notice and this permission notice shall be included in\r | |
13 | all copies or substantial portions of the Software.\r | |
14 | \r | |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r | |
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r | |
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r | |
18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r | |
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r | |
21 | \r | |
22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r | |
23 | used in advertising or otherwise to promote the sale, use or other dealings\r | |
24 | in this Software without prior written authorization from Robert M Supnik.\r | |
25 | \r | |
26 | 31-Mar-08 RMS Fixed bug in local/global register search (found by Mark Pizzolato)\r | |
27 | Fixed bug in restore of RO units (from Mark Pizzolato)\r | |
28 | 06-Feb-08 RMS Added SET/SHO/NO BR with default argument\r | |
29 | 18-Jul-07 RMS Modified match_ext for VMS ext;version support\r | |
30 | 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all\r | |
31 | Fixed bug in get_sim_opt\r | |
32 | Fixed bug in restoration with changed memory size\r | |
33 | 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing\r | |
34 | 30-Jan-07 RMS Fixed bugs in get_ipaddr\r | |
35 | 17-Oct-06 RMS Added idle support\r | |
36 | 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q\r | |
37 | 14-Jul-06 RMS Added sim_activate_abs\r | |
38 | 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure\r | |
39 | Added -E switch to do_cmd to exit on any error\r | |
40 | 14-Feb-06 RMS Upgraded save file format to V3.5\r | |
41 | 18-Jan-06 RMS Added fprint_stopped_gen\r | |
42 | Added breakpoint spaces\r | |
43 | Fixed unaligned register access (found by Doug Carman)\r | |
44 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r | |
45 | 30-Aug-05 RMS Revised to trim trailing spaces on file names\r | |
46 | 25-Aug-05 RMS Added variable default device support\r | |
47 | 23-Aug-05 RMS Added Linux line history support\r | |
48 | 16-Aug-05 RMS Fixed C++ declaration and cast problems\r | |
49 | 01-May-05 RMS Revised syntax for SET DEBUG (from Dave Bryan)\r | |
50 | 22-Mar-05 JDB Modified DO command to allow ten-level nesting\r | |
51 | 18-Mar-05 RMS Moved DETACH tests into detach_unit (from Dave Bryan)\r | |
52 | Revised interface to fprint_sym, fparse_sym\r | |
53 | 07-Feb-05 RMS Added ASSERT command (from Dave Bryan)\r | |
54 | 02-Feb-05 RMS Fixed bug in global register search\r | |
55 | 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST\r | |
56 | 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file\r | |
57 | 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy\r | |
58 | Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (from Dave Bryan)\r | |
59 | Revised to flush output files after simulation stop (from Dave Bryan)\r | |
60 | 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions\r | |
61 | 27-Sep-04 RMS Fixed comma-separation options in set (from David Bryan)\r | |
62 | 09-Sep-04 RMS Added -p option for RESET\r | |
63 | 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST\r | |
64 | 17-Jul-04 RMS Added ECHO command (from Dave Bryan)\r | |
65 | 12-Jul-04 RMS Fixed problem ATTACHing to read only files\r | |
66 | (found by John Dundas)\r | |
67 | 28-May-04 RMS Added SET/SHOW CONSOLE\r | |
68 | 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2)\r | |
69 | RMS Added debug print routines (from Dave Hittner)\r | |
70 | RMS Added sim_vm_parse_addr and sim_vm_fprint_addr\r | |
71 | RMS Added REG_VMAD support\r | |
72 | RMS Split out libraries\r | |
73 | RMS Moved logging function to SCP\r | |
74 | RMS Exposed step counter interface(s)\r | |
75 | RMS Fixed double logging of SHOW BREAK (found by Mark Pizzolato)\r | |
76 | RMS Fixed implementation of REG_VMIO\r | |
77 | RMS Added SET/SHOW DEBUG, SET/SHOW <device> DEBUG,\r | |
78 | SHOW <device> MODIFIERS, SHOW <device> RADIX\r | |
79 | RMS Changed sim_fsize to take uptr argument\r | |
80 | 29-Dec-03 RMS Added Telnet console output stall support\r | |
81 | 01-Nov-03 RMS Cleaned up implicit detach on attach/restore\r | |
82 | Fixed bug in command line read while logging (found by Mark Pizzolato)\r | |
83 | 01-Sep-03 RMS Fixed end-of-file problem in dep, idep\r | |
84 | Fixed error on trailing spaces in dep, idep\r | |
85 | 15-Jul-03 RMS Removed unnecessary test in reset_all\r | |
86 | 15-Jun-03 RMS Added register flag REG_VMIO\r | |
87 | 25-Apr-03 RMS Added extended address support (V3.0)\r | |
88 | Fixed bug in SAVE (found by Peter Schorn)\r | |
89 | Added u5, u6 fields\r | |
90 | Added logical name support\r | |
91 | 03-Mar-03 RMS Added sim_fsize\r | |
92 | 27-Feb-03 RMS Fixed bug in multiword deposits to files\r | |
93 | 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char*\r | |
94 | Added multiple actions, .ini file support\r | |
95 | Added multiple switch evaluations per line\r | |
96 | 07-Feb-03 RMS Added VMS support for ! (from Mark Pizzolato)\r | |
97 | 01-Feb-03 RMS Added breakpoint table extension, actions\r | |
98 | 14-Jan-03 RMS Added missing function prototypes\r | |
99 | 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support,\r | |
100 | case sensitive SET options\r | |
101 | 22-Dec-02 RMS Added ! (OS command) feature (from Mark Pizzolato)\r | |
102 | 17-Dec-02 RMS Added get_ipaddr\r | |
103 | 02-Dec-02 RMS Added EValuate command\r | |
104 | 16-Nov-02 RMS Fixed bug in register name match algorithm\r | |
105 | 13-Oct-02 RMS Fixed Borland compiler warnings (found by Hans Pufal)\r | |
106 | 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (found by David Hittner)\r | |
107 | Added support for fixed buffer devices\r | |
108 | Added support for Telnet console, removed VT support\r | |
109 | Added help <command>\r | |
110 | Added VMS file optimizations (from Robert Alan Byer)\r | |
111 | Added quiet mode, DO with parameters, GUI interface,\r | |
112 | extensible commands (from Brian Knittel)\r | |
113 | Added device enable/disable commands\r | |
114 | 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from Brian Knittel)\r | |
115 | 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by\r | |
116 | Norm Lastovic)\r | |
117 | 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG\r | |
118 | 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added\r | |
119 | magtape record length error (found by Jonathan Engdahl)\r | |
120 | 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval\r | |
121 | (found by Brian Knittel)\r | |
122 | 10-Feb-02 RMS Fixed problem in clock calibration\r | |
123 | 06-Jan-02 RMS Moved device enable/disable to simulators\r | |
124 | 30-Dec-01 RMS Generalized timer packaged, added circular arrays\r | |
125 | 19-Dec-01 RMS Fixed DO command bug (found by John Dundas)\r | |
126 | 07-Dec-01 RMS Implemented breakpoint package\r | |
127 | 05-Dec-01 RMS Fixed bug in universal register logic\r | |
128 | 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers\r | |
129 | 24-Nov-01 RMS Added unit-based registers\r | |
130 | 16-Nov-01 RMS Added DO command\r | |
131 | 28-Oct-01 RMS Added relative range addressing\r | |
132 | 08-Oct-01 RMS Added SHOW VERSION\r | |
133 | 30-Sep-01 RMS Relaxed attach test in BOOT\r | |
134 | 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod\r | |
135 | 17-Sep-01 RMS Removed multiple console support\r | |
136 | 07-Sep-01 RMS Removed conditional externs on function prototypes\r | |
137 | Added special modifier print\r | |
138 | 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7)\r | |
139 | 18-Jul-01 RMS Minor changes for Macintosh port\r | |
140 | 12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy)\r | |
141 | 27-May-01 RMS Added multiple console support\r | |
142 | 16-May-01 RMS Added logging\r | |
143 | 15-May-01 RMS Added features from Tim Litt\r | |
144 | 12-May-01 RMS Fixed missing return in disable_cmd\r | |
145 | 25-Mar-01 RMS Added ENABLE/DISABLE\r | |
146 | 14-Mar-01 RMS Revised LOAD/DUMP interface (again)\r | |
147 | 05-Mar-01 RMS Added clock calibration support\r | |
148 | 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0\r | |
149 | 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine\r | |
150 | 21-Jan-01 RMS Added relative time\r | |
151 | 22-Dec-00 RMS Fixed find_device for devices ending in numbers\r | |
152 | 08-Dec-00 RMS V2.5a changes\r | |
153 | 30-Oct-00 RMS Added output file option to examine\r | |
154 | 11-Jul-99 RMS V2.5 changes\r | |
155 | 13-Apr-99 RMS Fixed handling of 32b addresses\r | |
156 | 04-Oct-98 RMS V2.4 changes\r | |
157 | 20-Aug-98 RMS Added radix commands\r | |
158 | 05-Jun-98 RMS Fixed bug in ^D handling for UNIX\r | |
159 | 10-Apr-98 RMS Added switches to all commands\r | |
160 | 26-Oct-97 RMS Added search capability\r | |
161 | 25-Jan-97 RMS Revised data types\r | |
162 | 23-Jan-97 RMS Added bi-endian I/O\r | |
163 | 06-Sep-96 RMS Fixed bug in variable length IEXAMINE\r | |
164 | 16-Jun-96 RMS Changed interface to parse/print_sym\r | |
165 | 06-Apr-96 RMS Added error checking in reset all\r | |
166 | 07-Jan-96 RMS Added register buffers in save/restore\r | |
167 | 11-Dec-95 RMS Fixed ordering bug in save/restore\r | |
168 | 22-May-95 RMS Added symbolic input\r | |
169 | 13-Apr-95 RMS Added symbolic printouts\r | |
170 | */\r | |
171 | \r | |
172 | /* Macros and data structures */\r | |
173 | \r | |
174 | #include "sim_defs.h"\r | |
175 | #include "sim_rev.h"\r | |
176 | #include <signal.h>\r | |
177 | #include <ctype.h>\r | |
178 | \r | |
179 | #ifdef HAVE_READLINE\r | |
180 | #include <sim_readline.h>\r | |
181 | #endif\r | |
182 | \r | |
183 | #define EX_D 0 /* deposit */\r | |
184 | #define EX_E 1 /* examine */\r | |
185 | #define EX_I 2 /* interactive */\r | |
186 | #define SCH_OR 0 /* search logicals */\r | |
187 | #define SCH_AND 1\r | |
188 | #define SCH_XOR 2\r | |
189 | #define SCH_E 0 /* search booleans */\r | |
190 | #define SCH_N 1\r | |
191 | #define SCH_G 2\r | |
192 | #define SCH_L 3\r | |
193 | #define SCH_EE 4\r | |
194 | #define SCH_NE 5\r | |
195 | #define SCH_GE 6\r | |
196 | #define SCH_LE 7\r | |
197 | #define SSH_ST 0 /* set */\r | |
198 | #define SSH_SH 1 /* show */\r | |
199 | #define SSH_CL 2 /* clear */\r | |
200 | \r | |
201 | #define DO_NEST_LVL 10 /* DO cmd nesting level */\r | |
202 | #define SRBSIZ 1024 /* save/restore buffer */\r | |
203 | #define SIM_BRK_INILNT 4096 /* bpt tbl length */\r | |
204 | #define SIM_BRK_ALLTYP 0xFFFFFFFF\r | |
205 | #define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \\r | |
206 | sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \\r | |
207 | x = sim_interval\r | |
208 | \r | |
209 | #define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT])\r | |
210 | #define SZ_R(rp) \\r | |
211 | (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT])\r | |
212 | #if defined (USE_INT64)\r | |
213 | #define SZ_LOAD(sz,v,mb,j) \\r | |
214 | if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \\r | |
215 | else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \\r | |
216 | else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \\r | |
217 | else v = *(((t_uint64 *) mb) + ((uint32) j));\r | |
218 | #define SZ_STORE(sz,v,mb,j) \\r | |
219 | if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \\r | |
220 | else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \\r | |
221 | else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \\r | |
222 | else *(((t_uint64 *) mb) + ((uint32) j)) = v;\r | |
223 | #else\r | |
224 | #define SZ_LOAD(sz,v,mb,j) \\r | |
225 | if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \\r | |
226 | else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \\r | |
227 | else v = *(((uint32 *) mb) + ((uint32) j));\r | |
228 | #define SZ_STORE(sz,v,mb,j) \\r | |
229 | if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \\r | |
230 | else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \\r | |
231 | else *(((uint32 *) mb) + ((uint32) j)) = v;\r | |
232 | #endif\r | |
233 | #define GET_SWITCHES(cp) \\r | |
234 | if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW\r | |
235 | #define GET_RADIX(val,dft) \\r | |
236 | if (sim_switches & SWMASK ('O')) val = 8; \\r | |
237 | else if (sim_switches & SWMASK ('D')) val = 10; \\r | |
238 | else if (sim_switches & SWMASK ('H')) val = 16; \\r | |
239 | else val = dft;\r | |
240 | \r | |
241 | /* VM interface */\r | |
242 | \r | |
243 | extern char sim_name[];\r | |
244 | extern DEVICE *sim_devices[];\r | |
245 | extern REG *sim_PC;\r | |
246 | extern const char *sim_stop_messages[];\r | |
247 | extern t_stat sim_instr (void);\r | |
248 | extern t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int32 flag);\r | |
249 | extern int32 sim_emax;\r | |
250 | extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,\r | |
251 | UNIT *uptr, int32 sw);\r | |
252 | extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val,\r | |
253 | int32 sw);\r | |
254 | \r | |
255 | /* The per-simulator init routine is a weak global that defaults to NULL\r | |
256 | The other per-simulator pointers can be overrriden by the init routine */\r | |
257 | \r | |
258 | void (*sim_vm_init) (void);\r | |
259 | char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL;\r | |
260 | void (*sim_vm_post) (t_bool from_scp) = NULL;\r | |
261 | CTAB *sim_vm_cmd = NULL;\r | |
262 | void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL;\r | |
263 | t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr) = NULL;\r | |
264 | \r | |
265 | /* Prototypes */\r | |
266 | \r | |
267 | /* Set and show command processors */\r | |
268 | \r | |
269 | t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
270 | t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
271 | t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
272 | t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
273 | t_stat ssh_break (FILE *st, char *cptr, int32 flg);\r | |
274 | t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr);\r | |
275 | t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
276 | t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
277 | t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
278 | t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
279 | t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
280 | t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
281 | t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
282 | t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
283 | t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
284 | t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
285 | t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);\r | |
286 | t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);\r | |
287 | t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);\r | |
288 | t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg);\r | |
289 | t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag);\r | |
290 | t_stat sim_check_console (int32 sec);\r | |
291 | t_stat sim_save (FILE *sfile);\r | |
292 | t_stat sim_rest (FILE *rfile);\r | |
293 | \r | |
294 | /* Breakpoint package */\r | |
295 | \r | |
296 | t_stat sim_brk_init (void);\r | |
297 | t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act);\r | |
298 | t_stat sim_brk_clr (t_addr loc, int32 sw);\r | |
299 | t_stat sim_brk_clrall (int32 sw);\r | |
300 | t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);\r | |
301 | t_stat sim_brk_showall (FILE *st, int32 sw);\r | |
302 | char *sim_brk_getact (char *buf, int32 size);\r | |
303 | void sim_brk_clract (void);\r | |
304 | void sim_brk_npc (uint32 cnt);\r | |
305 | BRKTAB *sim_brk_new (t_addr loc);\r | |
306 | \r | |
307 | /* Commands support routines */\r | |
308 | \r | |
309 | SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr);\r | |
310 | int32 test_search (t_value val, SCHTAB *schptr);\r | |
311 | char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc);\r | |
312 | int32 get_switches (char *cptr);\r | |
313 | char *get_sim_sw (char *cptr);\r | |
314 | t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);\r | |
315 | t_value get_rval (REG *rptr, uint32 idx);\r | |
316 | void put_rval (REG *rptr, uint32 idx, t_value val);\r | |
317 | t_value strtotv (char *inptr, char **endptr, uint32 radix);\r | |
318 | void fprint_help (FILE *st);\r | |
319 | void fprint_stopped (FILE *st, t_stat r);\r | |
320 | void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);\r | |
321 | char *read_line (char *ptr, int32 size, FILE *stream);\r | |
322 | REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr);\r | |
323 | char *sim_trim_endspc (char *cptr);\r | |
324 | \r | |
325 | /* Forward references */\r | |
326 | \r | |
327 | t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr);\r | |
328 | t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr);\r | |
329 | t_bool qdisable (DEVICE *dptr);\r | |
330 | t_stat attach_err (UNIT *uptr, t_stat stat);\r | |
331 | t_stat detach_all (int32 start_device, t_bool shutdown);\r | |
332 | t_stat assign_device (DEVICE *dptr, char *cptr);\r | |
333 | t_stat deassign_device (DEVICE *dptr);\r | |
334 | t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr);\r | |
335 | t_stat run_boot_prep (void);\r | |
336 | t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,\r | |
337 | REG *lowr, REG *highr, uint32 lows, uint32 highs);\r | |
338 | t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx);\r | |
339 | t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx);\r | |
340 | t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,\r | |
341 | t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);\r | |
342 | t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr);\r | |
343 | t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr,\r | |
344 | UNIT *uptr, int32 dfltinc);\r | |
345 | t_stat step_svc (UNIT *ptr);\r | |
346 | void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[]);\r | |
347 | \r | |
348 | /* Global data */\r | |
349 | \r | |
350 | DEVICE *sim_dflt_dev = NULL;\r | |
351 | UNIT *sim_clock_queue = NULL;\r | |
352 | int32 sim_interval = 0;\r | |
353 | int32 sim_switches = 0;\r | |
354 | FILE *sim_ofile = NULL;\r | |
355 | SCHTAB *sim_schptr = FALSE;\r | |
356 | DEVICE *sim_dfdev = NULL;\r | |
357 | UNIT *sim_dfunit = NULL;\r | |
358 | int32 sim_opt_out = 0;\r | |
359 | int32 sim_is_running = 0;\r | |
360 | uint32 sim_brk_summ = 0;\r | |
361 | uint32 sim_brk_types = 0;\r | |
362 | uint32 sim_brk_dflt = 0;\r | |
363 | char *sim_brk_act = NULL;\r | |
364 | BRKTAB *sim_brk_tab = NULL;\r | |
365 | int32 sim_brk_ent = 0;\r | |
366 | int32 sim_brk_lnt = 0;\r | |
367 | int32 sim_brk_ins = 0;\r | |
368 | t_bool sim_brk_pend[SIM_BKPT_N_SPC] = { FALSE };\r | |
369 | t_addr sim_brk_ploc[SIM_BKPT_N_SPC] = { 0 };\r | |
370 | int32 sim_quiet = 0;\r | |
371 | int32 sim_step = 0;\r | |
372 | static double sim_time;\r | |
373 | static uint32 sim_rtime;\r | |
374 | static int32 noqueue_time;\r | |
375 | volatile int32 stop_cpu = 0;\r | |
376 | t_value *sim_eval = NULL;\r | |
377 | int32 sim_deb_close = 0; /* 1 = close debug */\r | |
378 | FILE *sim_log = NULL; /* log file */\r | |
379 | FILE *sim_deb = NULL; /* debug file */\r | |
380 | static SCHTAB sim_stab;\r | |
381 | \r | |
382 | static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) };\r | |
383 | #if defined USE_INT64\r | |
384 | static const char *sim_si64 = "64b data";\r | |
385 | #else\r | |
386 | static const char *sim_si64 = "32b data";\r | |
387 | #endif\r | |
388 | #if defined USE_ADDR64\r | |
389 | static const char *sim_sa64 = "64b addresses";\r | |
390 | #else\r | |
391 | static const char *sim_sa64 = "32b addresses";\r | |
392 | #endif\r | |
393 | #if defined USE_NETWORK\r | |
394 | static const char *sim_snet = "Ethernet support";\r | |
395 | #else\r | |
396 | static const char *sim_snet = "no Ethernet";\r | |
397 | #endif\r | |
398 | \r | |
399 | /* Tables and strings */\r | |
400 | \r | |
401 | const char save_vercur[] = "V3.5";\r | |
402 | const char save_ver32[] = "V3.2";\r | |
403 | const char save_ver30[] = "V3.0";\r | |
404 | const char *scp_error_messages[] = {\r | |
405 | "Address space exceeded",\r | |
406 | "Unit not attached",\r | |
407 | "I/O error",\r | |
408 | "Checksum error",\r | |
409 | "Format error",\r | |
410 | "Unit not attachable",\r | |
411 | "File open error",\r | |
412 | "Memory exhausted",\r | |
413 | "Invalid argument",\r | |
414 | "Step expired",\r | |
415 | "Unknown command",\r | |
416 | "Read only argument",\r | |
417 | "Command not completed",\r | |
418 | "Simulation stopped",\r | |
419 | "Goodbye",\r | |
420 | "Console input I/O error",\r | |
421 | "Console output I/O error",\r | |
422 | "End of file",\r | |
423 | "Relocation error",\r | |
424 | "No settable parameters",\r | |
425 | "Unit already attached",\r | |
426 | "Hardware timer error",\r | |
427 | "SIGINT handler setup error",\r | |
428 | "Console terminal setup error",\r | |
429 | "Subscript out of range",\r | |
430 | "Command not allowed",\r | |
431 | "Unit disabled",\r | |
432 | "Read only operation not allowed",\r | |
433 | "Invalid switch",\r | |
434 | "Missing value",\r | |
435 | "Too few arguments",\r | |
436 | "Too many arguments",\r | |
437 | "Non-existent device",\r | |
438 | "Non-existent unit",\r | |
439 | "Non-existent register",\r | |
440 | "Non-existent parameter",\r | |
441 | "Nested DO command limit exceeded",\r | |
442 | "Internal error",\r | |
443 | "Invalid magtape record length",\r | |
444 | "Console Telnet connection lost",\r | |
445 | "Console Telnet connection timed out",\r | |
446 | "Console Telnet output stall",\r | |
447 | "Assertion failed"\r | |
448 | };\r | |
449 | \r | |
450 | const size_t size_map[] = { sizeof (int8),\r | |
451 | sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)\r | |
452 | #if defined (USE_INT64)\r | |
453 | , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)\r | |
454 | #endif\r | |
455 | };\r | |
456 | \r | |
457 | const t_value width_mask[] = { 0,\r | |
458 | 0x1, 0x3, 0x7, 0xF,\r | |
459 | 0x1F, 0x3F, 0x7F, 0xFF,\r | |
460 | 0x1FF, 0x3FF, 0x7FF, 0xFFF,\r | |
461 | 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,\r | |
462 | 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,\r | |
463 | 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,\r | |
464 | 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,\r | |
465 | 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF\r | |
466 | #if defined (USE_INT64)\r | |
467 | , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,\r | |
468 | 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,\r | |
469 | 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,\r | |
470 | 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,\r | |
471 | 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,\r | |
472 | 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,\r | |
473 | 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF,\r | |
474 | 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,\r | |
475 | 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF,\r | |
476 | 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF\r | |
477 | #endif\r | |
478 | };\r | |
479 | \r | |
480 | static CTAB cmd_table[] = {\r | |
481 | { "RESET", &reset_cmd, 0,\r | |
482 | "r{eset} {ALL|<device>} reset simulator\n" },\r | |
483 | { "EXAMINE", &exdep_cmd, EX_E,\r | |
484 | "e{xamine} <list> examine memory or registers\n" },\r | |
485 | { "IEXAMINE", &exdep_cmd, EX_E+EX_I,\r | |
486 | "ie{xamine} <list> interactive examine memory or registers\n" },\r | |
487 | { "DEPOSIT", &exdep_cmd, EX_D,\r | |
488 | "d{eposit} <list> <val> deposit in memory or registers\n" },\r | |
489 | { "IDEPOSIT", &exdep_cmd, EX_D+EX_I,\r | |
490 | "id{eposit} <list> interactive deposit in memory or registers\n" },\r | |
491 | { "EVALUATE", &eval_cmd, 0,\r | |
492 | "ev{aluate} <expr> evaluate symbolic expression\n" },\r | |
493 | { "RUN", &run_cmd, RU_RUN,\r | |
494 | "ru{n} {new PC} reset and start simulation\n" },\r | |
495 | { "GO", &run_cmd, RU_GO,\r | |
496 | "go {new PC} start simulation\n" }, \r | |
497 | { "STEP", &run_cmd, RU_STEP,\r | |
498 | "s{tep} {n} simulate n instructions\n" },\r | |
499 | { "CONT", &run_cmd, RU_CONT,\r | |
500 | "c{ont} continue simulation\n" },\r | |
501 | { "BOOT", &run_cmd, RU_BOOT,\r | |
502 | "b{oot} <unit> bootstrap unit\n" },\r | |
503 | { "BREAK", &brk_cmd, SSH_ST,\r | |
504 | "br{eak} <list> set breakpoints\n" },\r | |
505 | { "NOBREAK", &brk_cmd, SSH_CL,\r | |
506 | "nobr{eak} <list> clear breakpoints\n" },\r | |
507 | { "ATTACH", &attach_cmd, 0,\r | |
508 | "at{tach} <unit> <file> attach file to simulated unit\n" },\r | |
509 | { "DETACH", &detach_cmd, 0,\r | |
510 | "det{ach} <unit> detach file from simulated unit\n" },\r | |
511 | { "ASSIGN", &assign_cmd, 0,\r | |
512 | "as{sign} <device> <name> assign logical name for device\n" },\r | |
513 | { "DEASSIGN", &deassign_cmd, 0,\r | |
514 | "dea{ssign} <device> deassign logical name for device\n" },\r | |
515 | { "SAVE", &save_cmd, 0,\r | |
516 | "sa{ve} <file> save simulator to file\n" },\r | |
517 | { "RESTORE", &restore_cmd, 0,\r | |
518 | "rest{ore}|ge{t} <file> restore simulator from file\n" },\r | |
519 | { "GET", &restore_cmd, 0, NULL },\r | |
520 | { "LOAD", &load_cmd, 0,\r | |
521 | "l{oad} <file> {<args>} load binary file\n" },\r | |
522 | { "DUMP", &load_cmd, 1,\r | |
523 | "du(mp) <file> {<args>} dump binary file\n" },\r | |
524 | { "EXIT", &exit_cmd, 0,\r | |
525 | "exi{t}|q{uit}|by{e} exit from simulation\n" },\r | |
526 | { "QUIT", &exit_cmd, 0, NULL },\r | |
527 | { "BYE", &exit_cmd, 0, NULL },\r | |
528 | { "SET", &set_cmd, 0,\r | |
529 | "set console arg{,arg...} set console options\n"\r | |
530 | "set break <list> set breakpoints\n"\r | |
531 | "set nobreak <list> clear breakpoints\n"\r | |
532 | "set throttle x{M|K|%%} set simulation rate\n"\r | |
533 | "set nothrottle set simulation rate to maximum\n"\r | |
534 | "set <dev> OCT|DEC|HEX set device display radix\n"\r | |
535 | "set <dev> ENABLED enable device\n"\r | |
536 | "set <dev> DISABLED disable device\n"\r | |
537 | "set <dev> DEBUG{=arg} set device debug flags\n"\r | |
538 | "set <dev> NODEBUG={arg} clear device debug flags\n"\r | |
539 | "set <dev> arg{,arg...} set device parameters\n"\r | |
540 | "set <unit> ENABLED enable unit\n"\r | |
541 | "set <unit> DISABLED disable unit\n"\r | |
542 | "set <unit> arg{,arg...} set unit parameters\n"\r | |
543 | },\r | |
544 | { "SHOW", &show_cmd, 0,\r | |
545 | "sh{ow} br{eak} <list> show breakpoints\n"\r | |
546 | "sh{ow} con{figuration} show configuration\n"\r | |
547 | "sh{ow} cons{ole} {arg} show console options\n"\r | |
548 | "sh{ow} dev{ices} show devices\n" \r | |
549 | "sh{ow} m{odifiers} show modifiers\n" \r | |
550 | "sh{ow} n{ames} show logical names\n" \r | |
551 | "sh{ow} q{ueue} show event queue\n" \r | |
552 | "sh{ow} ti{me} show simulated time\n"\r | |
553 | "sh{ow} th{rottle} show simulation rate\n" \r | |
554 | "sh{ow} ve{rsion} show simulator version\n" \r | |
555 | "sh{ow} <dev> RADIX show device display radix\n"\r | |
556 | "sh{ow} <dev> DEBUG show device debug flags\n"\r | |
557 | "sh{ow} <dev> MODIFIERS show device modifiers\n"\r | |
558 | "sh{ow} <dev} NAMES show device logical name\n"\r | |
559 | "sh{ow} <dev> {arg,...} show device parameters\n"\r | |
560 | "sh{ow} <unit> {arg,...} show unit parameters\n" },\r | |
561 | { "DO", &do_cmd, 1,\r | |
562 | "do <file> {arg,arg...} process command file\n" },\r | |
563 | { "ECHO", &echo_cmd, 0,\r | |
564 | "echo <string> display <string>\n" },\r | |
565 | { "ASSERT", &assert_cmd, 0,\r | |
566 | "assert {<dev>} <cond> test simulator state against condition\n" },\r | |
567 | { "HELP", &help_cmd, 0,\r | |
568 | "h{elp} type this message\n"\r | |
569 | "h{elp} <command> type help for command\n" },\r | |
570 | { "!", &spawn_cmd, 0,\r | |
571 | "! execute local command interpreter\n"\r | |
572 | "! <command> execute local host command\n" },\r | |
573 | { NULL, NULL, 0 }\r | |
574 | };\r | |
575 | \r | |
576 | /* Main command loop */\r | |
577 | \r | |
578 | int main (int argc, char *argv[])\r | |
579 | {\r | |
580 | char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr;\r | |
581 | int32 i, sw;\r | |
582 | t_bool lookswitch;\r | |
583 | t_stat stat;\r | |
584 | CTAB *cmdp;\r | |
585 | \r | |
586 | #ifdef HAVE_READLINE\r | |
587 | readline_read_history();\r | |
588 | #endif\r | |
589 | \r | |
590 | #if defined (__MWERKS__) && defined (macintosh)\r | |
591 | argc = ccommand (&argv);\r | |
592 | #endif\r | |
593 | \r | |
594 | *cbuf = 0; /* init arg buffer */\r | |
595 | sim_switches = 0; /* init switches */\r | |
596 | lookswitch = TRUE;\r | |
597 | for (i = 1; i < argc; i++) { /* loop thru args */\r | |
598 | if (argv[i] == NULL) continue; /* paranoia */\r | |
599 | if ((*argv[i] == '-') && lookswitch) { /* switch? */\r | |
600 | if ((sw = get_switches (argv[i])) < 0) {\r | |
601 | fprintf (stderr, "Invalid switch %s\n", argv[i]);\r | |
602 | return 0;\r | |
603 | }\r | |
604 | sim_switches = sim_switches | sw;\r | |
605 | }\r | |
606 | else {\r | |
607 | if ((strlen (argv[i]) + strlen (cbuf) + 1) >= CBUFSIZE) {\r | |
608 | fprintf (stderr, "Argument string too long\n");\r | |
609 | return 0;\r | |
610 | }\r | |
611 | if (*cbuf) strcat (cbuf, " "); /* concat args */\r | |
612 | strcat (cbuf, argv[i]);\r | |
613 | lookswitch = FALSE; /* no more switches */\r | |
614 | }\r | |
615 | } /* end for */\r | |
616 | sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */\r | |
617 | \r | |
618 | if (sim_vm_init != NULL) (*sim_vm_init)(); /* call once only */\r | |
619 | sim_finit (); /* init fio package */\r | |
620 | stop_cpu = 0;\r | |
621 | sim_interval = 0;\r | |
622 | sim_time = sim_rtime = 0;\r | |
623 | noqueue_time = 0;\r | |
624 | sim_clock_queue = NULL;\r | |
625 | sim_is_running = 0;\r | |
626 | sim_log = NULL;\r | |
627 | if (sim_emax <= 0) sim_emax = 1;\r | |
628 | sim_timer_init ();\r | |
629 | \r | |
630 | if ((stat = sim_ttinit ()) != SCPE_OK) {\r | |
631 | fprintf (stderr, "Fatal terminal initialization error\n%s\n",\r | |
632 | scp_error_messages[stat - SCPE_BASE]);\r | |
633 | return 0;\r | |
634 | }\r | |
635 | if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) {\r | |
636 | fprintf (stderr, "Unable to allocate examine buffer\n");\r | |
637 | return 0;\r | |
638 | };\r | |
639 | if ((stat = reset_all_p (0)) != SCPE_OK) {\r | |
640 | fprintf (stderr, "Fatal simulator initialization error\n%s\n",\r | |
641 | scp_error_messages[stat - SCPE_BASE]);\r | |
642 | return 0;\r | |
643 | }\r | |
644 | if ((stat = sim_brk_init ()) != SCPE_OK) {\r | |
645 | fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n",\r | |
646 | scp_error_messages[stat - SCPE_BASE]);\r | |
647 | return 0;\r | |
648 | }\r | |
649 | if (!sim_quiet) {\r | |
650 | printf ("\n");\r | |
651 | show_version (stdout, NULL, NULL, 0, NULL);\r | |
652 | }\r | |
653 | if (sim_dflt_dev == NULL) sim_dflt_dev = sim_devices[0]; /* if no default */\r | |
654 | \r | |
655 | if (*cbuf) /* cmd file arg? */\r | |
656 | stat = do_cmd (0, cbuf); /* proc cmd file */\r | |
657 | else if (*argv[0]) { /* sim name arg? */\r | |
658 | char nbuf[PATH_MAX + 7], *np; /* "path.ini" */\r | |
659 | nbuf[0] = '"'; /* starting " */\r | |
660 | strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */\r | |
661 | if (np = match_ext (nbuf, "EXE")) *np = 0; /* remove .exe */\r | |
662 | strcat (nbuf, ".ini\""); /* add .ini" */\r | |
663 | stat = do_cmd (-1, nbuf); /* proc cmd file */\r | |
664 | }\r | |
665 | \r | |
666 | while (stat != SCPE_EXIT) { /* in case exit */\r | |
667 | \r | |
668 | if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */\r | |
669 | printf ("sim> %s\n", cptr); /* echo */\r | |
670 | else if (sim_vm_read != NULL) /* sim routine? */\r | |
671 | cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin);\r | |
672 | \r | |
673 | else\r | |
674 | #ifdef HAVE_READLINE\r | |
675 | cptr = readline_read_line(cbuf,CBUFSIZE,"sim>");\r | |
676 | #else\r | |
677 | printf("sim>");\r | |
678 | cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */\r | |
679 | #endif\r | |
680 | if (cptr == NULL){ /* ignore EOF */\r | |
681 | printf("\r");\r | |
682 | continue;\r | |
683 | }\r | |
684 | if (*cptr == 0) continue; /* ignore blank */\r | |
685 | \r | |
686 | if (sim_log) fprintf (sim_log, "sim> %s\n", cptr); /* log cmd */\r | |
687 | cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */\r | |
688 | sim_switches = 0; /* init switches */\r | |
689 | if (cmdp = find_cmd (gbuf)) /* lookup command */\r | |
690 | stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */\r | |
691 | else stat = SCPE_UNK;\r | |
692 | if (stat >= SCPE_BASE) { /* error? */\r | |
693 | printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);\r | |
694 | if (sim_log) fprintf (sim_log, "%s\n",\r | |
695 | scp_error_messages[stat - SCPE_BASE]);\r | |
696 | }\r | |
697 | if (sim_vm_post != NULL) (*sim_vm_post) (TRUE);\r | |
698 | } /* end while */\r | |
699 | \r | |
700 | detach_all (0, TRUE); /* close files */\r | |
701 | sim_set_deboff (0, NULL); /* close debug */\r | |
702 | sim_set_logoff (0, NULL); /* close log */\r | |
703 | sim_set_notelnet (0, NULL); /* close Telnet */\r | |
704 | sim_ttclose (); /* close console */\r | |
705 | \r | |
706 | /* Write command history back to file */\r | |
707 | #ifdef HAVE_READLINE\r | |
708 | readline_write_history();\r | |
709 | #endif\r | |
710 | \r | |
711 | return 0;\r | |
712 | }\r | |
713 | \r | |
714 | /* Find command routine */\r | |
715 | \r | |
716 | CTAB *find_cmd (char *gbuf)\r | |
717 | {\r | |
718 | CTAB *cmdp = NULL;\r | |
719 | \r | |
720 | if (sim_vm_cmd) cmdp = find_ctab (sim_vm_cmd, gbuf); /* try ext commands */\r | |
721 | if (cmdp == NULL) cmdp = find_ctab (cmd_table, gbuf); /* try regular cmds */\r | |
722 | return cmdp;\r | |
723 | }\r | |
724 | \r | |
725 | /* Exit command */\r | |
726 | \r | |
727 | t_stat exit_cmd (int32 flag, char *cptr)\r | |
728 | {\r | |
729 | return SCPE_EXIT;\r | |
730 | }\r | |
731 | \r | |
732 | /* Help command */\r | |
733 | \r | |
734 | void fprint_help (FILE *st)\r | |
735 | {\r | |
736 | CTAB *cmdp;\r | |
737 | \r | |
738 | for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) {\r | |
739 | if (cmdp->help) fprintf (st, cmdp->help);\r | |
740 | }\r | |
741 | for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) {\r | |
742 | if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name)))\r | |
743 | fprintf (st, cmdp->help);\r | |
744 | }\r | |
745 | return;\r | |
746 | }\r | |
747 | \r | |
748 | t_stat help_cmd (int32 flag, char *cptr)\r | |
749 | {\r | |
750 | char gbuf[CBUFSIZE];\r | |
751 | CTAB *cmdp;\r | |
752 | \r | |
753 | GET_SWITCHES (cptr);\r | |
754 | if (*cptr) {\r | |
755 | cptr = get_glyph (cptr, gbuf, 0);\r | |
756 | if (*cptr) return SCPE_2MARG;\r | |
757 | if (cmdp = find_cmd (gbuf)) {\r | |
758 | printf (cmdp->help);\r | |
759 | if (sim_log) fprintf (sim_log, cmdp->help);\r | |
760 | }\r | |
761 | else return SCPE_ARG;\r | |
762 | }\r | |
763 | else {\r | |
764 | fprint_help (stdout);\r | |
765 | if (sim_log) fprint_help (sim_log);\r | |
766 | }\r | |
767 | return SCPE_OK;\r | |
768 | }\r | |
769 | \r | |
770 | /* Spawn command */\r | |
771 | \r | |
772 | t_stat spawn_cmd (int32 flag, char *cptr)\r | |
773 | {\r | |
774 | if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("SHELL");\r | |
775 | if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("ComSpec");\r | |
776 | #if defined (VMS)\r | |
777 | if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = "SPAWN/INPUT=SYS$COMMAND:";\r | |
778 | #endif\r | |
779 | fflush(stdout); /* flush stdout */\r | |
780 | if (sim_log) fflush (sim_log); /* flush log if enabled */\r | |
781 | system (cptr);\r | |
782 | #if defined (VMS)\r | |
783 | printf ("\n");\r | |
784 | #endif\r | |
785 | \r | |
786 | return SCPE_OK;\r | |
787 | }\r | |
788 | \r | |
789 | /* Echo command */\r | |
790 | \r | |
791 | t_stat echo_cmd (int32 flag, char *cptr)\r | |
792 | {\r | |
793 | puts (cptr);\r | |
794 | if (sim_log) fprintf (sim_log, "%s\n", cptr);\r | |
795 | return SCPE_OK;\r | |
796 | }\r | |
797 | \r | |
798 | /* Do command\r | |
799 | \r | |
800 | Syntax: DO {-E} {-V} <filename> {<arguments>...}\r | |
801 | \r | |
802 | -E causes all command errors to be fatal; without it, only EXIT and ASSERT\r | |
803 | failure will stop a command file.\r | |
804 | \r | |
805 | -V causes commands to be echoed before execution.\r | |
806 | \r | |
807 | Note that SCPE_STEP ("Step expired") is considered a note and not an error\r | |
808 | and so does not abort command execution when using -E.\r | |
809 | \r | |
810 | Inputs:\r | |
811 | flag = caller and nesting level indicator\r | |
812 | fcptr = filename and optional arguments, space-separated\r | |
813 | Outputs:\r | |
814 | status = error status\r | |
815 | \r | |
816 | The "flag" input value indicates the source of the call, as follows:\r | |
817 | \r | |
818 | -1 = initialization file (no error if not found)\r | |
819 | 0 = command line file\r | |
820 | 1 = "DO" command\r | |
821 | >1 = nested "DO" command\r | |
822 | */\r | |
823 | \r | |
824 | #define SCPE_DOFAILED 0040000 /* fail in DO, not subproc */\r | |
825 | \r | |
826 | t_stat do_cmd (int32 flag, char *fcptr)\r | |
827 | {\r | |
828 | char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE], *c, quote, *do_arg[10];\r | |
829 | FILE *fpin;\r | |
830 | CTAB *cmdp;\r | |
831 | int32 echo, nargs, errabort;\r | |
832 | t_bool interactive, isdo, staying;\r | |
833 | t_stat stat;\r | |
834 | char *ocptr;\r | |
835 | \r | |
836 | stat = SCPE_OK;\r | |
837 | staying = TRUE;\r | |
838 | interactive = (flag > 0); /* issued interactively? */\r | |
839 | if (interactive) { GET_SWITCHES (fcptr); } /* get switches */\r | |
840 | echo = sim_switches & SWMASK ('V'); /* -v means echo */\r | |
841 | errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */\r | |
842 | \r | |
843 | c = fcptr;\r | |
844 | for (nargs = 0; nargs < 10; ) { /* extract arguments */\r | |
845 | while (isspace (*c)) c++; /* skip blanks */\r | |
846 | if (*c == 0) break; /* all done */\r | |
847 | if (*c == '\'' || *c == '"') quote = *c++; /* quoted string? */\r | |
848 | else quote = 0;\r | |
849 | do_arg[nargs++] = c; /* save start */\r | |
850 | while (*c && (quote? (*c != quote): !isspace (*c))) c++;\r | |
851 | if (*c) *c++ = 0; /* term at quote/spc */\r | |
852 | } /* end for */\r | |
853 | if (nargs <= 0) return SCPE_2FARG; /* need at least 1 */\r | |
854 | if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */\r | |
855 | if (flag == 0) /* cmd line file? */\r | |
856 | fprintf (stderr, "Can't open file %s\n", do_arg[0]);\r | |
857 | if (flag > 1)\r | |
858 | return SCPE_OPENERR | SCPE_DOFAILED; /* return failure with flag */\r | |
859 | else\r | |
860 | return SCPE_OPENERR; /* return failure */\r | |
861 | }\r | |
862 | if (flag < 1) flag = 1; /* start at level 1 */\r | |
863 | \r | |
864 | do {\r | |
865 | ocptr = cptr = sim_brk_getact (cbuf, CBUFSIZE); /* get bkpt action */\r | |
866 | if (!ocptr) /* no pending action? */\r | |
867 | ocptr = cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */\r | |
868 | sub_args (cbuf, gbuf, CBUFSIZE, nargs, do_arg); /* substitute args */\r | |
869 | if (cptr == NULL) { /* EOF? */\r | |
870 | stat = SCPE_OK; /* set good return */\r | |
871 | break;\r | |
872 | }\r | |
873 | if (*cptr == 0) continue; /* ignore blank */\r | |
874 | if (echo) printf("do> %s\n", cptr); /* echo if -v */\r | |
875 | if (echo && sim_log) fprintf (sim_log, "do> %s\n", cptr);\r | |
876 | cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */\r | |
877 | sim_switches = 0; /* init switches */\r | |
878 | isdo = FALSE;\r | |
879 | if (cmdp = find_cmd (gbuf)) { /* lookup command */\r | |
880 | isdo = (cmdp->action == &do_cmd);\r | |
881 | if (isdo) { /* DO command? */\r | |
882 | if (flag >= DO_NEST_LVL) stat = SCPE_NEST; /* nest too deep? */\r | |
883 | else stat = do_cmd (flag + 1, cptr); /* exec DO cmd */\r | |
884 | }\r | |
885 | else stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */\r | |
886 | }\r | |
887 | else stat = SCPE_UNK; /* bad cmd given */\r | |
888 | staying = (stat != SCPE_EXIT) && /* decide if staying */\r | |
889 | (stat != SCPE_AFAIL) &&\r | |
890 | (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP));\r | |
891 | if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */\r | |
892 | (stat != SCPE_STEP)) {\r | |
893 | if (!echo && !sim_quiet && /* report if not echoing */\r | |
894 | (!isdo || (stat & SCPE_DOFAILED))) { /* and not from DO return */\r | |
895 | printf("%s> %s\n", do_arg[0], ocptr);\r | |
896 | if (sim_log)\r | |
897 | fprintf (sim_log, "%s> %s\n", do_arg[0], ocptr);\r | |
898 | }\r | |
899 | stat = stat & ~SCPE_DOFAILED; /* remove possible flag */\r | |
900 | }\r | |
901 | if ((staying || !interactive) && /* report error if staying */\r | |
902 | (stat >= SCPE_BASE)) { /* or in cmdline file */\r | |
903 | printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);\r | |
904 | if (sim_log) fprintf (sim_log, "%s\n",\r | |
905 | scp_error_messages[stat - SCPE_BASE]);\r | |
906 | }\r | |
907 | if (sim_vm_post != NULL) (*sim_vm_post) (TRUE);\r | |
908 | } while (staying);\r | |
909 | \r | |
910 | fclose (fpin); /* close file */\r | |
911 | return stat;\r | |
912 | }\r | |
913 | \r | |
914 | /* Substitute_args - replace %n tokens in 'instr' with the do command's arguments\r | |
915 | \r | |
916 | Calling sequence\r | |
917 | instr = input string\r | |
918 | tmpbuf = temp buffer\r | |
919 | maxstr = min (len (instr), len (tmpbuf))\r | |
920 | nargs = number of arguments\r | |
921 | do_arg[10] = arguments\r | |
922 | */\r | |
923 | \r | |
924 | void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[])\r | |
925 | {\r | |
926 | char *ip, *op, *ap, *oend = tmpbuf + maxstr - 2;\r | |
927 | \r | |
928 | for (ip = instr, op = tmpbuf; *ip && (op < oend); ) {\r | |
929 | if ((*ip == '\\') && (ip[1] == '%')) { /* \% = literal % */\r | |
930 | ip++; /* skip \ */\r | |
931 | if (*ip) *op++ = *ip++; /* copy next */\r | |
932 | }\r | |
933 | else if ((*ip == '%') && /* %n = sub */\r | |
934 | ((ip[1] >= '1') && (ip[1] <= ('0'+ nargs - 1)))) {\r | |
935 | ap = do_arg[ip[1] - '0'];\r | |
936 | ip = ip + 2;\r | |
937 | while (*ap && (op < oend)) *op++ = *ap++; /* copy the argument */\r | |
938 | }\r | |
939 | else *op++ = *ip++; /* literal character */\r | |
940 | }\r | |
941 | *op = 0; /* term buffer */\r | |
942 | strcpy (instr, tmpbuf);\r | |
943 | return;\r | |
944 | }\r | |
945 | \r | |
946 | /* Assert command\r | |
947 | \r | |
948 | Syntax: ASSERT {<dev>} <reg>{<logical-op><value>}<conditional-op><value>\r | |
949 | \r | |
950 | If <dev> is not specified, CPU is assumed. <value> is expressed in the radix\r | |
951 | specified for <reg>. <logical-op> and <conditional-op> are the same as that\r | |
952 | allowed for examine and deposit search specifications. */\r | |
953 | \r | |
954 | t_stat assert_cmd (int32 flag, char *cptr)\r | |
955 | {\r | |
956 | char gbuf[CBUFSIZE], *gptr, *aptr, *tptr;\r | |
957 | REG *rptr;\r | |
958 | uint32 idx;\r | |
959 | t_value val;\r | |
960 | t_stat r;\r | |
961 | \r | |
962 | aptr = cptr; /* save assertion */\r | |
963 | cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, cptr, &r); /* get sw, default */\r | |
964 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
965 | cptr = get_glyph (cptr, gbuf, 0); /* get register */\r | |
966 | rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */\r | |
967 | if (!rptr) return SCPE_NXREG; /* not there */\r | |
968 | if (*gptr == '[') { /* subscript? */\r | |
969 | if (rptr->depth <= 1) return SCPE_ARG; /* array register? */\r | |
970 | idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */\r | |
971 | if ((gptr == tptr) || (*tptr++ != ']')) return SCPE_ARG;\r | |
972 | gptr = tptr; /* update */\r | |
973 | }\r | |
974 | else idx = 0; /* not array */\r | |
975 | if (idx >= rptr->depth) return SCPE_SUB; /* validate subscript */\r | |
976 | if (*gptr != 0) get_glyph (gptr, gbuf, 0); /* more? must be search */\r | |
977 | else {\r | |
978 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
979 | cptr = get_glyph (cptr, gbuf, 0); /* get search cond */\r | |
980 | }\r | |
981 | if (*cptr != 0) return SCPE_2MARG; /* must be done */\r | |
982 | if (!get_search (gbuf, rptr->radix, &sim_stab)) /* parse condition */\r | |
983 | return SCPE_MISVAL;\r | |
984 | val = get_rval (rptr, idx); /* get register value */\r | |
985 | if (test_search (val, &sim_stab)) return SCPE_OK; /* test condition */\r | |
986 | return SCPE_AFAIL; /* condition fails */\r | |
987 | }\r | |
988 | \r | |
989 | /* Set command */\r | |
990 | \r | |
991 | t_stat set_cmd (int32 flag, char *cptr)\r | |
992 | {\r | |
993 | int32 lvl;\r | |
994 | t_stat r;\r | |
995 | char gbuf[CBUFSIZE], *cvptr, *svptr;\r | |
996 | DEVICE *dptr;\r | |
997 | UNIT *uptr;\r | |
998 | MTAB *mptr;\r | |
999 | CTAB *gcmdp;\r | |
1000 | C1TAB *ctbr, *glbr;\r | |
1001 | \r | |
1002 | static CTAB set_glob_tab[] = {\r | |
1003 | { "CONSOLE", &sim_set_console, 0 },\r | |
1004 | { "BREAK", &brk_cmd, SSH_ST },\r | |
1005 | { "TELNET", &sim_set_telnet, 0 }, /* deprecated */\r | |
1006 | { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */\r | |
1007 | { "LOG", &sim_set_logon, 0 }, /* deprecated */\r | |
1008 | { "NOLOG", &sim_set_logoff, 0 }, /* deprecated */\r | |
1009 | { "DEBUG", &sim_set_debon, 0 }, /* deprecated */\r | |
1010 | { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */\r | |
1011 | { "THROTTLE", &sim_set_throt, 1 },\r | |
1012 | { "NOTHROTTLE", &sim_set_throt, 0 },\r | |
1013 | { NULL, NULL, 0 }\r | |
1014 | };\r | |
1015 | \r | |
1016 | static C1TAB set_dev_tab[] = {\r | |
1017 | { "OCTAL", &set_dev_radix, 8 },\r | |
1018 | { "DECIMAL", &set_dev_radix, 10 },\r | |
1019 | { "HEX", &set_dev_radix, 16 },\r | |
1020 | { "ENABLED", &set_dev_enbdis, 1 },\r | |
1021 | { "DISABLED", &set_dev_enbdis, 0 },\r | |
1022 | { "DEBUG", &set_dev_debug, 1 },\r | |
1023 | { "NODEBUG", &set_dev_debug, 0 },\r | |
1024 | { NULL, NULL, 0 }\r | |
1025 | };\r | |
1026 | \r | |
1027 | static C1TAB set_unit_tab[] = {\r | |
1028 | { "ENABLED", &set_unit_enbdis, 1 },\r | |
1029 | { "DISABLED", &set_unit_enbdis, 0 },\r | |
1030 | { NULL, NULL, 0 }\r | |
1031 | };\r | |
1032 | \r | |
1033 | GET_SWITCHES (cptr); /* get switches */\r | |
1034 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1035 | cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */\r | |
1036 | \r | |
1037 | if (dptr = find_dev (gbuf)) { /* device match? */\r | |
1038 | uptr = dptr->units; /* first unit */\r | |
1039 | ctbr = set_dev_tab; /* global table */\r | |
1040 | lvl = MTAB_VDV; /* device match */\r | |
1041 | }\r | |
1042 | else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */\r | |
1043 | if (uptr == NULL) return SCPE_NXUN; /* invalid unit */\r | |
1044 | ctbr = set_unit_tab; /* global table */\r | |
1045 | lvl = MTAB_VUN; /* unit match */\r | |
1046 | }\r | |
1047 | else if (gcmdp = find_ctab (set_glob_tab, gbuf)) /* global? */\r | |
1048 | return gcmdp->action (gcmdp->arg, cptr); /* do the rest */\r | |
1049 | else return SCPE_NXDEV; /* no match */\r | |
1050 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1051 | \r | |
1052 | while (*cptr != 0) { /* do all mods */\r | |
1053 | cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */\r | |
1054 | if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */\r | |
1055 | for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) {\r | |
1056 | if ((mptr->mstring) && /* match string */\r | |
1057 | (MATCH_CMD (gbuf, mptr->mstring) == 0)) { /* matches option? */\r | |
1058 | if (mptr->mask & MTAB_XTD) { /* extended? */\r | |
1059 | if ((lvl & mptr->mask) == 0) return SCPE_ARG;\r | |
1060 | if ((lvl & MTAB_VUN) && (uptr->flags & UNIT_DIS))\r | |
1061 | return SCPE_UDIS; /* unit disabled? */\r | |
1062 | if (mptr->valid) { /* validation rtn? */\r | |
1063 | if (cvptr && (mptr->mask & MTAB_NC))\r | |
1064 | get_glyph_nc (svptr, gbuf, ',');\r | |
1065 | r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc);\r | |
1066 | if (r != SCPE_OK) return r;\r | |
1067 | }\r | |
1068 | else if (!mptr->desc) break; /* value desc? */\r | |
1069 | else if (mptr->mask & MTAB_VAL) { /* take a value? */\r | |
1070 | if (!cvptr) return SCPE_MISVAL; /* none? error */\r | |
1071 | r = dep_reg (0, cvptr, (REG *) mptr->desc, 0);\r | |
1072 | if (r != SCPE_OK) return r;\r | |
1073 | }\r | |
1074 | else if (cvptr) return SCPE_ARG; /* = value? */\r | |
1075 | else *((int32 *) mptr->desc) = mptr->match;\r | |
1076 | } /* end if xtd */\r | |
1077 | else { /* old style */\r | |
1078 | if (cvptr) return SCPE_ARG; /* = value? */\r | |
1079 | if (uptr->flags & UNIT_DIS) /* disabled? */\r | |
1080 | return SCPE_UDIS;\r | |
1081 | if ((mptr->valid) && ((r = mptr->valid\r | |
1082 | (uptr, mptr->match, cvptr, mptr->desc))\r | |
1083 | != SCPE_OK)) return r; /* invalid? */\r | |
1084 | uptr->flags = (uptr->flags & ~(mptr->mask)) |\r | |
1085 | (mptr->match & mptr->mask); /* set new value */\r | |
1086 | } /* end else xtd */\r | |
1087 | break; /* terminate for */\r | |
1088 | } /* end if match */\r | |
1089 | } /* end for */\r | |
1090 | if (!mptr || (mptr->mask == 0)) { /* no match? */\r | |
1091 | if (glbr = find_c1tab (ctbr, gbuf)) { /* global match? */\r | |
1092 | r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */\r | |
1093 | if (r != SCPE_OK) return r;\r | |
1094 | }\r | |
1095 | else if (!dptr->modifiers) return SCPE_NOPARAM; /* no modifiers? */\r | |
1096 | else return SCPE_NXPAR;\r | |
1097 | } /* end if no mat */\r | |
1098 | } /* end while */\r | |
1099 | return SCPE_OK; /* done all */\r | |
1100 | }\r | |
1101 | \r | |
1102 | /* Match CTAB/CTAB1 name */\r | |
1103 | \r | |
1104 | CTAB *find_ctab (CTAB *tab, char *gbuf)\r | |
1105 | {\r | |
1106 | for (; tab->name != NULL; tab++) {\r | |
1107 | if (MATCH_CMD (gbuf, tab->name) == 0) return tab;\r | |
1108 | }\r | |
1109 | return NULL;\r | |
1110 | }\r | |
1111 | \r | |
1112 | C1TAB *find_c1tab (C1TAB *tab, char *gbuf)\r | |
1113 | {\r | |
1114 | for (; tab->name != NULL; tab++) {\r | |
1115 | if (MATCH_CMD (gbuf, tab->name) == 0) return tab;\r | |
1116 | }\r | |
1117 | return NULL;\r | |
1118 | }\r | |
1119 | \r | |
1120 | /* Set device data radix routine */\r | |
1121 | \r | |
1122 | t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1123 | {\r | |
1124 | if (cptr) return SCPE_ARG;\r | |
1125 | dptr->dradix = flag & 037;\r | |
1126 | return SCPE_OK;\r | |
1127 | }\r | |
1128 | \r | |
1129 | /* Set device enabled/disabled routine */\r | |
1130 | \r | |
1131 | t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1132 | {\r | |
1133 | UNIT *up;\r | |
1134 | uint32 i;\r | |
1135 | \r | |
1136 | if (cptr) return SCPE_ARG;\r | |
1137 | if ((dptr->flags & DEV_DISABLE) == 0) return SCPE_NOFNC;/* allowed? */\r | |
1138 | if (flag) { /* enable? */\r | |
1139 | if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */\r | |
1140 | return SCPE_OK;\r | |
1141 | dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */\r | |
1142 | }\r | |
1143 | else {\r | |
1144 | if (dptr->flags & DEV_DIS) return SCPE_OK; /* already dsb? ok */\r | |
1145 | for (i = 0; i < dptr->numunits; i++) { /* check units */\r | |
1146 | up = (dptr->units) + i; /* att or active? */\r | |
1147 | if ((up->flags & UNIT_ATT) || sim_is_active (up))\r | |
1148 | return SCPE_NOFNC; /* can't do it */\r | |
1149 | }\r | |
1150 | dptr->flags = dptr->flags | DEV_DIS; /* disable */\r | |
1151 | }\r | |
1152 | if (dptr->reset) return dptr->reset (dptr); /* reset device */\r | |
1153 | else return SCPE_OK;\r | |
1154 | }\r | |
1155 | \r | |
1156 | /* Set unit enabled/disabled routine */\r | |
1157 | \r | |
1158 | t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1159 | {\r | |
1160 | if (cptr) return SCPE_ARG;\r | |
1161 | if (!(uptr->flags & UNIT_DISABLE)) return SCPE_NOFNC; /* allowed? */\r | |
1162 | if (flag) uptr->flags = uptr->flags & ~UNIT_DIS; /* enb? enable */\r | |
1163 | else {\r | |
1164 | if ((uptr->flags & UNIT_ATT) || /* dsb */\r | |
1165 | sim_is_active (uptr)) return SCPE_NOFNC; /* more tests */\r | |
1166 | uptr->flags = uptr->flags | UNIT_DIS; /* disable */\r | |
1167 | }\r | |
1168 | return SCPE_OK;\r | |
1169 | }\r | |
1170 | \r | |
1171 | /* Set device debug enabled/disabled routine */\r | |
1172 | \r | |
1173 | t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1174 | {\r | |
1175 | char gbuf[CBUFSIZE];\r | |
1176 | DEBTAB *dep;\r | |
1177 | \r | |
1178 | if ((dptr->flags & DEV_DEBUG) == 0) return SCPE_NOFNC;\r | |
1179 | if (cptr == NULL) { /* no arguments? */\r | |
1180 | dptr->dctrl = flag; /* disable/enable w/o table */\r | |
1181 | if (flag && dptr->debflags) { /* enable with table? */\r | |
1182 | for (dep = dptr->debflags; dep->name != NULL; dep++)\r | |
1183 | dptr->dctrl = dptr->dctrl | dep->mask; /* set all */\r | |
1184 | }\r | |
1185 | return SCPE_OK;\r | |
1186 | }\r | |
1187 | if (dptr->debflags == NULL) return SCPE_ARG; /* must have table */\r | |
1188 | while (*cptr) {\r | |
1189 | cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */\r | |
1190 | for (dep = dptr->debflags; dep->name != NULL; dep++) {\r | |
1191 | if (strcmp (dep->name, gbuf) == 0) { /* match? */\r | |
1192 | if (flag) dptr->dctrl = dptr->dctrl | dep->mask;\r | |
1193 | else dptr->dctrl = dptr->dctrl & ~dep->mask;\r | |
1194 | break;\r | |
1195 | }\r | |
1196 | } /* end for */\r | |
1197 | if (dep->mask == 0) return SCPE_ARG; /* no match? */\r | |
1198 | } /* end while */\r | |
1199 | return SCPE_OK;\r | |
1200 | }\r | |
1201 | \r | |
1202 | /* Show command */\r | |
1203 | \r | |
1204 | t_stat show_cmd (int32 flag, char *cptr)\r | |
1205 | {\r | |
1206 | t_stat r;\r | |
1207 | \r | |
1208 | cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r); /* get sw, ofile */\r | |
1209 | if (!cptr) return r; /* error? */\r | |
1210 | if (sim_ofile) { /* output file? */\r | |
1211 | r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */\r | |
1212 | fclose (sim_ofile);\r | |
1213 | }\r | |
1214 | else {\r | |
1215 | r = show_cmd_fi (stdout, flag, cptr); /* no, stdout, log */\r | |
1216 | if (sim_log) show_cmd_fi (sim_log, flag, cptr);\r | |
1217 | }\r | |
1218 | return r;\r | |
1219 | }\r | |
1220 | \r | |
1221 | t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr)\r | |
1222 | {\r | |
1223 | int32 lvl;\r | |
1224 | char gbuf[CBUFSIZE], *cvptr;\r | |
1225 | DEVICE *dptr;\r | |
1226 | UNIT *uptr;\r | |
1227 | MTAB *mptr;\r | |
1228 | SHTAB *shtb, *shptr;\r | |
1229 | \r | |
1230 | static SHTAB show_glob_tab[] = {\r | |
1231 | { "CONFIGURATION", &show_config, 0 },\r | |
1232 | { "DEVICES", &show_config, 1 },\r | |
1233 | { "QUEUE", &show_queue, 0 },\r | |
1234 | { "TIME", &show_time, 0 },\r | |
1235 | { "MODIFIERS", &show_mod_names, 0 },\r | |
1236 | { "NAMES", &show_log_names, 0 },\r | |
1237 | { "VERSION", &show_version, 1 },\r | |
1238 | { "CONSOLE", &sim_show_console, 0 },\r | |
1239 | { "BREAK", &show_break, 0 },\r | |
1240 | { "LOG", &sim_show_log, 0 }, /* deprecated */\r | |
1241 | { "TELNET", &sim_show_telnet, 0 }, /* deprecated */\r | |
1242 | { "DEBUG", &sim_show_debug, 0 }, /* deprecated */\r | |
1243 | { "THROTTLE", &sim_show_throt, 0 },\r | |
1244 | { NULL, NULL, 0 }\r | |
1245 | };\r | |
1246 | \r | |
1247 | static SHTAB show_dev_tab[] = {\r | |
1248 | { "RADIX", &show_dev_radix, 0 },\r | |
1249 | { "DEBUG", &show_dev_debug, 0 },\r | |
1250 | { "MODIFIERS", &show_dev_modifiers, 0 },\r | |
1251 | { "NAMES", &show_dev_logicals, 0 },\r | |
1252 | { NULL, NULL, 0 }\r | |
1253 | };\r | |
1254 | \r | |
1255 | static SHTAB show_unit_tab[] = {\r | |
1256 | { NULL, NULL, 0 }\r | |
1257 | };\r | |
1258 | \r | |
1259 | GET_SWITCHES (cptr); /* get switches */\r | |
1260 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1261 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1262 | if (shptr = find_shtab (show_glob_tab, gbuf)) /* global? */\r | |
1263 | return shptr->action (ofile, NULL, NULL, shptr->arg, cptr);\r | |
1264 | \r | |
1265 | if (dptr = find_dev (gbuf)) { /* device match? */\r | |
1266 | uptr = dptr->units; /* first unit */\r | |
1267 | shtb = show_dev_tab; /* global table */\r | |
1268 | lvl = MTAB_VDV; /* device match */\r | |
1269 | }\r | |
1270 | else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */\r | |
1271 | if (uptr == NULL) return SCPE_NXUN; /* invalid unit */\r | |
1272 | if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */\r | |
1273 | shtb = show_unit_tab; /* global table */\r | |
1274 | lvl = MTAB_VUN; /* unit match */\r | |
1275 | }\r | |
1276 | else return SCPE_NXDEV; /* no match */\r | |
1277 | \r | |
1278 | if (*cptr == 0) { /* now eol? */\r | |
1279 | return (lvl == MTAB_VDV)?\r | |
1280 | show_device (ofile, dptr, 0):\r | |
1281 | show_unit (ofile, dptr, uptr, -1);\r | |
1282 | }\r | |
1283 | if (dptr->modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */\r | |
1284 | \r | |
1285 | while (*cptr != 0) { /* do all mods */\r | |
1286 | cptr = get_glyph (cptr, gbuf, ','); /* get modifier */\r | |
1287 | if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */\r | |
1288 | for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {\r | |
1289 | if (((mptr->mask & MTAB_XTD)? /* right level? */\r | |
1290 | (mptr->mask & lvl): (MTAB_VUN & lvl)) && \r | |
1291 | ((mptr->disp && mptr->pstring && /* named disp? */\r | |
1292 | (MATCH_CMD (gbuf, mptr->pstring) == 0)) ||\r | |
1293 | ((mptr->mask & MTAB_VAL) && /* named value? */\r | |
1294 | mptr->mstring &&\r | |
1295 | (MATCH_CMD (gbuf, mptr->mstring) == 0)))) {\r | |
1296 | if (cvptr && !(mptr->mask & MTAB_SHP)) return SCPE_ARG;\r | |
1297 | show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1);\r | |
1298 | break;\r | |
1299 | } /* end if */\r | |
1300 | } /* end for */\r | |
1301 | if (mptr->mask == 0) { /* no match? */\r | |
1302 | if (shptr = find_shtab (shtb, gbuf)) /* global match? */\r | |
1303 | shptr->action (ofile, dptr, uptr, shptr->arg, cptr);\r | |
1304 | else return SCPE_ARG;\r | |
1305 | } /* end if */\r | |
1306 | } /* end while */\r | |
1307 | return SCPE_OK;\r | |
1308 | }\r | |
1309 | \r | |
1310 | SHTAB *find_shtab (SHTAB *tab, char *gbuf)\r | |
1311 | {\r | |
1312 | for (; tab->name != NULL; tab++) {\r | |
1313 | if (MATCH_CMD (gbuf, tab->name) == 0) return tab;\r | |
1314 | }\r | |
1315 | return NULL;\r | |
1316 | }\r | |
1317 | \r | |
1318 | /* Show device and unit */\r | |
1319 | \r | |
1320 | t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)\r | |
1321 | {\r | |
1322 | uint32 j, udbl, ucnt;\r | |
1323 | UNIT *uptr;\r | |
1324 | \r | |
1325 | fprintf (st, "%s", sim_dname (dptr)); /* print dev name */\r | |
1326 | if (qdisable (dptr)) { /* disabled? */\r | |
1327 | fprintf (st, ", disabled\n");\r | |
1328 | return SCPE_OK;\r | |
1329 | }\r | |
1330 | for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */\r | |
1331 | uptr = dptr->units + j;\r | |
1332 | if (uptr->flags & UNIT_DISABLE) udbl++;\r | |
1333 | if (!(uptr->flags & UNIT_DIS)) ucnt++;\r | |
1334 | }\r | |
1335 | show_all_mods (st, dptr, dptr->units, MTAB_VDV); /* show dev mods */\r | |
1336 | if (dptr->numunits == 0) fprintf (st, "\n");\r | |
1337 | else {\r | |
1338 | if (udbl && (ucnt == 0)) fprintf (st, ", all units disabled\n");\r | |
1339 | else if (ucnt > 1) fprintf (st, ", %d units\n", ucnt);\r | |
1340 | else if (flag) fprintf (st, "\n");\r | |
1341 | }\r | |
1342 | if (flag) return SCPE_OK; /* dev only? */\r | |
1343 | for (j = 0; j < dptr->numunits; j++) { /* loop thru units */\r | |
1344 | uptr = dptr->units + j;\r | |
1345 | if ((uptr->flags & UNIT_DIS) == 0)\r | |
1346 | show_unit (st, dptr, uptr, ucnt);\r | |
1347 | }\r | |
1348 | return SCPE_OK;\r | |
1349 | }\r | |
1350 | \r | |
1351 | t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)\r | |
1352 | {\r | |
1353 | int32 u = uptr - dptr->units;\r | |
1354 | \r | |
1355 | if (flag > 1) fprintf (st, " %s%d", sim_dname (dptr), u);\r | |
1356 | else if (flag < 0) fprintf (st, "%s%d", sim_dname (dptr), u);\r | |
1357 | if (uptr->flags & UNIT_FIX) {\r | |
1358 | fprintf (st, ", ");\r | |
1359 | fprint_capac (st, dptr, uptr);\r | |
1360 | }\r | |
1361 | if (uptr->flags & UNIT_ATT) {\r | |
1362 | fprintf (st, ", attached to %s", uptr->filename);\r | |
1363 | if (uptr->flags & UNIT_RO) fprintf (st, ", read only");\r | |
1364 | }\r | |
1365 | else if (uptr->flags & UNIT_ATTABLE)\r | |
1366 | fprintf (st, ", not attached");\r | |
1367 | show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */ \r | |
1368 | fprintf (st, "\n");\r | |
1369 | return SCPE_OK;\r | |
1370 | }\r | |
1371 | \r | |
1372 | void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr)\r | |
1373 | {\r | |
1374 | t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000;\r | |
1375 | t_addr mval = kval * kval;\r | |
1376 | t_addr psize = uptr->capac;\r | |
1377 | char scale, width;\r | |
1378 | \r | |
1379 | if ((dptr->dwidth / dptr->aincr) > 8) width = 'W';\r | |
1380 | else width = 'B';\r | |
1381 | if (uptr->capac < (kval * 10)) scale = 0;\r | |
1382 | else if (uptr->capac < (mval * 10)) {\r | |
1383 | scale = 'K';\r | |
1384 | psize = psize / kval;\r | |
1385 | }\r | |
1386 | else {\r | |
1387 | scale = 'M';\r | |
1388 | psize = psize / mval;\r | |
1389 | }\r | |
1390 | fprint_val (st, (t_value) psize, 10, T_ADDR_W, PV_LEFT);\r | |
1391 | if (scale) fputc (scale, st);\r | |
1392 | fputc (width, st);\r | |
1393 | return;\r | |
1394 | }\r | |
1395 | \r | |
1396 | /* Show <global name> processors */\r | |
1397 | \r | |
1398 | t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1399 | {\r | |
1400 | int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH, vdelt = SIM_DELTA;\r | |
1401 | \r | |
1402 | if (cptr && (*cptr != 0)) return SCPE_2MARG;\r | |
1403 | fprintf (st, "%s simulator V%d.%d-%d", sim_name, vmaj, vmin, vpat);\r | |
1404 | if (vdelt) fprintf (st, "(%d)", vdelt);\r | |
1405 | if (flag) fprintf (st, " [%s, %s, %s]", sim_si64, sim_sa64, sim_snet);\r | |
1406 | fprintf (st, "\n");\r | |
1407 | return SCPE_OK;\r | |
1408 | }\r | |
1409 | \r | |
1410 | t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)\r | |
1411 | {\r | |
1412 | int32 i;\r | |
1413 | DEVICE *dptr;\r | |
1414 | \r | |
1415 | if (cptr && (*cptr != 0)) return SCPE_2MARG;\r | |
1416 | fprintf (st, "%s simulator configuration\n\n", sim_name);\r | |
1417 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++)\r | |
1418 | show_device (st, dptr, flag);\r | |
1419 | return SCPE_OK;\r | |
1420 | }\r | |
1421 | \r | |
1422 | t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)\r | |
1423 | {\r | |
1424 | int32 i;\r | |
1425 | DEVICE *dptr;\r | |
1426 | \r | |
1427 | if (cptr && (*cptr != 0)) return SCPE_2MARG;\r | |
1428 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++)\r | |
1429 | show_dev_logicals (st, dptr, NULL, 1, cptr);\r | |
1430 | return SCPE_OK;\r | |
1431 | }\r | |
1432 | \r | |
1433 | t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1434 | {\r | |
1435 | if (dptr->lname) fprintf (st, "%s -> %s\n", dptr->lname, dptr->name);\r | |
1436 | else if (!flag) fputs ("no logical name assigned\n", st);\r | |
1437 | return SCPE_OK;\r | |
1438 | }\r | |
1439 | \r | |
1440 | t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)\r | |
1441 | {\r | |
1442 | DEVICE *dptr;\r | |
1443 | UNIT *uptr;\r | |
1444 | int32 accum;\r | |
1445 | \r | |
1446 | if (cptr && (*cptr != 0)) return SCPE_2MARG;\r | |
1447 | if (sim_clock_queue == NULL) {\r | |
1448 | fprintf (st, "%s event queue empty, time = %.0f\n",\r | |
1449 | sim_name, sim_time);\r | |
1450 | return SCPE_OK;\r | |
1451 | }\r | |
1452 | fprintf (st, "%s event queue status, time = %.0f\n",\r | |
1453 | sim_name, sim_time);\r | |
1454 | accum = 0;\r | |
1455 | for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) {\r | |
1456 | if (uptr == &sim_step_unit) fprintf (st, " Step timer");\r | |
1457 | else if ((dptr = find_dev_from_unit (uptr)) != NULL) {\r | |
1458 | fprintf (st, " %s", sim_dname (dptr));\r | |
1459 | if (dptr->numunits > 1) fprintf (st, " unit %d",\r | |
1460 | (int32) (uptr - dptr->units));\r | |
1461 | }\r | |
1462 | else fprintf (st, " Unknown");\r | |
1463 | fprintf (st, " at %d\n", accum + uptr->time);\r | |
1464 | accum = accum + uptr->time;\r | |
1465 | }\r | |
1466 | return SCPE_OK;\r | |
1467 | }\r | |
1468 | \r | |
1469 | t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1470 | {\r | |
1471 | if (cptr && (*cptr != 0)) return SCPE_2MARG;\r | |
1472 | fprintf (st, "Time:\t%.0f\n", sim_time);\r | |
1473 | return SCPE_OK;\r | |
1474 | }\r | |
1475 | \r | |
1476 | t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1477 | {\r | |
1478 | t_stat r;\r | |
1479 | \r | |
1480 | if (cptr && (*cptr != 0)) r = ssh_break (st, cptr, 1); /* more? */\r | |
1481 | else r = sim_brk_showall (st, sim_switches);\r | |
1482 | return r;\r | |
1483 | }\r | |
1484 | \r | |
1485 | t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1486 | {\r | |
1487 | fprintf (st, "Radix=%d\n", dptr->dradix);\r | |
1488 | return SCPE_OK;\r | |
1489 | }\r | |
1490 | \r | |
1491 | t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1492 | {\r | |
1493 | int32 any = 0;\r | |
1494 | DEBTAB *dep;\r | |
1495 | \r | |
1496 | if (dptr->flags & DEV_DEBUG) {\r | |
1497 | if (dptr->dctrl == 0) fputs ("Debugging disabled", st);\r | |
1498 | else if (dptr->debflags == NULL) fputs ("Debugging enabled", st);\r | |
1499 | else {\r | |
1500 | fputs ("Debug=", st);\r | |
1501 | for (dep = dptr->debflags; dep->name != NULL; dep++) {\r | |
1502 | if (dptr->dctrl & dep->mask) {\r | |
1503 | if (any) fputc (';', st);\r | |
1504 | fputs (dep->name, st);\r | |
1505 | any = 1;\r | |
1506 | }\r | |
1507 | }\r | |
1508 | }\r | |
1509 | fputc ('\n', st);\r | |
1510 | return SCPE_OK;\r | |
1511 | }\r | |
1512 | else return SCPE_NOFNC;\r | |
1513 | }\r | |
1514 | \r | |
1515 | /* Show modifiers */\r | |
1516 | \r | |
1517 | t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr)\r | |
1518 | {\r | |
1519 | int32 i;\r | |
1520 | DEVICE *dptr;\r | |
1521 | \r | |
1522 | if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */\r | |
1523 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) \r | |
1524 | show_dev_modifiers (st, dptr, NULL, flag, cptr);\r | |
1525 | return SCPE_OK;\r | |
1526 | }\r | |
1527 | \r | |
1528 | t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)\r | |
1529 | {\r | |
1530 | int any, enb;\r | |
1531 | MTAB *mptr;\r | |
1532 | DEBTAB *dep;\r | |
1533 | \r | |
1534 | any = enb = 0;\r | |
1535 | if (dptr->modifiers) {\r | |
1536 | for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {\r | |
1537 | if (mptr->mstring) {\r | |
1538 | if (strcmp (mptr->mstring, "ENABLED") == 0) enb = 1;\r | |
1539 | if (any++) fprintf (st, ", %s", mptr->mstring);\r | |
1540 | else fprintf (st, "%s\t%s", sim_dname (dptr), mptr->mstring);\r | |
1541 | }\r | |
1542 | }\r | |
1543 | }\r | |
1544 | if (dptr->flags & DEV_DEBUG) {\r | |
1545 | if (any++) fprintf (st, ", DEBUG, NODEBUG");\r | |
1546 | else fprintf (st, "%s\tDEBUG, NODEBUG", sim_dname (dptr));\r | |
1547 | }\r | |
1548 | if (!enb && (dptr->flags & DEV_DISABLE)) {\r | |
1549 | if (any++) fprintf (st, ", ENABLED, DISABLED");\r | |
1550 | else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr));\r | |
1551 | }\r | |
1552 | if (any) fprintf (st, "\n");\r | |
1553 | if ((dptr->flags & DEV_DEBUG) && dptr->debflags) {\r | |
1554 | fprintf (st, "%s\tDEBUG=", sim_dname (dptr));\r | |
1555 | for (dep = dptr->debflags; dep->name != NULL; dep++)\r | |
1556 | fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ","), dep->name);\r | |
1557 | fprintf (st, "\n");\r | |
1558 | }\r | |
1559 | return SCPE_OK;\r | |
1560 | }\r | |
1561 | \r | |
1562 | t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)\r | |
1563 | {\r | |
1564 | MTAB *mptr;\r | |
1565 | \r | |
1566 | if (dptr->modifiers == NULL) return SCPE_OK;\r | |
1567 | for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) {\r | |
1568 | if (mptr->pstring && ((mptr->mask & MTAB_XTD)?\r | |
1569 | ((mptr->mask & flag) && !(mptr->mask & MTAB_NMO)): \r | |
1570 | ((MTAB_VUN & flag) && ((uptr->flags & mptr->mask) == mptr->match)))) {\r | |
1571 | fputs (", ", st);\r | |
1572 | show_one_mod (st, dptr, uptr, mptr, NULL, 0);\r | |
1573 | }\r | |
1574 | }\r | |
1575 | return SCPE_OK;\r | |
1576 | }\r | |
1577 | \r | |
1578 | t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr,\r | |
1579 | char *cptr, int32 flag)\r | |
1580 | {\r | |
1581 | t_value val;\r | |
1582 | \r | |
1583 | if (mptr->disp) mptr->disp (st, uptr, mptr->match, cptr? cptr: mptr->desc);\r | |
1584 | else if ((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_VAL)) {\r | |
1585 | REG *rptr = (REG *) mptr->desc;\r | |
1586 | fprintf (st, "%s=", mptr->pstring);\r | |
1587 | val = get_rval (rptr, 0);\r | |
1588 | fprint_val (st, val, rptr->radix, rptr->width,\r | |
1589 | rptr->flags & REG_FMT);\r | |
1590 | }\r | |
1591 | else fputs (mptr->pstring, st);\r | |
1592 | if (flag && !((mptr->mask & MTAB_XTD) &&\r | |
1593 | (mptr->mask & MTAB_NMO))) fputc ('\n', st);\r | |
1594 | return SCPE_OK;\r | |
1595 | }\r | |
1596 | \r | |
1597 | /* Breakpoint commands */\r | |
1598 | \r | |
1599 | t_stat brk_cmd (int32 flg, char *cptr)\r | |
1600 | {\r | |
1601 | GET_SWITCHES (cptr); /* get switches */\r | |
1602 | return ssh_break (NULL, cptr, flg); /* call common code */\r | |
1603 | }\r | |
1604 | \r | |
1605 | t_stat ssh_break (FILE *st, char *cptr, int32 flg)\r | |
1606 | {\r | |
1607 | char gbuf[CBUFSIZE], *tptr, *t1ptr, *aptr;\r | |
1608 | DEVICE *dptr = sim_dflt_dev;\r | |
1609 | UNIT *uptr = dptr->units;\r | |
1610 | t_stat r;\r | |
1611 | t_addr lo, hi, max = uptr->capac - 1;\r | |
1612 | int32 cnt;\r | |
1613 | \r | |
1614 | if (sim_brk_types == 0) return SCPE_NOFNC;\r | |
1615 | if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;\r | |
1616 | if (aptr = strchr (cptr, ';')) { /* ;action? */\r | |
1617 | if (flg != SSH_ST) return SCPE_ARG; /* only on SET */\r | |
1618 | *aptr++ = 0; /* separate strings */\r | |
1619 | }\r | |
1620 | if (*cptr == 0) { /* no argument? */\r | |
1621 | lo = (t_addr) get_rval (sim_PC, 0); /* use PC */\r | |
1622 | return ssh_break_one (st, flg, lo, 0, aptr);\r | |
1623 | }\r | |
1624 | while (*cptr) {\r | |
1625 | cptr = get_glyph (cptr, gbuf, ',');\r | |
1626 | tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0);\r | |
1627 | if (tptr == NULL) return SCPE_ARG;\r | |
1628 | if (*tptr == '[') {\r | |
1629 | cnt = (int32) strtotv (tptr + 1, &t1ptr, 10);\r | |
1630 | if ((tptr == t1ptr) || (*t1ptr != ']') ||\r | |
1631 | (flg != SSH_ST)) return SCPE_ARG;\r | |
1632 | tptr = t1ptr + 1;\r | |
1633 | }\r | |
1634 | else cnt = 0;\r | |
1635 | if (*tptr != 0) return SCPE_ARG;\r | |
1636 | if ((lo == 0) && (hi == max)) {\r | |
1637 | if (flg == SSH_CL) sim_brk_clrall (sim_switches);\r | |
1638 | else if (flg == SSH_SH) sim_brk_showall (st, sim_switches);\r | |
1639 | else return SCPE_ARG;\r | |
1640 | }\r | |
1641 | else { \r | |
1642 | for ( ; lo <= hi; lo = lo + 1) {\r | |
1643 | r = ssh_break_one (st, flg, lo, cnt, aptr);\r | |
1644 | if (r != SCPE_OK) return r;\r | |
1645 | }\r | |
1646 | }\r | |
1647 | }\r | |
1648 | return SCPE_OK;\r | |
1649 | }\r | |
1650 | \r | |
1651 | t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr)\r | |
1652 | {\r | |
1653 | switch (flg) {\r | |
1654 | \r | |
1655 | case SSH_ST:\r | |
1656 | return sim_brk_set (lo, sim_switches, cnt, aptr);\r | |
1657 | break;\r | |
1658 | \r | |
1659 | case SSH_CL:\r | |
1660 | return sim_brk_clr (lo, sim_switches);\r | |
1661 | break;\r | |
1662 | \r | |
1663 | case SSH_SH:\r | |
1664 | return sim_brk_show (st, lo, sim_switches);\r | |
1665 | break;\r | |
1666 | \r | |
1667 | default:\r | |
1668 | return SCPE_ARG;\r | |
1669 | }\r | |
1670 | }\r | |
1671 | \r | |
1672 | /* Reset command and routines\r | |
1673 | \r | |
1674 | re[set] reset all devices\r | |
1675 | re[set] all reset all devices\r | |
1676 | re[set] device reset specific device\r | |
1677 | */\r | |
1678 | \r | |
1679 | t_stat reset_cmd (int32 flag, char *cptr)\r | |
1680 | {\r | |
1681 | char gbuf[CBUFSIZE];\r | |
1682 | DEVICE *dptr;\r | |
1683 | \r | |
1684 | GET_SWITCHES (cptr); /* get switches */\r | |
1685 | if (*cptr == 0) return (reset_all (0)); /* reset(cr) */\r | |
1686 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1687 | if (*cptr != 0) return SCPE_2MARG; /* now eol? */\r | |
1688 | if (strcmp (gbuf, "ALL") == 0) return (reset_all (0));\r | |
1689 | dptr = find_dev (gbuf); /* locate device */\r | |
1690 | if (dptr == NULL) return SCPE_NXDEV; /* found it? */\r | |
1691 | if (dptr->reset != NULL) return dptr->reset (dptr);\r | |
1692 | else return SCPE_OK;\r | |
1693 | }\r | |
1694 | \r | |
1695 | /* Reset devices start..end\r | |
1696 | \r | |
1697 | Inputs:\r | |
1698 | start = number of starting device\r | |
1699 | Outputs:\r | |
1700 | status = error status\r | |
1701 | */\r | |
1702 | \r | |
1703 | t_stat reset_all (uint32 start)\r | |
1704 | {\r | |
1705 | DEVICE *dptr;\r | |
1706 | uint32 i;\r | |
1707 | t_stat reason;\r | |
1708 | \r | |
1709 | for (i = 0; i < start; i++) {\r | |
1710 | if (sim_devices[i] == NULL) return SCPE_IERR;\r | |
1711 | }\r | |
1712 | for (i = start; (dptr = sim_devices[i]) != NULL; i++) {\r | |
1713 | if (dptr->reset != NULL) {\r | |
1714 | reason = dptr->reset (dptr);\r | |
1715 | if (reason != SCPE_OK) return reason;\r | |
1716 | }\r | |
1717 | }\r | |
1718 | return SCPE_OK;\r | |
1719 | }\r | |
1720 | \r | |
1721 | /* Reset to powerup state\r | |
1722 | \r | |
1723 | Inputs:\r | |
1724 | start = number of starting device\r | |
1725 | Outputs:\r | |
1726 | status = error status\r | |
1727 | */\r | |
1728 | \r | |
1729 | t_stat reset_all_p (uint32 start)\r | |
1730 | {\r | |
1731 | t_stat r;\r | |
1732 | int32 old_sw = sim_switches;\r | |
1733 | \r | |
1734 | sim_switches = SWMASK ('P');\r | |
1735 | r = reset_all (start);\r | |
1736 | sim_switches = old_sw;\r | |
1737 | return r;\r | |
1738 | }\r | |
1739 | \r | |
1740 | /* Load and dump commands\r | |
1741 | \r | |
1742 | lo[ad] filename {arg} load specified file\r | |
1743 | du[mp] filename {arg} dump to specified file\r | |
1744 | */\r | |
1745 | \r | |
1746 | t_stat load_cmd (int32 flag, char *cptr)\r | |
1747 | {\r | |
1748 | char gbuf[CBUFSIZE];\r | |
1749 | FILE *loadfile;\r | |
1750 | t_stat reason;\r | |
1751 | \r | |
1752 | GET_SWITCHES (cptr); /* get switches */\r | |
1753 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1754 | cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */\r | |
1755 | loadfile = sim_fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */\r | |
1756 | if (loadfile == NULL) return SCPE_OPENERR;\r | |
1757 | GET_SWITCHES (cptr); /* get switches */\r | |
1758 | reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */\r | |
1759 | fclose (loadfile);\r | |
1760 | return reason;\r | |
1761 | }\r | |
1762 | \r | |
1763 | /* Attach command\r | |
1764 | \r | |
1765 | at[tach] unit file attach specified unit to file\r | |
1766 | */\r | |
1767 | \r | |
1768 | t_stat attach_cmd (int32 flag, char *cptr)\r | |
1769 | {\r | |
1770 | char gbuf[CBUFSIZE];\r | |
1771 | DEVICE *dptr;\r | |
1772 | UNIT *uptr;\r | |
1773 | t_stat r;\r | |
1774 | \r | |
1775 | GET_SWITCHES (cptr); /* get switches */\r | |
1776 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1777 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1778 | GET_SWITCHES (cptr); /* get switches */\r | |
1779 | if (*cptr == 0) return SCPE_2FARG; /* now eol? */\r | |
1780 | dptr = find_unit (gbuf, &uptr); /* locate unit */\r | |
1781 | if (dptr == NULL) return SCPE_NXDEV; /* found dev? */\r | |
1782 | if (uptr == NULL) return SCPE_NXUN; /* valid unit? */\r | |
1783 | if (uptr->flags & UNIT_ATT) { /* already attached? */\r | |
1784 | r = scp_detach_unit (dptr, uptr); /* detach it */\r | |
1785 | if (r != SCPE_OK) return r; /* error? */\r | |
1786 | }\r | |
1787 | sim_trim_endspc (cptr); /* trim trailing spc */\r | |
1788 | return scp_attach_unit (dptr, uptr, cptr); /* attach */\r | |
1789 | }\r | |
1790 | \r | |
1791 | /* Call device-specific or file-oriented attach unit routine */\r | |
1792 | \r | |
1793 | t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr)\r | |
1794 | {\r | |
1795 | if (dptr->attach != NULL) /* device routine? */\r | |
1796 | return dptr->attach (uptr, cptr); /* call it */\r | |
1797 | return attach_unit (uptr, cptr); /* no, std routine */\r | |
1798 | }\r | |
1799 | \r | |
1800 | /* Attach unit to file */\r | |
1801 | \r | |
1802 | t_stat attach_unit (UNIT *uptr, char *cptr)\r | |
1803 | {\r | |
1804 | DEVICE *dptr;\r | |
1805 | \r | |
1806 | if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */\r | |
1807 | if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* not attachable? */\r | |
1808 | if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT;\r | |
1809 | if (dptr->flags & DEV_RAWONLY) return SCPE_NOFNC; /* raw mode only? */\r | |
1810 | uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */\r | |
1811 | if (uptr->filename == NULL) return SCPE_MEM;\r | |
1812 | strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */\r | |
1813 | if (sim_switches & SWMASK ('R')) { /* read only? */\r | |
1814 | if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */\r | |
1815 | return attach_err (uptr, SCPE_NORO); /* no, error */\r | |
1816 | uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */\r | |
1817 | if (uptr->fileref == NULL) /* open fail? */\r | |
1818 | return attach_err (uptr, SCPE_OPENERR); /* yes, error */\r | |
1819 | uptr->flags = uptr->flags | UNIT_RO; /* set rd only */\r | |
1820 | if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr));\r | |
1821 | }\r | |
1822 | else { /* normal */\r | |
1823 | uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */\r | |
1824 | if (uptr->fileref == NULL) { /* open fail? */\r | |
1825 | if ((errno == EROFS) || (errno == EACCES)) { /* read only? */\r | |
1826 | if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */\r | |
1827 | return attach_err (uptr, SCPE_NORO); /* no error */\r | |
1828 | uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */\r | |
1829 | if (uptr->fileref == NULL) /* open fail? */\r | |
1830 | return attach_err (uptr, SCPE_OPENERR); /* yes, error */\r | |
1831 | uptr->flags = uptr->flags | UNIT_RO; /* set rd only */\r | |
1832 | if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr));\r | |
1833 | }\r | |
1834 | else { /* doesn't exist */\r | |
1835 | if (sim_switches & SWMASK ('E')) /* must exist? */\r | |
1836 | return attach_err (uptr, SCPE_OPENERR); /* yes, error */\r | |
1837 | uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */\r | |
1838 | if (uptr->fileref == NULL) /* open fail? */\r | |
1839 | return attach_err (uptr, SCPE_OPENERR); /* yes, error */\r | |
1840 | if (!sim_quiet) printf ("%s: creating new file\n", sim_dname (dptr));\r | |
1841 | }\r | |
1842 | } /* end if null */\r | |
1843 | } /* end else */\r | |
1844 | if (uptr->flags & UNIT_BUFABLE) { /* buffer? */\r | |
1845 | uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */\r | |
1846 | if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */\r | |
1847 | uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */\r | |
1848 | if (uptr->filebuf == NULL) /* no buffer? */\r | |
1849 | return attach_err (uptr, SCPE_MEM); /* error */\r | |
1850 | if (!sim_quiet) printf ("%s: buffering file in memory\n", sim_dname (dptr));\r | |
1851 | uptr->hwmark = sim_fread (uptr->filebuf, /* read file */\r | |
1852 | SZ_D (dptr), cap, uptr->fileref);\r | |
1853 | uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */\r | |
1854 | }\r | |
1855 | uptr->flags = uptr->flags | UNIT_ATT;\r | |
1856 | uptr->pos = 0;\r | |
1857 | return SCPE_OK;\r | |
1858 | }\r | |
1859 | \r | |
1860 | t_stat attach_err (UNIT *uptr, t_stat stat)\r | |
1861 | {\r | |
1862 | free (uptr->filename);\r | |
1863 | uptr->filename = NULL;\r | |
1864 | return stat;\r | |
1865 | }\r | |
1866 | \r | |
1867 | /* Detach command\r | |
1868 | \r | |
1869 | det[ach] all detach all units\r | |
1870 | det[ach] unit detach specified unit\r | |
1871 | */\r | |
1872 | \r | |
1873 | t_stat detach_cmd (int32 flag, char *cptr)\r | |
1874 | {\r | |
1875 | char gbuf[CBUFSIZE];\r | |
1876 | DEVICE *dptr;\r | |
1877 | UNIT *uptr;\r | |
1878 | \r | |
1879 | GET_SWITCHES (cptr); /* get switches */\r | |
1880 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1881 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1882 | if (*cptr != 0) return SCPE_2MARG; /* now eol? */\r | |
1883 | if (strcmp (gbuf, "ALL") == 0) return (detach_all (0, FALSE));\r | |
1884 | dptr = find_unit (gbuf, &uptr); /* locate unit */\r | |
1885 | if (dptr == NULL) return SCPE_NXDEV; /* found dev? */\r | |
1886 | if (uptr == NULL) return SCPE_NXUN; /* valid unit? */\r | |
1887 | return scp_detach_unit (dptr, uptr); /* detach */\r | |
1888 | }\r | |
1889 | \r | |
1890 | /* Detach devices start..end\r | |
1891 | \r | |
1892 | Inputs:\r | |
1893 | start = number of starting device\r | |
1894 | shutdown = TRUE if simulator shutting down\r | |
1895 | Outputs:\r | |
1896 | status = error status\r | |
1897 | \r | |
1898 | Note that during shutdown, detach routines for non-attachable devices\r | |
1899 | will be called. These routines can implement simulator shutdown.\r | |
1900 | */\r | |
1901 | \r | |
1902 | t_stat detach_all (int32 start, t_bool shutdown)\r | |
1903 | {\r | |
1904 | uint32 i, j;\r | |
1905 | DEVICE *dptr;\r | |
1906 | UNIT *uptr;\r | |
1907 | t_stat r;\r | |
1908 | \r | |
1909 | if ((start < 0) || (start > 1)) return SCPE_IERR;\r | |
1910 | for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */\r | |
1911 | for (j = 0; j < dptr->numunits; j++) { /* loop thru units */\r | |
1912 | uptr = (dptr->units) + j;\r | |
1913 | if ((uptr->flags & UNIT_ATT) || /* attached? */\r | |
1914 | (shutdown && dptr->detach && /* shutdown, spec rtn, */\r | |
1915 | !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */\r | |
1916 | r = scp_detach_unit (dptr, uptr); /* detach unit */\r | |
1917 | if (r != SCPE_OK) return r;\r | |
1918 | }\r | |
1919 | }\r | |
1920 | }\r | |
1921 | return SCPE_OK;\r | |
1922 | }\r | |
1923 | \r | |
1924 | /* Call device-specific or file-oriented detach unit routine */\r | |
1925 | \r | |
1926 | t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)\r | |
1927 | {\r | |
1928 | if (dptr->detach != NULL) return dptr->detach (uptr); /* device routine? */\r | |
1929 | return detach_unit (uptr); /* no, standard */\r | |
1930 | }\r | |
1931 | \r | |
1932 | /* Detach unit from file */\r | |
1933 | \r | |
1934 | t_stat detach_unit (UNIT *uptr)\r | |
1935 | {\r | |
1936 | DEVICE *dptr;\r | |
1937 | \r | |
1938 | if (uptr == NULL) return SCPE_IERR;\r | |
1939 | if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* attachable? */\r | |
1940 | if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */\r | |
1941 | if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK;\r | |
1942 | if (uptr->flags & UNIT_BUF) {\r | |
1943 | uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr;\r | |
1944 | if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) {\r | |
1945 | if (!sim_quiet) printf ("%s: writing buffer to file\n", sim_dname (dptr));\r | |
1946 | rewind (uptr->fileref);\r | |
1947 | sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref);\r | |
1948 | if (ferror (uptr->fileref)) perror ("I/O error");\r | |
1949 | }\r | |
1950 | if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */\r | |
1951 | free (uptr->filebuf); /* free buf */\r | |
1952 | uptr->filebuf = NULL;\r | |
1953 | }\r | |
1954 | uptr->flags = uptr->flags & ~UNIT_BUF;\r | |
1955 | }\r | |
1956 | uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO);\r | |
1957 | free (uptr->filename);\r | |
1958 | uptr->filename = NULL;\r | |
1959 | if (fclose (uptr->fileref) == EOF) return SCPE_IOERR;\r | |
1960 | return SCPE_OK;\r | |
1961 | }\r | |
1962 | \r | |
1963 | /* Assign command\r | |
1964 | \r | |
1965 | as[sign] device name assign logical name to device\r | |
1966 | */\r | |
1967 | \r | |
1968 | t_stat assign_cmd (int32 flag, char *cptr)\r | |
1969 | {\r | |
1970 | char gbuf[CBUFSIZE];\r | |
1971 | DEVICE *dptr;\r | |
1972 | \r | |
1973 | GET_SWITCHES (cptr); /* get switches */\r | |
1974 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
1975 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1976 | GET_SWITCHES (cptr); /* get switches */\r | |
1977 | if (*cptr == 0) return SCPE_2FARG; /* now eol? */\r | |
1978 | dptr = find_dev (gbuf); /* locate device */\r | |
1979 | if (dptr == NULL) return SCPE_NXDEV; /* found dev? */\r | |
1980 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
1981 | if (*cptr != 0) return SCPE_2MARG; /* must be eol */\r | |
1982 | if (find_dev (gbuf)) return SCPE_ARG; /* name in use */\r | |
1983 | deassign_device (dptr); /* release current */\r | |
1984 | return assign_device (dptr, gbuf);\r | |
1985 | }\r | |
1986 | \r | |
1987 | t_stat assign_device (DEVICE *dptr, char *cptr)\r | |
1988 | {\r | |
1989 | dptr->lname = (char *) calloc (CBUFSIZE, sizeof (char));\r | |
1990 | if (dptr->lname == NULL) return SCPE_MEM;\r | |
1991 | strncpy (dptr->lname, cptr, CBUFSIZE);\r | |
1992 | return SCPE_OK;\r | |
1993 | }\r | |
1994 | \r | |
1995 | /* Deassign command\r | |
1996 | \r | |
1997 | dea[ssign] device deassign logical name\r | |
1998 | */\r | |
1999 | \r | |
2000 | t_stat deassign_cmd (int32 flag, char *cptr)\r | |
2001 | {\r | |
2002 | char gbuf[CBUFSIZE];\r | |
2003 | DEVICE *dptr;\r | |
2004 | \r | |
2005 | GET_SWITCHES (cptr); /* get switches */\r | |
2006 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
2007 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
2008 | if (*cptr != 0) return SCPE_2MARG; /* now eol? */\r | |
2009 | dptr = find_dev (gbuf); /* locate device */\r | |
2010 | if (dptr == NULL) return SCPE_NXDEV; /* found dev? */\r | |
2011 | return deassign_device (dptr);\r | |
2012 | }\r | |
2013 | \r | |
2014 | t_stat deassign_device (DEVICE *dptr)\r | |
2015 | {\r | |
2016 | if (dptr->lname) free (dptr->lname);\r | |
2017 | dptr->lname = NULL;\r | |
2018 | return SCPE_OK;\r | |
2019 | }\r | |
2020 | \r | |
2021 | /* Get device display name */\r | |
2022 | \r | |
2023 | char *sim_dname (DEVICE *dptr)\r | |
2024 | {\r | |
2025 | return (dptr->lname? dptr->lname: dptr->name);\r | |
2026 | }\r | |
2027 | \r | |
2028 | /* Save command\r | |
2029 | \r | |
2030 | sa[ve] filename save state to specified file\r | |
2031 | */\r | |
2032 | \r | |
2033 | t_stat save_cmd (int32 flag, char *cptr)\r | |
2034 | {\r | |
2035 | FILE *sfile;\r | |
2036 | t_stat r;\r | |
2037 | GET_SWITCHES (cptr); /* get switches */\r | |
2038 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
2039 | sim_trim_endspc (cptr);\r | |
2040 | if ((sfile = sim_fopen (cptr, "wb")) == NULL) return SCPE_OPENERR;\r | |
2041 | r = sim_save (sfile);\r | |
2042 | fclose (sfile);\r | |
2043 | return r;\r | |
2044 | }\r | |
2045 | \r | |
2046 | t_stat sim_save (FILE *sfile)\r | |
2047 | {\r | |
2048 | void *mbuf;\r | |
2049 | int32 l, t;\r | |
2050 | uint32 i, j;\r | |
2051 | t_addr k, high;\r | |
2052 | t_value val;\r | |
2053 | t_stat r;\r | |
2054 | t_bool zeroflg;\r | |
2055 | size_t sz;\r | |
2056 | DEVICE *dptr;\r | |
2057 | UNIT *uptr;\r | |
2058 | REG *rptr;\r | |
2059 | \r | |
2060 | #define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile)\r | |
2061 | \r | |
2062 | fprintf (sfile, "%s\n%s\n%s\n%s\n%s\n%.0f\n",\r | |
2063 | save_vercur, /* [V2.5] save format */\r | |
2064 | sim_name, /* sim name */\r | |
2065 | sim_si64, sim_sa64, sim_snet, /* [V3.5] options */\r | |
2066 | sim_time); /* [V3.2] sim time */\r | |
2067 | WRITE_I (sim_rtime); /* [V2.6] sim rel time */\r | |
2068 | \r | |
2069 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */\r | |
2070 | fputs (dptr->name, sfile); /* device name */\r | |
2071 | fputc ('\n', sfile);\r | |
2072 | if (dptr->lname) fputs (dptr->lname, sfile); /* [V3.0] logical name */\r | |
2073 | fputc ('\n', sfile);\r | |
2074 | WRITE_I (dptr->flags); /* [V2.10] flags */\r | |
2075 | for (j = 0; j < dptr->numunits; j++) {\r | |
2076 | uptr = dptr->units + j;\r | |
2077 | t = sim_is_active (uptr);\r | |
2078 | WRITE_I (j); /* unit number */\r | |
2079 | WRITE_I (t); /* activation time */\r | |
2080 | WRITE_I (uptr->u3); /* unit specific */\r | |
2081 | WRITE_I (uptr->u4);\r | |
2082 | WRITE_I (uptr->u5); /* [V3.0] more unit */\r | |
2083 | WRITE_I (uptr->u6);\r | |
2084 | WRITE_I (uptr->flags); /* [V2.10] flags */\r | |
2085 | WRITE_I (uptr->capac); /* [V3.5] capacity */\r | |
2086 | if (uptr->flags & UNIT_ATT) fputs (uptr->filename, sfile);\r | |
2087 | fputc ('\n', sfile);\r | |
2088 | if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&\r | |
2089 | (dptr->examine != NULL) &&\r | |
2090 | ((high = uptr->capac) != 0)) { /* memory-like unit? */\r | |
2091 | WRITE_I (high); /* [V2.5] write size */\r | |
2092 | sz = SZ_D (dptr);\r | |
2093 | if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {\r | |
2094 | fclose (sfile);\r | |
2095 | return SCPE_MEM;\r | |
2096 | }\r | |
2097 | for (k = 0; k < high; ) { /* loop thru mem */\r | |
2098 | zeroflg = TRUE;\r | |
2099 | for (l = 0; (l < SRBSIZ) && (k < high); l++,\r | |
2100 | k = k + (dptr->aincr)) { /* check for 0 block */\r | |
2101 | r = dptr->examine (&val, k, uptr, SIM_SW_REST);\r | |
2102 | if (r != SCPE_OK) return r;\r | |
2103 | if (val) zeroflg = FALSE;\r | |
2104 | SZ_STORE (sz, val, mbuf, l);\r | |
2105 | } /* end for l */\r | |
2106 | if (zeroflg) { /* all zero's? */\r | |
2107 | l = -l; /* invert block count */\r | |
2108 | WRITE_I (l); /* write only count */\r | |
2109 | }\r | |
2110 | else {\r | |
2111 | WRITE_I (l); /* block count */\r | |
2112 | sim_fwrite (mbuf, sz, l, sfile);\r | |
2113 | }\r | |
2114 | } /* end for k */\r | |
2115 | free (mbuf); /* dealloc buffer */\r | |
2116 | } /* end if mem */\r | |
2117 | else { /* no memory */\r | |
2118 | high = 0; /* write 0 */\r | |
2119 | WRITE_I (high);\r | |
2120 | } /* end else mem */\r | |
2121 | } /* end unit loop */\r | |
2122 | t = -1; /* end units */\r | |
2123 | WRITE_I (t); /* write marker */\r | |
2124 | for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */\r | |
2125 | (rptr->name != NULL); rptr++) {\r | |
2126 | fputs (rptr->name, sfile); /* name */\r | |
2127 | fputc ('\n', sfile);\r | |
2128 | WRITE_I (rptr->depth); /* [V2.10] depth */\r | |
2129 | for (j = 0; j < rptr->depth; j++) { /* loop thru values */\r | |
2130 | val = get_rval (rptr, j); /* get value */\r | |
2131 | WRITE_I (val); /* store */\r | |
2132 | }\r | |
2133 | }\r | |
2134 | fputc ('\n', sfile); /* end registers */\r | |
2135 | }\r | |
2136 | fputc ('\n', sfile); /* end devices */\r | |
2137 | return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */\r | |
2138 | }\r | |
2139 | \r | |
2140 | /* Restore command\r | |
2141 | \r | |
2142 | re[store] filename restore state from specified file\r | |
2143 | */\r | |
2144 | \r | |
2145 | t_stat restore_cmd (int32 flag, char *cptr)\r | |
2146 | {\r | |
2147 | FILE *rfile;\r | |
2148 | t_stat r;\r | |
2149 | \r | |
2150 | GET_SWITCHES (cptr); /* get switches */\r | |
2151 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
2152 | sim_trim_endspc (cptr);\r | |
2153 | if ((rfile = sim_fopen (cptr, "rb")) == NULL) return SCPE_OPENERR;\r | |
2154 | r = sim_rest (rfile);\r | |
2155 | fclose (rfile);\r | |
2156 | return r;\r | |
2157 | }\r | |
2158 | \r | |
2159 | t_stat sim_rest (FILE *rfile)\r | |
2160 | {\r | |
2161 | char buf[CBUFSIZE];\r | |
2162 | void *mbuf;\r | |
2163 | int32 j, blkcnt, limit, unitno, time, flg;\r | |
2164 | uint32 us, depth;\r | |
2165 | t_addr k, high, old_capac;\r | |
2166 | t_value val, mask;\r | |
2167 | t_stat r;\r | |
2168 | size_t sz;\r | |
2169 | t_bool v35, v32;\r | |
2170 | DEVICE *dptr;\r | |
2171 | UNIT *uptr;\r | |
2172 | REG *rptr;\r | |
2173 | \r | |
2174 | #define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \\r | |
2175 | return SCPE_IOERR;\r | |
2176 | #define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \\r | |
2177 | return SCPE_IOERR;\r | |
2178 | \r | |
2179 | READ_S (buf); /* [V2.5+] read version */\r | |
2180 | v35 = v32 = FALSE;\r | |
2181 | if (strcmp (buf, save_vercur) == 0) v35 = v32 = TRUE; /* version 3.5? */ \r | |
2182 | else if (strcmp (buf, save_ver32) == 0) v32 = TRUE; /* version 3.2? */\r | |
2183 | else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */\r | |
2184 | printf ("Invalid file version: %s\n", buf);\r | |
2185 | return SCPE_INCOMP;\r | |
2186 | }\r | |
2187 | READ_S (buf); /* read sim name */\r | |
2188 | if (strcmp (buf, sim_name)) { /* name match? */\r | |
2189 | printf ("Wrong system type: %s\n", buf);\r | |
2190 | return SCPE_INCOMP;\r | |
2191 | }\r | |
2192 | if (v35) { /* [V3.5+] options */\r | |
2193 | READ_S (buf); /* integer size */\r | |
2194 | if (strcmp (buf, sim_si64) != 0) {\r | |
2195 | printf ("Incompatible integer size, save file = %s\n", buf);\r | |
2196 | return SCPE_INCOMP;\r | |
2197 | }\r | |
2198 | READ_S (buf); /* address size */\r | |
2199 | if (strcmp (buf, sim_sa64) != 0) {\r | |
2200 | printf ("Incompatible address size, save file = %s\n", buf);\r | |
2201 | return SCPE_INCOMP;\r | |
2202 | }\r | |
2203 | READ_S (buf); /* Ethernet */\r | |
2204 | }\r | |
2205 | if (v32) { /* [V3.2+] time as string */\r | |
2206 | READ_S (buf);\r | |
2207 | sscanf (buf, "%lf", &sim_time);\r | |
2208 | }\r | |
2209 | else READ_I (sim_time); /* sim time */\r | |
2210 | READ_I (sim_rtime); /* [V2.6+] sim rel time */\r | |
2211 | \r | |
2212 | for ( ;; ) { /* device loop */\r | |
2213 | READ_S (buf); /* read device name */\r | |
2214 | if (buf[0] == 0) break; /* last? */\r | |
2215 | if ((dptr = find_dev (buf)) == NULL) { /* locate device */\r | |
2216 | printf ("Invalid device name: %s\n", buf);\r | |
2217 | return SCPE_INCOMP;\r | |
2218 | }\r | |
2219 | READ_S (buf); /* [V3.0+] logical name */\r | |
2220 | deassign_device (dptr); /* delete old name */\r | |
2221 | if ((buf[0] != 0) && \r | |
2222 | ((r = assign_device (dptr, buf)) != SCPE_OK)) return r;\r | |
2223 | READ_I (flg); /* [V2.10+] ctlr flags */\r | |
2224 | if (!v32) flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) |\r | |
2225 | (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */\r | |
2226 | dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */\r | |
2227 | (flg & DEV_RFLAGS);\r | |
2228 | for ( ;; ) { /* unit loop */\r | |
2229 | sim_switches = SIM_SW_REST; /* flag rstr, clr RO */\r | |
2230 | READ_I (unitno); /* unit number */\r | |
2231 | if (unitno < 0) break; /* end units? */\r | |
2232 | if ((uint32) unitno >= dptr->numunits) { /* too big? */\r | |
2233 | printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno);\r | |
2234 | return SCPE_INCOMP;\r | |
2235 | }\r | |
2236 | READ_I (time); /* event time */\r | |
2237 | uptr = (dptr->units) + unitno;\r | |
2238 | sim_cancel (uptr);\r | |
2239 | if (time > 0) sim_activate (uptr, time - 1);\r | |
2240 | READ_I (uptr->u3); /* device specific */\r | |
2241 | READ_I (uptr->u4);\r | |
2242 | READ_I (uptr->u5); /* [V3.0+] more dev spec */\r | |
2243 | READ_I (uptr->u6);\r | |
2244 | READ_I (flg); /* [V2.10+] unit flags */\r | |
2245 | old_capac = uptr->capac; /* save current capacity */\r | |
2246 | if (v35) { /* [V3.5+] capacity */\r | |
2247 | READ_I (uptr->capac);\r | |
2248 | }\r | |
2249 | if (!v32) flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) |\r | |
2250 | (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */\r | |
2251 | uptr->flags = (uptr->flags & ~UNIT_RFLAGS) |\r | |
2252 | (flg & UNIT_RFLAGS); /* restore */\r | |
2253 | READ_S (buf); /* attached file */\r | |
2254 | if ((uptr->flags & UNIT_ATTABLE) && /* if attachable and */\r | |
2255 | (!(dptr->flags & DEV_NET) || /* not net dev or */\r | |
2256 | !(uptr->flags & UNIT_ATT) || /* not currently att */\r | |
2257 | (buf[0] == 0))) { /* or will not be att */\r | |
2258 | r = scp_detach_unit (dptr, uptr); /* detach old */\r | |
2259 | if (r != SCPE_OK) return r;\r | |
2260 | if (buf[0] != 0) { /* any file? */\r | |
2261 | uptr->flags = uptr->flags & ~UNIT_DIS;\r | |
2262 | if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */\r | |
2263 | sim_switches |= SWMASK ('R'); /* RO attach */\r | |
2264 | r = scp_attach_unit (dptr, uptr, buf);\r | |
2265 | if (r != SCPE_OK) return r;\r | |
2266 | }\r | |
2267 | }\r | |
2268 | READ_I (high); /* memory capacity */\r | |
2269 | if (high > 0) { /* [V2.5+] any memory? */\r | |
2270 | if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||\r | |
2271 | (dptr->deposit == NULL)) {\r | |
2272 | printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno);\r | |
2273 | return SCPE_INCOMP;\r | |
2274 | }\r | |
2275 | if (high != old_capac) { /* size change? */\r | |
2276 | uptr->capac = old_capac; /* temp restore old */\r | |
2277 | if ((dptr->flags & DEV_DYNM) &&\r | |
2278 | ((dptr->msize == NULL) ||\r | |
2279 | (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) {\r | |
2280 | printf ("Can't change memory size: %s%d\n",\r | |
2281 | sim_dname (dptr), unitno);\r | |
2282 | return SCPE_INCOMP;\r | |
2283 | }\r | |
2284 | uptr->capac = high; /* new memory size */\r | |
2285 | printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno);\r | |
2286 | fprint_capac (stdout, dptr, uptr);\r | |
2287 | printf ("\n");\r | |
2288 | }\r | |
2289 | sz = SZ_D (dptr); /* allocate buffer */\r | |
2290 | if ((mbuf = calloc (SRBSIZ, sz)) == NULL)\r | |
2291 | return SCPE_MEM;\r | |
2292 | for (k = 0; k < high; ) { /* loop thru mem */\r | |
2293 | READ_I (blkcnt); /* block count */\r | |
2294 | if (blkcnt < 0) limit = -blkcnt; /* compressed? */\r | |
2295 | else limit = sim_fread (mbuf, sz, blkcnt, rfile);\r | |
2296 | if (limit <= 0) return SCPE_IOERR; /* invalid or err? */\r | |
2297 | for (j = 0; j < limit; j++, k = k + (dptr->aincr)) {\r | |
2298 | if (blkcnt < 0) val = 0; /* compressed? */\r | |
2299 | else SZ_LOAD (sz, val, mbuf, j); /* saved value */\r | |
2300 | r = dptr->deposit (val, k, uptr, SIM_SW_REST);\r | |
2301 | if (r != SCPE_OK) return r;\r | |
2302 | } /* end for j */\r | |
2303 | } /* end for k */\r | |
2304 | free (mbuf); /* dealloc buffer */\r | |
2305 | } /* end if high */\r | |
2306 | } /* end unit loop */\r | |
2307 | for ( ;; ) { /* register loop */\r | |
2308 | READ_S (buf); /* read reg name */\r | |
2309 | if (buf[0] == 0) break; /* last? */\r | |
2310 | READ_I (depth); /* [V2.10+] depth */\r | |
2311 | if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {\r | |
2312 | printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf);\r | |
2313 | for (us = 0; us < depth; us++) { /* skip values */\r | |
2314 | READ_I (val);\r | |
2315 | }\r | |
2316 | continue;\r | |
2317 | }\r | |
2318 | if (depth != rptr->depth) /* [V2.10+] mismatch? */\r | |
2319 | printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n",\r | |
2320 | sim_dname (dptr), buf, depth, rptr->depth);\r | |
2321 | mask = width_mask[rptr->width]; /* get mask */\r | |
2322 | for (us = 0; us < depth; us++) { /* loop thru values */\r | |
2323 | READ_I (val); /* read value */\r | |
2324 | if (val > mask) /* value ok? */\r | |
2325 | printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf);\r | |
2326 | else if (us < rptr->depth) /* in range? */\r | |
2327 | put_rval (rptr, us, val);\r | |
2328 | }\r | |
2329 | }\r | |
2330 | } /* end device loop */\r | |
2331 | return SCPE_OK;\r | |
2332 | }\r | |
2333 | \r | |
2334 | /* Run, go, cont, step commands\r | |
2335 | \r | |
2336 | ru[n] [new PC] reset and start simulation\r | |
2337 | go [new PC] start simulation\r | |
2338 | co[nt] start simulation\r | |
2339 | s[tep] [step limit] start simulation for 'limit' instructions\r | |
2340 | b[oot] device bootstrap from device and start simulation\r | |
2341 | */\r | |
2342 | \r | |
2343 | t_stat run_cmd (int32 flag, char *cptr)\r | |
2344 | {\r | |
2345 | char *tptr, gbuf[CBUFSIZE];\r | |
2346 | uint32 i, j;\r | |
2347 | int32 unitno;\r | |
2348 | t_value pcv;\r | |
2349 | t_stat r;\r | |
2350 | DEVICE *dptr;\r | |
2351 | UNIT *uptr;\r | |
2352 | void int_handler (int signal);\r | |
2353 | \r | |
2354 | GET_SWITCHES (cptr); /* get switches */\r | |
2355 | sim_step = 0;\r | |
2356 | if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */\r | |
2357 | if (*cptr != 0) { /* argument? */\r | |
2358 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
2359 | if (*cptr != 0) return SCPE_2MARG; /* should be end */\r | |
2360 | if (sim_vm_parse_addr) /* address parser? */\r | |
2361 | pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr);\r | |
2362 | else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */\r | |
2363 | if ((tptr == gbuf) || (*tptr != 0) || /* error? */\r | |
2364 | (pcv > width_mask[sim_PC->width])) return SCPE_ARG;\r | |
2365 | put_rval (sim_PC, 0, pcv);\r | |
2366 | }\r | |
2367 | if ((flag == RU_RUN) && /* run? */\r | |
2368 | ((r = run_boot_prep ()) != SCPE_OK)) return r; /* reset sim */\r | |
2369 | }\r | |
2370 | \r | |
2371 | else if (flag == RU_STEP) { /* step */\r | |
2372 | if (*cptr != 0) { /* argument? */\r | |
2373 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
2374 | if (*cptr != 0) return SCPE_2MARG; /* should be end */\r | |
2375 | sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r);\r | |
2376 | if ((r != SCPE_OK) || (sim_step <= 0)) /* error? */\r | |
2377 | return SCPE_ARG;\r | |
2378 | }\r | |
2379 | else sim_step = 1;\r | |
2380 | }\r | |
2381 | \r | |
2382 | else if (flag == RU_BOOT) { /* boot */\r | |
2383 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
2384 | cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */\r | |
2385 | if (*cptr != 0) return SCPE_2MARG; /* should be end */\r | |
2386 | dptr = find_unit (gbuf, &uptr); /* locate unit */\r | |
2387 | if (dptr == NULL) return SCPE_NXDEV; /* found dev? */\r | |
2388 | if (uptr == NULL) return SCPE_NXUN; /* valid unit? */\r | |
2389 | if (dptr->boot == NULL) return SCPE_NOFNC; /* can it boot? */\r | |
2390 | if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */\r | |
2391 | if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */\r | |
2392 | !(uptr->flags & UNIT_ATT)) return SCPE_UNATT;\r | |
2393 | unitno = (int32) (uptr - dptr->units); /* recover unit# */\r | |
2394 | if ((r = run_boot_prep ()) != SCPE_OK) return r; /* reset sim */\r | |
2395 | if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */\r | |
2396 | return r;\r | |
2397 | }\r | |
2398 | \r | |
2399 | else if (flag != RU_CONT) return SCPE_IERR; /* must be cont */\r | |
2400 | \r | |
2401 | for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */\r | |
2402 | for (j = 0; j < dptr->numunits; j++) { /* seq devices */\r | |
2403 | uptr = dptr->units + j;\r | |
2404 | if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) ==\r | |
2405 | (UNIT_ATT + UNIT_SEQ))\r | |
2406 | sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);\r | |
2407 | }\r | |
2408 | }\r | |
2409 | stop_cpu = 0;\r | |
2410 | if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */\r | |
2411 | return SCPE_SIGERR;\r | |
2412 | }\r | |
2413 | if (sim_ttrun () != SCPE_OK) { /* set console mode */\r | |
2414 | sim_ttcmd ();\r | |
2415 | return SCPE_TTYERR;\r | |
2416 | }\r | |
2417 | if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */\r | |
2418 | sim_ttcmd ();\r | |
2419 | return r;\r | |
2420 | }\r | |
2421 | if (sim_step) sim_activate (&sim_step_unit, sim_step); /* set step timer */\r | |
2422 | sim_throt_sched (); /* set throttle */\r | |
2423 | sim_is_running = 1; /* flag running */\r | |
2424 | sim_brk_clract (); /* defang actions */\r | |
2425 | sim_rtcn_init_all (); /* re-init clocks */\r | |
2426 | r = sim_instr();\r | |
2427 | \r | |
2428 | sim_is_running = 0; /* flag idle */\r | |
2429 | sim_ttcmd (); /* restore console */\r | |
2430 | signal (SIGINT, SIG_DFL); /* cancel WRU */\r | |
2431 | sim_cancel (&sim_step_unit); /* cancel step timer */\r | |
2432 | sim_throt_cancel (); /* cancel throttle */\r | |
2433 | if (sim_clock_queue != NULL) { /* update sim time */\r | |
2434 | UPDATE_SIM_TIME (sim_clock_queue->time);\r | |
2435 | }\r | |
2436 | else {\r | |
2437 | UPDATE_SIM_TIME (noqueue_time);\r | |
2438 | }\r | |
2439 | if (sim_log) fflush (sim_log); /* flush console log */\r | |
2440 | if (sim_deb) fflush (sim_deb); /* flush debug log */\r | |
2441 | for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */\r | |
2442 | for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */\r | |
2443 | uptr = dptr->units + j;\r | |
2444 | if ((uptr->flags & UNIT_ATT) && /* attached, */\r | |
2445 | !(uptr->flags & UNIT_BUF) && /* not buffered, */\r | |
2446 | (uptr->fileref) && /* real file, */\r | |
2447 | !(uptr->flags & UNIT_RAW) && /* not raw, */\r | |
2448 | !(uptr->flags & UNIT_RO)) /* not read only? */\r | |
2449 | fflush (uptr->fileref);\r | |
2450 | }\r | |
2451 | }\r | |
2452 | #if defined (VMS)\r | |
2453 | printf ("\n");\r | |
2454 | #endif\r | |
2455 | fprint_stopped (stdout, r); /* print msg */\r | |
2456 | if (sim_log) fprint_stopped (sim_log, r); /* log if enabled */\r | |
2457 | return SCPE_OK;\r | |
2458 | }\r | |
2459 | \r | |
2460 | /* Common setup for RUN or BOOT */\r | |
2461 | \r | |
2462 | t_stat run_boot_prep (void)\r | |
2463 | {\r | |
2464 | sim_interval = 0; /* reset queue */\r | |
2465 | sim_time = sim_rtime = 0;\r | |
2466 | noqueue_time = 0;\r | |
2467 | sim_clock_queue = NULL;\r | |
2468 | return reset_all_p (0);\r | |
2469 | }\r | |
2470 | \r | |
2471 | /* Print stopped message */\r | |
2472 | \r | |
2473 | void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr)\r | |
2474 | {\r | |
2475 | int32 i;\r | |
2476 | t_stat r = 0;\r | |
2477 | t_addr k;\r | |
2478 | t_value pcval;\r | |
2479 | \r | |
2480 | if (v >= SCPE_BASE) fprintf (st, "\n%s, %s: ",\r | |
2481 | scp_error_messages[v - SCPE_BASE], pc->name);\r | |
2482 | else fprintf (st, "\n%s, %s: ", sim_stop_messages[v], pc->name);\r | |
2483 | pcval = get_rval (pc, 0);\r | |
2484 | if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, (t_addr) pcval);\r | |
2485 | else fprint_val (st, pcval, pc->radix, pc->width,\r | |
2486 | pc->flags & REG_FMT);\r | |
2487 | if ((dptr != NULL) && (dptr->examine != NULL)) {\r | |
2488 | for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;\r | |
2489 | for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) {\r | |
2490 | if ((r = dptr->examine (&sim_eval[i], k, dptr->units,\r | |
2491 | SWMASK ('V'))) != SCPE_OK) break;\r | |
2492 | }\r | |
2493 | if ((r == SCPE_OK) || (i > 0)) {\r | |
2494 | fprintf (st, " (");\r | |
2495 | if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0)\r | |
2496 | fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO);\r | |
2497 | fprintf (st, ")");\r | |
2498 | }\r | |
2499 | }\r | |
2500 | fprintf (st, "\n");\r | |
2501 | return;\r | |
2502 | }\r | |
2503 | \r | |
2504 | void fprint_stopped (FILE *st, t_stat v)\r | |
2505 | {\r | |
2506 | fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev);\r | |
2507 | return;\r | |
2508 | }\r | |
2509 | \r | |
2510 | /* Unit service for step timeout, originally scheduled by STEP n command\r | |
2511 | Return step timeout SCP code, will cause simulation to stop */\r | |
2512 | \r | |
2513 | t_stat step_svc (UNIT *uptr)\r | |
2514 | {\r | |
2515 | return SCPE_STEP;\r | |
2516 | }\r | |
2517 | \r | |
2518 | /* Cancel scheduled step service */\r | |
2519 | \r | |
2520 | t_stat sim_cancel_step (void)\r | |
2521 | {\r | |
2522 | return sim_cancel (&sim_step_unit);\r | |
2523 | }\r | |
2524 | \r | |
2525 | /* Signal handler for ^C signal - set stop simulation flag */\r | |
2526 | \r | |
2527 | void int_handler (int sig)\r | |
2528 | {\r | |
2529 | stop_cpu = 1;\r | |
2530 | return;\r | |
2531 | }\r | |
2532 | \r | |
2533 | /* Examine/deposit commands\r | |
2534 | \r | |
2535 | ex[amine] [modifiers] list examine\r | |
2536 | de[posit] [modifiers] list val deposit\r | |
2537 | ie[xamine] [modifiers] list interactive examine\r | |
2538 | id[eposit] [modifiers] list interactive deposit\r | |
2539 | \r | |
2540 | modifiers\r | |
2541 | @filename output file\r | |
2542 | -letter(s) switches\r | |
2543 | devname'n device name and unit number\r | |
2544 | [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification\r | |
2545 | \r | |
2546 | list list of addresses and registers\r | |
2547 | addr[:addr|-addr] address range\r | |
2548 | ALL all addresses\r | |
2549 | register[:register|-register] register range\r | |
2550 | STATE all registers\r | |
2551 | */\r | |
2552 | \r | |
2553 | t_stat exdep_cmd (int32 flag, char *cptr)\r | |
2554 | {\r | |
2555 | char gbuf[CBUFSIZE], *gptr, *tptr;\r | |
2556 | int32 opt;\r | |
2557 | t_addr low, high;\r | |
2558 | t_stat reason;\r | |
2559 | DEVICE *tdptr;\r | |
2560 | REG *lowr, *highr;\r | |
2561 | FILE *ofile;\r | |
2562 | \r | |
2563 | opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */\r | |
2564 | if (flag == EX_E) opt = opt | CMD_OPT_OF; /* extra for EX */\r | |
2565 | cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */\r | |
2566 | if (!cptr) return reason; /* error? */\r | |
2567 | if (*cptr == 0) return SCPE_2FARG; /* must be more */\r | |
2568 | if (sim_dfunit == NULL) return SCPE_NXUN; /* got a unit? */\r | |
2569 | cptr = get_glyph (cptr, gbuf, 0); /* get list */\r | |
2570 | if ((flag == EX_D) && (*cptr == 0)) return SCPE_2FARG; /* deposit needs more */\r | |
2571 | ofile = sim_ofile? sim_ofile: stdout; /* no ofile? use stdout */\r | |
2572 | \r | |
2573 | for (gptr = gbuf, reason = SCPE_OK;\r | |
2574 | (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {\r | |
2575 | tdptr = sim_dfdev; /* working dptr */\r | |
2576 | if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {\r | |
2577 | tptr = gptr + strlen ("STATE");\r | |
2578 | if (*tptr && (*tptr++ != ',')) return SCPE_ARG;\r | |
2579 | if ((lowr = sim_dfdev->registers) == NULL) return SCPE_NXREG;\r | |
2580 | for (highr = lowr; highr->name != NULL; highr++) ;\r | |
2581 | sim_switches = sim_switches | SIM_SW_HIDE;\r | |
2582 | reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr,\r | |
2583 | lowr, --highr, 0, 0);\r | |
2584 | continue;\r | |
2585 | }\r | |
2586 | \r | |
2587 | if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */\r | |
2588 | (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */\r | |
2589 | (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) {\r | |
2590 | low = high = 0;\r | |
2591 | if ((*tptr == '-') || (*tptr == ':')) {\r | |
2592 | highr = find_reg (tptr + 1, &tptr, tdptr);\r | |
2593 | if (highr == NULL) return SCPE_NXREG;\r | |
2594 | }\r | |
2595 | else {\r | |
2596 | highr = lowr;\r | |
2597 | if (*tptr == '[') {\r | |
2598 | if (lowr->depth <= 1) return SCPE_ARG;\r | |
2599 | tptr = get_range (NULL, tptr + 1, &low, &high,\r | |
2600 | 10, lowr->depth - 1, ']');\r | |
2601 | if (tptr == NULL) return SCPE_ARG;\r | |
2602 | }\r | |
2603 | }\r | |
2604 | if (*tptr && (*tptr++ != ',')) return SCPE_ARG;\r | |
2605 | reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr,\r | |
2606 | lowr, highr, (uint32) low, (uint32) high);\r | |
2607 | continue;\r | |
2608 | }\r | |
2609 | \r | |
2610 | tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix,\r | |
2611 | (((sim_dfunit->capac == 0) || (flag == EX_E))? 0:\r | |
2612 | sim_dfunit->capac - sim_dfdev->aincr), 0);\r | |
2613 | if (tptr == NULL) return SCPE_ARG;\r | |
2614 | if (*tptr && (*tptr++ != ',')) return SCPE_ARG;\r | |
2615 | reason = exdep_addr_loop (ofile, sim_schptr, flag, cptr, low, high,\r | |
2616 | sim_dfdev, sim_dfunit);\r | |
2617 | } /* end for */\r | |
2618 | if (sim_ofile) fclose (sim_ofile); /* close output file */\r | |
2619 | return reason;\r | |
2620 | }\r | |
2621 | \r | |
2622 | /* Loop controllers for examine/deposit\r | |
2623 | \r | |
2624 | exdep_reg_loop examine/deposit range of registers\r | |
2625 | exdep_addr_loop examine/deposit range of addresses\r | |
2626 | */\r | |
2627 | \r | |
2628 | t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, \r | |
2629 | REG *lowr, REG *highr, uint32 lows, uint32 highs)\r | |
2630 | {\r | |
2631 | t_stat reason;\r | |
2632 | uint32 idx;\r | |
2633 | t_value val;\r | |
2634 | REG *rptr;\r | |
2635 | \r | |
2636 | if ((lowr == NULL) || (highr == NULL)) return SCPE_IERR;\r | |
2637 | if (lowr > highr) return SCPE_ARG;\r | |
2638 | for (rptr = lowr; rptr <= highr; rptr++) {\r | |
2639 | if ((sim_switches & SIM_SW_HIDE) &&\r | |
2640 | (rptr->flags & REG_HIDDEN)) continue;\r | |
2641 | for (idx = lows; idx <= highs; idx++) {\r | |
2642 | if (idx >= rptr->depth) return SCPE_SUB;\r | |
2643 | val = get_rval (rptr, idx);\r | |
2644 | if (schptr && !test_search (val, schptr)) continue;\r | |
2645 | if (flag != EX_D) {\r | |
2646 | reason = ex_reg (ofile, val, flag, rptr, idx);\r | |
2647 | if (reason != SCPE_OK) return reason;\r | |
2648 | if (sim_log && (ofile == stdout))\r | |
2649 | ex_reg (sim_log, val, flag, rptr, idx);\r | |
2650 | }\r | |
2651 | if (flag != EX_E) {\r | |
2652 | reason = dep_reg (flag, cptr, rptr, idx);\r | |
2653 | if (reason != SCPE_OK) return reason;\r | |
2654 | }\r | |
2655 | }\r | |
2656 | }\r | |
2657 | return SCPE_OK;\r | |
2658 | }\r | |
2659 | \r | |
2660 | t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr,\r | |
2661 | t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)\r | |
2662 | {\r | |
2663 | t_addr i, mask;\r | |
2664 | t_stat reason;\r | |
2665 | \r | |
2666 | if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */\r | |
2667 | mask = (t_addr) width_mask[dptr->awidth];\r | |
2668 | if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG;\r | |
2669 | for (i = low; i <= high; ) { /* all paths must incr!! */\r | |
2670 | reason = get_aval (i, dptr, uptr); /* get data */\r | |
2671 | if (reason != SCPE_OK) return reason; /* return if error */\r | |
2672 | if (schptr && !test_search (sim_eval[0], schptr))\r | |
2673 | i = i + dptr->aincr; /* sch fails, incr */\r | |
2674 | else { /* no sch or success */\r | |
2675 | if (flag != EX_D) { /* ex, ie, or id? */\r | |
2676 | reason = ex_addr (ofile, flag, i, dptr, uptr);\r | |
2677 | if (reason > SCPE_OK) return reason;\r | |
2678 | if (sim_log && (ofile == stdout))\r | |
2679 | ex_addr (sim_log, flag, i, dptr, uptr);\r | |
2680 | }\r | |
2681 | else reason = 1 - dptr->aincr; /* no, dflt incr */\r | |
2682 | if (flag != EX_E) { /* ie, id, or d? */\r | |
2683 | reason = dep_addr (flag, cptr, i, dptr, uptr, reason);\r | |
2684 | if (reason > SCPE_OK) return reason;\r | |
2685 | }\r | |
2686 | i = i + (1 - reason); /* incr */\r | |
2687 | }\r | |
2688 | }\r | |
2689 | return SCPE_OK;\r | |
2690 | }\r | |
2691 | \r | |
2692 | /* Examine register routine\r | |
2693 | \r | |
2694 | Inputs:\r | |
2695 | ofile = output stream\r | |
2696 | val = current register value\r | |
2697 | flag = type of ex/mod command (ex, iex, idep)\r | |
2698 | rptr = pointer to register descriptor\r | |
2699 | idx = index\r | |
2700 | Outputs:\r | |
2701 | return = error status\r | |
2702 | */\r | |
2703 | \r | |
2704 | t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx)\r | |
2705 | {\r | |
2706 | int32 rdx;\r | |
2707 | \r | |
2708 | if (rptr == NULL) return SCPE_IERR;\r | |
2709 | if (rptr->depth > 1) fprintf (ofile, "%s[%d]:\t", rptr->name, idx);\r | |
2710 | else fprintf (ofile, "%s:\t", rptr->name);\r | |
2711 | if (!(flag & EX_E)) return SCPE_OK;\r | |
2712 | GET_RADIX (rdx, rptr->radix);\r | |
2713 | if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr)\r | |
2714 | sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val);\r | |
2715 | else if (!(rptr->flags & REG_VMIO) ||\r | |
2716 | (fprint_sym (ofile, rdx, &val, NULL, sim_switches | SIM_SW_REG) > 0))\r | |
2717 | fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT);\r | |
2718 | if (flag & EX_I) fprintf (ofile, "\t");\r | |
2719 | else fprintf (ofile, "\n");\r | |
2720 | return SCPE_OK;\r | |
2721 | }\r | |
2722 | \r | |
2723 | /* Get register value\r | |
2724 | \r | |
2725 | Inputs:\r | |
2726 | rptr = pointer to register descriptor\r | |
2727 | idx = index\r | |
2728 | Outputs:\r | |
2729 | return = register value\r | |
2730 | */\r | |
2731 | \r | |
2732 | t_value get_rval (REG *rptr, uint32 idx)\r | |
2733 | {\r | |
2734 | size_t sz;\r | |
2735 | t_value val;\r | |
2736 | UNIT *uptr;\r | |
2737 | \r | |
2738 | sz = SZ_R (rptr);\r | |
2739 | if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {\r | |
2740 | idx = idx + rptr->qptr;\r | |
2741 | if (idx >= rptr->depth) idx = idx - rptr->depth;\r | |
2742 | }\r | |
2743 | if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {\r | |
2744 | uptr = ((UNIT *) rptr->loc) + idx;\r | |
2745 | #if defined (USE_INT64)\r | |
2746 | if (sz <= sizeof (uint32)) val = *((uint32 *) uptr);\r | |
2747 | else val = *((t_uint64 *) uptr);\r | |
2748 | #else\r | |
2749 | val = *((uint32 *) uptr);\r | |
2750 | #endif\r | |
2751 | }\r | |
2752 | else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&\r | |
2753 | (sz == sizeof (uint8)))\r | |
2754 | val = *(((uint8 *) rptr->loc) + idx);\r | |
2755 | else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&\r | |
2756 | (sz == sizeof (uint16)))\r | |
2757 | val = *(((uint16 *) rptr->loc) + idx);\r | |
2758 | #if defined (USE_INT64)\r | |
2759 | else if (sz <= sizeof (uint32))\r | |
2760 | val = *(((uint32 *) rptr->loc) + idx);\r | |
2761 | else val = *(((t_uint64 *) rptr->loc) + idx);\r | |
2762 | #else\r | |
2763 | else val = *(((uint32 *) rptr->loc) + idx);\r | |
2764 | #endif\r | |
2765 | val = (val >> rptr->offset) & width_mask[rptr->width];\r | |
2766 | return val;\r | |
2767 | }\r | |
2768 | \r | |
2769 | /* Deposit register routine\r | |
2770 | \r | |
2771 | Inputs:\r | |
2772 | flag = type of deposit (normal/interactive)\r | |
2773 | cptr = pointer to input string\r | |
2774 | rptr = pointer to register descriptor\r | |
2775 | idx = index\r | |
2776 | Outputs:\r | |
2777 | return = error status\r | |
2778 | */\r | |
2779 | \r | |
2780 | t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx)\r | |
2781 | {\r | |
2782 | t_stat r;\r | |
2783 | t_value val, mask;\r | |
2784 | int32 rdx;\r | |
2785 | char *tptr, gbuf[CBUFSIZE];\r | |
2786 | \r | |
2787 | if ((cptr == NULL) || (rptr == NULL)) return SCPE_IERR;\r | |
2788 | if (rptr->flags & REG_RO) return SCPE_RO;\r | |
2789 | if (flag & EX_I) {\r | |
2790 | cptr = read_line (gbuf, CBUFSIZE, stdin);\r | |
2791 | if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);\r | |
2792 | if (cptr == NULL) return 1; /* force exit */\r | |
2793 | if (*cptr == 0) return SCPE_OK; /* success */\r | |
2794 | }\r | |
2795 | mask = width_mask[rptr->width];\r | |
2796 | GET_RADIX (rdx, rptr->radix);\r | |
2797 | if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr) { /* address form? */\r | |
2798 | val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr);\r | |
2799 | if ((tptr == cptr) || (*tptr != 0) ||\r | |
2800 | (val > mask)) return SCPE_ARG;\r | |
2801 | }\r | |
2802 | else if (!(rptr->flags & REG_VMIO) || /* dont use sym? */\r | |
2803 | (parse_sym (cptr, rdx, NULL, &val, sim_switches | SIM_SW_REG) > SCPE_OK)) {\r | |
2804 | val = get_uint (cptr, rdx, mask, &r);\r | |
2805 | if (r != SCPE_OK) return SCPE_ARG;\r | |
2806 | }\r | |
2807 | if ((rptr->flags & REG_NZ) && (val == 0)) return SCPE_ARG;\r | |
2808 | put_rval (rptr, idx, val);\r | |
2809 | return SCPE_OK;\r | |
2810 | }\r | |
2811 | \r | |
2812 | /* Put register value\r | |
2813 | \r | |
2814 | Inputs:\r | |
2815 | rptr = pointer to register descriptor\r | |
2816 | idx = index\r | |
2817 | val = new value\r | |
2818 | mask = mask\r | |
2819 | Outputs:\r | |
2820 | none\r | |
2821 | */\r | |
2822 | \r | |
2823 | void put_rval (REG *rptr, uint32 idx, t_value val)\r | |
2824 | {\r | |
2825 | size_t sz;\r | |
2826 | t_value mask;\r | |
2827 | UNIT *uptr;\r | |
2828 | \r | |
2829 | #define PUT_RVAL(sz,rp,id,v,m) \\r | |
2830 | *(((sz *) rp->loc) + id) = \\r | |
2831 | (*(((sz *) rp->loc) + id) & \\r | |
2832 | ~((m) << (rp)->offset)) | ((v) << (rp)->offset)\r | |
2833 | \r | |
2834 | if (rptr == sim_PC) sim_brk_npc (0);\r | |
2835 | sz = SZ_R (rptr);\r | |
2836 | mask = width_mask[rptr->width];\r | |
2837 | if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) {\r | |
2838 | idx = idx + rptr->qptr;\r | |
2839 | if (idx >= rptr->depth) idx = idx - rptr->depth;\r | |
2840 | }\r | |
2841 | if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) {\r | |
2842 | uptr = ((UNIT *) rptr->loc) + idx;\r | |
2843 | #if defined (USE_INT64)\r | |
2844 | if (sz <= sizeof (uint32))\r | |
2845 | *((uint32 *) uptr) = (*((uint32 *) uptr) &\r | |
2846 | ~(((uint32) mask) << rptr->offset)) | \r | |
2847 | (((uint32) val) << rptr->offset);\r | |
2848 | else *((t_uint64 *) uptr) = (*((t_uint64 *) uptr)\r | |
2849 | & ~(mask << rptr->offset)) | (val << rptr->offset);\r | |
2850 | #else\r | |
2851 | *((uint32 *) uptr) = (*((uint32 *) uptr) &\r | |
2852 | ~(((uint32) mask) << rptr->offset)) | \r | |
2853 | (((uint32) val) << rptr->offset);\r | |
2854 | #endif\r | |
2855 | }\r | |
2856 | else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&\r | |
2857 | (sz == sizeof (uint8)))\r | |
2858 | PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);\r | |
2859 | else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) &&\r | |
2860 | (sz == sizeof (uint16)))\r | |
2861 | PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);\r | |
2862 | #if defined (USE_INT64)\r | |
2863 | else if (sz <= sizeof (uint32))\r | |
2864 | PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);\r | |
2865 | else PUT_RVAL (t_uint64, rptr, idx, val, mask);\r | |
2866 | #else\r | |
2867 | else PUT_RVAL (uint32, rptr, idx, val, mask);\r | |
2868 | #endif\r | |
2869 | return;\r | |
2870 | }\r | |
2871 | \r | |
2872 | /* Examine address routine\r | |
2873 | \r | |
2874 | Inputs: (sim_eval is an implicit argument)\r | |
2875 | ofile = output stream\r | |
2876 | flag = type of ex/mod command (ex, iex, idep)\r | |
2877 | addr = address to examine\r | |
2878 | dptr = pointer to device\r | |
2879 | uptr = pointer to unit\r | |
2880 | Outputs:\r | |
2881 | return = if > 0, error status\r | |
2882 | if <= 0,-number of extra addr units retired\r | |
2883 | */\r | |
2884 | \r | |
2885 | t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr)\r | |
2886 | {\r | |
2887 | t_stat reason;\r | |
2888 | int32 rdx;\r | |
2889 | \r | |
2890 | if (sim_vm_fprint_addr) sim_vm_fprint_addr (ofile, dptr, addr);\r | |
2891 | else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT);\r | |
2892 | fprintf (ofile, ":\t");\r | |
2893 | if (!(flag & EX_E)) return (1 - dptr->aincr);\r | |
2894 | \r | |
2895 | GET_RADIX (rdx, dptr->dradix);\r | |
2896 | if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) {\r | |
2897 | fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO);\r | |
2898 | reason = 1 - dptr->aincr;\r | |
2899 | }\r | |
2900 | if (flag & EX_I) fprintf (ofile, "\t");\r | |
2901 | else fprintf (ofile, "\n");\r | |
2902 | return reason;\r | |
2903 | }\r | |
2904 | \r | |
2905 | /* Get address routine\r | |
2906 | \r | |
2907 | Inputs:\r | |
2908 | flag = type of ex/mod command (ex, iex, idep)\r | |
2909 | addr = address to examine\r | |
2910 | dptr = pointer to device\r | |
2911 | uptr = pointer to unit\r | |
2912 | Outputs: (sim_eval is an implicit output)\r | |
2913 | return = error status\r | |
2914 | */\r | |
2915 | \r | |
2916 | t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)\r | |
2917 | {\r | |
2918 | int32 i;\r | |
2919 | t_value mask;\r | |
2920 | t_addr j, loc;\r | |
2921 | size_t sz;\r | |
2922 | t_stat reason = SCPE_OK;\r | |
2923 | \r | |
2924 | if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;\r | |
2925 | mask = width_mask[dptr->dwidth];\r | |
2926 | for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;\r | |
2927 | for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) {\r | |
2928 | if (dptr->examine != NULL) {\r | |
2929 | reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches);\r | |
2930 | if (reason != SCPE_OK) break;\r | |
2931 | }\r | |
2932 | else {\r | |
2933 | if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;\r | |
2934 | if (uptr->flags & UNIT_RAW) return SCPE_NOFNC;\r | |
2935 | if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) {\r | |
2936 | reason = SCPE_NXM;\r | |
2937 | break;\r | |
2938 | }\r | |
2939 | sz = SZ_D (dptr);\r | |
2940 | loc = j / dptr->aincr;\r | |
2941 | if (uptr->flags & UNIT_BUF) {\r | |
2942 | SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc);\r | |
2943 | }\r | |
2944 | else {\r | |
2945 | sim_fseek (uptr->fileref, sz * loc, SEEK_SET);\r | |
2946 | sim_fread (&sim_eval[i], sz, 1, uptr->fileref);\r | |
2947 | if ((feof (uptr->fileref)) &&\r | |
2948 | !(uptr->flags & UNIT_FIX)) {\r | |
2949 | reason = SCPE_EOF;\r | |
2950 | break;\r | |
2951 | }\r | |
2952 | else if (ferror (uptr->fileref)) {\r | |
2953 | clearerr (uptr->fileref);\r | |
2954 | reason = SCPE_IOERR;\r | |
2955 | break;\r | |
2956 | }\r | |
2957 | }\r | |
2958 | }\r | |
2959 | sim_eval[i] = sim_eval[i] & mask;\r | |
2960 | }\r | |
2961 | if ((reason != SCPE_OK) && (i == 0)) return reason;\r | |
2962 | return SCPE_OK;\r | |
2963 | }\r | |
2964 | \r | |
2965 | /* Deposit address routine\r | |
2966 | \r | |
2967 | Inputs:\r | |
2968 | flag = type of deposit (normal/interactive)\r | |
2969 | cptr = pointer to input string\r | |
2970 | addr = address to examine\r | |
2971 | dptr = pointer to device\r | |
2972 | uptr = pointer to unit\r | |
2973 | dfltinc = value to return on cr input\r | |
2974 | Outputs:\r | |
2975 | return = if > 0, error status\r | |
2976 | if <= 0, -number of extra address units retired\r | |
2977 | */\r | |
2978 | \r | |
2979 | t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr,\r | |
2980 | UNIT *uptr, int32 dfltinc)\r | |
2981 | {\r | |
2982 | int32 i, count, rdx;\r | |
2983 | t_addr j, loc;\r | |
2984 | t_stat r, reason;\r | |
2985 | t_value mask;\r | |
2986 | size_t sz;\r | |
2987 | char gbuf[CBUFSIZE];\r | |
2988 | \r | |
2989 | if (dptr == NULL) return SCPE_IERR;\r | |
2990 | if (flag & EX_I) {\r | |
2991 | cptr = read_line (gbuf, CBUFSIZE, stdin);\r | |
2992 | if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);\r | |
2993 | if (cptr == NULL) return 1; /* force exit */\r | |
2994 | if (*cptr == 0) return dfltinc; /* success */\r | |
2995 | }\r | |
2996 | if (uptr->flags & UNIT_RO) return SCPE_RO; /* read only? */\r | |
2997 | mask = width_mask[dptr->dwidth];\r | |
2998 | \r | |
2999 | GET_RADIX (rdx, dptr->dradix);\r | |
3000 | if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) {\r | |
3001 | sim_eval[0] = get_uint (cptr, rdx, mask, &reason);\r | |
3002 | if (reason != SCPE_OK) return reason;\r | |
3003 | }\r | |
3004 | count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr;\r | |
3005 | \r | |
3006 | for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) {\r | |
3007 | sim_eval[i] = sim_eval[i] & mask;\r | |
3008 | if (dptr->deposit != NULL) {\r | |
3009 | r = dptr->deposit (sim_eval[i], j, uptr, sim_switches);\r | |
3010 | if (r != SCPE_OK) return r;\r | |
3011 | }\r | |
3012 | else {\r | |
3013 | if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT;\r | |
3014 | if (uptr->flags & UNIT_RAW) return SCPE_NOFNC;\r | |
3015 | if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac))\r | |
3016 | return SCPE_NXM;\r | |
3017 | sz = SZ_D (dptr);\r | |
3018 | loc = j / dptr->aincr;\r | |
3019 | if (uptr->flags & UNIT_BUF) {\r | |
3020 | SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc);\r | |
3021 | if (loc >= uptr->hwmark) uptr->hwmark = (uint32) loc + 1;\r | |
3022 | }\r | |
3023 | else {\r | |
3024 | sim_fseek (uptr->fileref, sz * loc, SEEK_SET);\r | |
3025 | sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref);\r | |
3026 | if (ferror (uptr->fileref)) {\r | |
3027 | clearerr (uptr->fileref);\r | |
3028 | return SCPE_IOERR;\r | |
3029 | }\r | |
3030 | }\r | |
3031 | }\r | |
3032 | }\r | |
3033 | return reason;\r | |
3034 | }\r | |
3035 | \r | |
3036 | /* Evaluate command */\r | |
3037 | \r | |
3038 | t_stat eval_cmd (int32 flg, char *cptr)\r | |
3039 | {\r | |
3040 | DEVICE *dptr = sim_dflt_dev;\r | |
3041 | int32 i, rdx, a, lim;\r | |
3042 | t_stat r;\r | |
3043 | \r | |
3044 | GET_SWITCHES (cptr);\r | |
3045 | GET_RADIX (rdx, dptr->dradix);\r | |
3046 | for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;\r | |
3047 | if (*cptr == 0) return SCPE_2FARG;\r | |
3048 | if ((r = parse_sym (cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) {\r | |
3049 | sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r);\r | |
3050 | if (r != SCPE_OK) return r;\r | |
3051 | }\r | |
3052 | lim = 1 - r;\r | |
3053 | for (i = a = 0; a < lim; ) {\r | |
3054 | printf ("%d:\t", a);\r | |
3055 | if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0)\r | |
3056 | r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);\r | |
3057 | printf ("\n");\r | |
3058 | if (sim_log) {\r | |
3059 | fprintf (sim_log, "%d\t", i);\r | |
3060 | if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0)\r | |
3061 | r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO);\r | |
3062 | fprintf (sim_log, "\n");\r | |
3063 | }\r | |
3064 | if (r < 0) a = a + 1 - r;\r | |
3065 | else a = a + dptr->aincr;\r | |
3066 | i = a / dptr->aincr;\r | |
3067 | }\r | |
3068 | return SCPE_OK;\r | |
3069 | }\r | |
3070 | \r | |
3071 | /* String processing routines\r | |
3072 | \r | |
3073 | read_line read line\r | |
3074 | \r | |
3075 | Inputs:\r | |
3076 | cptr = pointer to buffer\r | |
3077 | size = maximum size\r | |
3078 | stream = pointer to input stream\r | |
3079 | Outputs:\r | |
3080 | optr = pointer to first non-blank character\r | |
3081 | NULL if EOF\r | |
3082 | */\r | |
3083 | \r | |
3084 | char *read_line (char *cptr, int32 size, FILE *stream)\r | |
3085 | {\r | |
3086 | char *tptr;\r | |
3087 | \r | |
3088 | cptr = fgets (cptr, size, stream); /* get cmd line */\r | |
3089 | if (cptr == NULL) {\r | |
3090 | clearerr (stream); /* clear error */\r | |
3091 | return NULL; /* ignore EOF */\r | |
3092 | }\r | |
3093 | for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */\r | |
3094 | if ((*tptr == '\n') || (*tptr == '\r') ||\r | |
3095 | (tptr == (cptr + size - 1))) { /* str max length? */\r | |
3096 | *tptr = 0; /* terminate */\r | |
3097 | break;\r | |
3098 | }\r | |
3099 | }\r | |
3100 | while (isspace (*cptr)) cptr++; /* trim leading spc */\r | |
3101 | if (*cptr == ';') *cptr = 0; /* ignore comment */\r | |
3102 | \r | |
3103 | \r | |
3104 | return cptr;\r | |
3105 | }\r | |
3106 | \r | |
3107 | /* get_glyph get next glyph (force upper case)\r | |
3108 | get_glyph_nc get next glyph (no conversion)\r | |
3109 | get_glyph_gen get next glyph (general case)\r | |
3110 | \r | |
3111 | Inputs:\r | |
3112 | iptr = pointer to input string\r | |
3113 | optr = pointer to output string\r | |
3114 | mchar = optional end of glyph character\r | |
3115 | flag = TRUE for convert to upper case (_gen only)\r | |
3116 | Outputs\r | |
3117 | result = pointer to next character in input string\r | |
3118 | */\r | |
3119 | \r | |
3120 | char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc)\r | |
3121 | {\r | |
3122 | while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) {\r | |
3123 | if (islower (*iptr) && uc) *optr = toupper (*iptr);\r | |
3124 | else *optr = *iptr;\r | |
3125 | iptr++; optr++;\r | |
3126 | }\r | |
3127 | *optr = 0;\r | |
3128 | if (mchar && (*iptr == mchar)) iptr++; /* skip terminator */\r | |
3129 | while (isspace (*iptr)) iptr++; /* absorb spaces */\r | |
3130 | return iptr;\r | |
3131 | }\r | |
3132 | \r | |
3133 | char *get_glyph (char *iptr, char *optr, char mchar)\r | |
3134 | {\r | |
3135 | return get_glyph_gen (iptr, optr, mchar, TRUE);\r | |
3136 | }\r | |
3137 | \r | |
3138 | char *get_glyph_nc (char *iptr, char *optr, char mchar)\r | |
3139 | {\r | |
3140 | return get_glyph_gen (iptr, optr, mchar, FALSE);\r | |
3141 | }\r | |
3142 | \r | |
3143 | /* Trim trailing spaces from a string\r | |
3144 | \r | |
3145 | Inputs:\r | |
3146 | cptr = pointer to string\r | |
3147 | Outputs:\r | |
3148 | cptr = pointer to string\r | |
3149 | */\r | |
3150 | \r | |
3151 | char *sim_trim_endspc (char *cptr)\r | |
3152 | {\r | |
3153 | char *tptr;\r | |
3154 | \r | |
3155 | tptr = cptr + strlen (cptr);\r | |
3156 | while ((--tptr >= cptr) && isspace (*tptr)) *tptr = 0;\r | |
3157 | return cptr;\r | |
3158 | }\r | |
3159 | \r | |
3160 | /* get_yn yes/no question\r | |
3161 | \r | |
3162 | Inputs:\r | |
3163 | cptr = pointer to question\r | |
3164 | deflt = default answer\r | |
3165 | Outputs:\r | |
3166 | result = true if yes, false if no\r | |
3167 | */\r | |
3168 | \r | |
3169 | t_stat get_yn (char *ques, t_stat deflt)\r | |
3170 | {\r | |
3171 | char cbuf[CBUFSIZE], *cptr;\r | |
3172 | \r | |
3173 | printf ("%s ", ques);\r | |
3174 | cptr = read_line (cbuf, CBUFSIZE, stdin);\r | |
3175 | if ((cptr == NULL) || (*cptr == 0)) return deflt;\r | |
3176 | if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE;\r | |
3177 | return FALSE;\r | |
3178 | }\r | |
3179 | \r | |
3180 | /* get_uint unsigned number\r | |
3181 | \r | |
3182 | Inputs:\r | |
3183 | cptr = pointer to input string\r | |
3184 | radix = input radix\r | |
3185 | max = maximum acceptable value\r | |
3186 | *status = pointer to error status\r | |
3187 | Outputs:\r | |
3188 | val = value\r | |
3189 | */\r | |
3190 | \r | |
3191 | t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status)\r | |
3192 | {\r | |
3193 | t_value val;\r | |
3194 | char *tptr;\r | |
3195 | \r | |
3196 | *status = SCPE_OK;\r | |
3197 | val = strtotv (cptr, &tptr, radix);\r | |
3198 | if ((cptr == tptr) || (val > max)) *status = SCPE_ARG;\r | |
3199 | else {\r | |
3200 | while (isspace (*tptr)) tptr++;\r | |
3201 | if (*tptr != 0) *status = SCPE_ARG;\r | |
3202 | }\r | |
3203 | return val;\r | |
3204 | }\r | |
3205 | \r | |
3206 | /* get_range range specification\r | |
3207 | \r | |
3208 | Inputs:\r | |
3209 | dptr = pointer to device (NULL if none)\r | |
3210 | cptr = pointer to input string\r | |
3211 | *lo = pointer to low result\r | |
3212 | *hi = pointer to high result\r | |
3213 | aradix = radix\r | |
3214 | max = default high value\r | |
3215 | term = terminating character, 0 if none\r | |
3216 | Outputs:\r | |
3217 | tptr = input pointer after processing\r | |
3218 | NULL if error\r | |
3219 | */\r | |
3220 | \r | |
3221 | char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi,\r | |
3222 | uint32 rdx, t_addr max, char term)\r | |
3223 | {\r | |
3224 | char *tptr;\r | |
3225 | \r | |
3226 | if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */\r | |
3227 | tptr = cptr + strlen ("ALL");\r | |
3228 | *lo = 0;\r | |
3229 | *hi = max;\r | |
3230 | }\r | |
3231 | else {\r | |
3232 | if (dptr && sim_vm_parse_addr) /* get low */\r | |
3233 | *lo = sim_vm_parse_addr (dptr, cptr, &tptr);\r | |
3234 | else *lo = (t_addr) strtotv (cptr, &tptr, rdx);\r | |
3235 | if (cptr == tptr) return NULL; /* error? */\r | |
3236 | if ((*tptr == '-') || (*tptr == ':')) { /* range? */\r | |
3237 | cptr = tptr + 1;\r | |
3238 | if (dptr && sim_vm_parse_addr) /* get high */\r | |
3239 | *hi = sim_vm_parse_addr (dptr, cptr, &tptr);\r | |
3240 | else *hi = (t_addr) strtotv (cptr, &tptr, rdx);\r | |
3241 | if (cptr == tptr) return NULL;\r | |
3242 | if (*lo > *hi) return NULL;\r | |
3243 | }\r | |
3244 | else if (*tptr == '/') { /* relative? */\r | |
3245 | cptr = tptr + 1;\r | |
3246 | *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */\r | |
3247 | if ((cptr == tptr) || (*hi == 0)) return NULL;\r | |
3248 | *hi = *lo + *hi - 1;\r | |
3249 | }\r | |
3250 | else *hi = *lo;\r | |
3251 | }\r | |
3252 | if (term && (*tptr++ != term)) return NULL;\r | |
3253 | return tptr;\r | |
3254 | }\r | |
3255 | \r | |
3256 | /* get_ipaddr IP address:port\r | |
3257 | \r | |
3258 | Inputs:\r | |
3259 | cptr = pointer to input string\r | |
3260 | Outputs:\r | |
3261 | ipa = pointer to IP address (may be NULL), 0 = none\r | |
3262 | ipp = pointer to IP port (may be NULL), 0 = none\r | |
3263 | result = status\r | |
3264 | */\r | |
3265 | \r | |
3266 | t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp)\r | |
3267 | {\r | |
3268 | char gbuf[CBUFSIZE];\r | |
3269 | char *addrp, *portp, *octetp;\r | |
3270 | uint32 i, addr, port, octet;\r | |
3271 | t_stat r;\r | |
3272 | \r | |
3273 | if ((cptr == NULL) || (*cptr == 0))\r | |
3274 | return SCPE_ARG;\r | |
3275 | strncpy (gbuf, cptr, CBUFSIZE);\r | |
3276 | addrp = gbuf; /* default addr */\r | |
3277 | if (portp = strchr (gbuf, ':')) *portp++ = 0; /* x:y? split */\r | |
3278 | else if (strchr (gbuf, '.')) portp = NULL; /* x.y...? */\r | |
3279 | else {\r | |
3280 | portp = gbuf; /* port only */\r | |
3281 | addrp = NULL; /* no addr */\r | |
3282 | }\r | |
3283 | if (portp) { /* port string? */\r | |
3284 | if (ipp == NULL) return SCPE_ARG; /* not wanted? */\r | |
3285 | port = (int32) get_uint (portp, 10, 65535, &r);\r | |
3286 | if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG;\r | |
3287 | }\r | |
3288 | else port = 0;\r | |
3289 | if (addrp) { /* addr string? */\r | |
3290 | if (ipa == NULL) return SCPE_ARG; /* not wanted? */\r | |
3291 | for (i = addr = 0; i < 4; i++) { /* four octets */\r | |
3292 | octetp = strchr (addrp, '.'); /* find octet end */\r | |
3293 | if (octetp != NULL) *octetp++ = 0; /* split string */\r | |
3294 | else if (i < 3) return SCPE_ARG; /* except last */\r | |
3295 | octet = (int32) get_uint (addrp, 10, 255, &r);\r | |
3296 | if (r != SCPE_OK) return SCPE_ARG;\r | |
3297 | addr = (addr << 8) | octet;\r | |
3298 | addrp = octetp;\r | |
3299 | }\r | |
3300 | if (((addr & 0377) == 0) || ((addr & 0377) == 255))\r | |
3301 | return SCPE_ARG;\r | |
3302 | }\r | |
3303 | else addr = 0;\r | |
3304 | if (ipp) *ipp = port; /* return req values */\r | |
3305 | if (ipa) *ipa = addr;\r | |
3306 | return SCPE_OK; \r | |
3307 | }\r | |
3308 | \r | |
3309 | /* Find_device find device matching input string\r | |
3310 | \r | |
3311 | Inputs:\r | |
3312 | cptr = pointer to input string\r | |
3313 | Outputs:\r | |
3314 | result = pointer to device\r | |
3315 | */\r | |
3316 | \r | |
3317 | DEVICE *find_dev (char *cptr)\r | |
3318 | {\r | |
3319 | int32 i;\r | |
3320 | DEVICE *dptr;\r | |
3321 | \r | |
3322 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {\r | |
3323 | if ((strcmp (cptr, dptr->name) == 0) ||\r | |
3324 | (dptr->lname &&\r | |
3325 | (strcmp (cptr, dptr->lname) == 0))) return dptr;\r | |
3326 | }\r | |
3327 | return NULL;\r | |
3328 | }\r | |
3329 | \r | |
3330 | /* Find_unit find unit matching input string\r | |
3331 | \r | |
3332 | Inputs:\r | |
3333 | cptr = pointer to input string\r | |
3334 | uptr = pointer to unit pointer\r | |
3335 | Outputs:\r | |
3336 | result = pointer to device (null if no dev)\r | |
3337 | *iptr = pointer to unit (null if nx unit)\r | |
3338 | */\r | |
3339 | \r | |
3340 | DEVICE *find_unit (char *cptr, UNIT **uptr)\r | |
3341 | {\r | |
3342 | uint32 i, u;\r | |
3343 | char *nptr, *tptr;\r | |
3344 | t_stat r;\r | |
3345 | DEVICE *dptr;\r | |
3346 | \r | |
3347 | if (uptr == NULL) return NULL; /* arg error? */\r | |
3348 | if (dptr = find_dev (cptr)) { /* exact match? */\r | |
3349 | if (qdisable (dptr)) return NULL; /* disabled? */\r | |
3350 | *uptr = dptr->units; /* unit 0 */\r | |
3351 | return dptr;\r | |
3352 | }\r | |
3353 | \r | |
3354 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */\r | |
3355 | if (dptr->numunits && /* any units? */\r | |
3356 | (((nptr = dptr->name) &&\r | |
3357 | (strncmp (cptr, nptr, strlen (nptr)) == 0)) ||\r | |
3358 | ((nptr = dptr->lname) &&\r | |
3359 | (strncmp (cptr, nptr, strlen (nptr)) == 0)))) {\r | |
3360 | tptr = cptr + strlen (nptr);\r | |
3361 | if (isdigit (*tptr)) {\r | |
3362 | if (qdisable (dptr)) return NULL; /* disabled? */\r | |
3363 | u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r);\r | |
3364 | if (r != SCPE_OK) *uptr = NULL; /* error? */\r | |
3365 | else *uptr = dptr->units + u;\r | |
3366 | return dptr;\r | |
3367 | }\r | |
3368 | }\r | |
3369 | }\r | |
3370 | return NULL;\r | |
3371 | }\r | |
3372 | \r | |
3373 | /* Find_dev_from_unit find device for unit\r | |
3374 | \r | |
3375 | Inputs:\r | |
3376 | uptr = pointer to unit\r | |
3377 | Outputs:\r | |
3378 | result = pointer to device\r | |
3379 | */\r | |
3380 | \r | |
3381 | DEVICE *find_dev_from_unit (UNIT *uptr)\r | |
3382 | {\r | |
3383 | DEVICE *dptr;\r | |
3384 | uint32 i, j;\r | |
3385 | \r | |
3386 | if (uptr == NULL) return NULL;\r | |
3387 | for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {\r | |
3388 | for (j = 0; j < dptr->numunits; j++) {\r | |
3389 | if (uptr == (dptr->units + j)) return dptr;\r | |
3390 | }\r | |
3391 | }\r | |
3392 | return NULL;\r | |
3393 | }\r | |
3394 | \r | |
3395 | /* Test for disabled device */\r | |
3396 | \r | |
3397 | t_bool qdisable (DEVICE *dptr)\r | |
3398 | {\r | |
3399 | return (dptr->flags & DEV_DIS? TRUE: FALSE);\r | |
3400 | }\r | |
3401 | \r | |
3402 | /* find_reg_glob find globally unique register\r | |
3403 | \r | |
3404 | Inputs:\r | |
3405 | cptr = pointer to input string\r | |
3406 | optr = pointer to output pointer (can be null)\r | |
3407 | gdptr = pointer to global device\r | |
3408 | Outputs:\r | |
3409 | result = pointer to register, NULL if error\r | |
3410 | *optr = pointer to next character in input string\r | |
3411 | *gdptr = pointer to device where found\r | |
3412 | */\r | |
3413 | \r | |
3414 | REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr)\r | |
3415 | {\r | |
3416 | int32 i;\r | |
3417 | DEVICE *dptr;\r | |
3418 | REG *rptr, *srptr = NULL;\r | |
3419 | \r | |
3420 | for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */\r | |
3421 | if (dptr->flags & DEV_DIS) continue; /* skip disabled */\r | |
3422 | if (rptr = find_reg (cptr, optr, dptr)) { /* found? */\r | |
3423 | if (srptr) return NULL; /* ambig? err */\r | |
3424 | srptr = rptr; /* save reg */\r | |
3425 | *gdptr = dptr; /* save unit */\r | |
3426 | }\r | |
3427 | }\r | |
3428 | return srptr;\r | |
3429 | }\r | |
3430 | \r | |
3431 | /* find_reg find register matching input string\r | |
3432 | \r | |
3433 | Inputs:\r | |
3434 | cptr = pointer to input string\r | |
3435 | optr = pointer to output pointer (can be null)\r | |
3436 | dptr = pointer to device\r | |
3437 | Outputs:\r | |
3438 | result = pointer to register, NULL if error\r | |
3439 | *optr = pointer to next character in input string\r | |
3440 | */\r | |
3441 | \r | |
3442 | REG *find_reg (char *cptr, char **optr, DEVICE *dptr)\r | |
3443 | {\r | |
3444 | char *tptr;\r | |
3445 | REG *rptr;\r | |
3446 | uint32 slnt;\r | |
3447 | \r | |
3448 | if ((cptr == NULL) || (dptr == NULL) ||\r | |
3449 | (dptr->registers == NULL)) return NULL;\r | |
3450 | tptr = cptr;\r | |
3451 | do {\r | |
3452 | tptr++;\r | |
3453 | } while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_'));\r | |
3454 | slnt = tptr - cptr;\r | |
3455 | for (rptr = dptr->registers; rptr->name != NULL; rptr++) {\r | |
3456 | if ((slnt == strlen (rptr->name)) &&\r | |
3457 | (strncmp (cptr, rptr->name, slnt) == 0)) {\r | |
3458 | if (optr != NULL) *optr = tptr;\r | |
3459 | return rptr;\r | |
3460 | }\r | |
3461 | }\r | |
3462 | return NULL;\r | |
3463 | }\r | |
3464 | \r | |
3465 | /* get_switches get switches from input string\r | |
3466 | \r | |
3467 | Inputs:\r | |
3468 | cptr = pointer to input string\r | |
3469 | Outputs:\r | |
3470 | sw = switch bit mask\r | |
3471 | 0 if no switches, -1 if error\r | |
3472 | */\r | |
3473 | \r | |
3474 | int32 get_switches (char *cptr)\r | |
3475 | {\r | |
3476 | int32 sw;\r | |
3477 | \r | |
3478 | if (*cptr != '-') return 0;\r | |
3479 | sw = 0;\r | |
3480 | for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) {\r | |
3481 | if (isalpha (*cptr) == 0) return -1;\r | |
3482 | sw = sw | SWMASK (toupper (*cptr));\r | |
3483 | }\r | |
3484 | return sw;\r | |
3485 | }\r | |
3486 | \r | |
3487 | /* get_sim_sw accumulate sim_switches\r | |
3488 | \r | |
3489 | Inputs:\r | |
3490 | cptr = pointer to input string\r | |
3491 | Outputs:\r | |
3492 | ptr = pointer to first non-string glyph\r | |
3493 | NULL if error\r | |
3494 | */\r | |
3495 | \r | |
3496 | char *get_sim_sw (char *cptr)\r | |
3497 | {\r | |
3498 | int32 lsw;\r | |
3499 | char gbuf[CBUFSIZE];\r | |
3500 | \r | |
3501 | while (*cptr == '-') { /* while switches */\r | |
3502 | cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */\r | |
3503 | lsw = get_switches (gbuf); /* parse */\r | |
3504 | if (lsw <= 0) return NULL; /* invalid? */\r | |
3505 | sim_switches = sim_switches | lsw; /* accumulate */\r | |
3506 | }\r | |
3507 | return cptr;\r | |
3508 | }\r | |
3509 | \r | |
3510 | /* get_sim_opt get simulator command options\r | |
3511 | \r | |
3512 | Inputs:\r | |
3513 | opt = command options\r | |
3514 | cptr = pointer to input string\r | |
3515 | Outputs:\r | |
3516 | ptr = pointer to next glypsh, NULL if error\r | |
3517 | *stat = error status\r | |
3518 | */\r | |
3519 | \r | |
3520 | char *get_sim_opt (int32 opt, char *cptr, t_stat *st)\r | |
3521 | {\r | |
3522 | int32 t;\r | |
3523 | char *svptr, gbuf[CBUFSIZE];\r | |
3524 | DEVICE *tdptr;\r | |
3525 | UNIT *tuptr;\r | |
3526 | \r | |
3527 | sim_switches = 0; /* no switches */\r | |
3528 | sim_ofile = NULL; /* no output file */\r | |
3529 | sim_schptr = NULL; /* no search */\r | |
3530 | sim_stab.logic = SCH_OR; /* default search params */\r | |
3531 | sim_stab.boolop = SCH_GE;\r | |
3532 | sim_stab.mask = 0;\r | |
3533 | sim_stab.comp = 0;\r | |
3534 | sim_dfdev = sim_dflt_dev;\r | |
3535 | sim_dfunit = sim_dfdev->units;\r | |
3536 | sim_opt_out = 0; /* no options yet */\r | |
3537 | *st = SCPE_OK;\r | |
3538 | while (*cptr) { /* loop through modifiers */\r | |
3539 | svptr = cptr; /* save current position */\r | |
3540 | if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */\r | |
3541 | if (sim_ofile) { /* already got one? */\r | |
3542 | fclose (sim_ofile); /* one per customer */\r | |
3543 | *st = SCPE_ARG;\r | |
3544 | return NULL;\r | |
3545 | }\r | |
3546 | cptr = get_glyph_nc (cptr + 1, gbuf, 0);\r | |
3547 | sim_ofile = sim_fopen (gbuf, "a"); /* open for append */\r | |
3548 | if (sim_ofile == NULL) { /* open failed? */\r | |
3549 | *st = SCPE_OPENERR; \r | |
3550 | return NULL;\r | |
3551 | }\r | |
3552 | sim_opt_out |= CMD_OPT_OF; /* got output file */\r | |
3553 | continue;\r | |
3554 | }\r | |
3555 | cptr = get_glyph (cptr, gbuf, 0);\r | |
3556 | if ((t = get_switches (gbuf)) != 0) { /* try for switches */\r | |
3557 | if (t < 0) { /* err if bad switch */\r | |
3558 | *st = SCPE_INVSW;\r | |
3559 | return NULL;\r | |
3560 | }\r | |
3561 | sim_switches = sim_switches | t; /* or in new switches */\r | |
3562 | }\r | |
3563 | else if ((opt & CMD_OPT_SCH) && /* if allowed, */\r | |
3564 | get_search (gbuf, sim_dfdev->dradix, &sim_stab)) { /* try for search */\r | |
3565 | sim_schptr = &sim_stab; /* set search */\r | |
3566 | sim_opt_out |= CMD_OPT_SCH; /* got search */\r | |
3567 | }\r | |
3568 | else if ((opt & CMD_OPT_DFT) && /* default allowed? */\r | |
3569 | ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */\r | |
3570 | (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */\r | |
3571 | (tuptr != NULL)) {\r | |
3572 | sim_dfdev = tdptr; /* set as default */\r | |
3573 | sim_dfunit = tuptr;\r | |
3574 | sim_opt_out |= CMD_OPT_DFT; /* got default */\r | |
3575 | }\r | |
3576 | else return svptr; /* not rec, break out */\r | |
3577 | }\r | |
3578 | return cptr;\r | |
3579 | }\r | |
3580 | \r | |
3581 | /* Match file extension\r | |
3582 | \r | |
3583 | Inputs:\r | |
3584 | fnam = file name\r | |
3585 | ext = extension, without period\r | |
3586 | Outputs:\r | |
3587 | cp = pointer to final '.' if match, NULL if not\r | |
3588 | */\r | |
3589 | \r | |
3590 | char *match_ext (char *fnam, char *ext)\r | |
3591 | {\r | |
3592 | char *pptr, *fptr, *eptr;\r | |
3593 | \r | |
3594 | if ((fnam == NULL) || (ext == NULL)) /* bad arguments? */\r | |
3595 | return NULL;\r | |
3596 | pptr = strrchr (fnam, '.'); /* find last . */\r | |
3597 | if (pptr) { /* any? */\r | |
3598 | for (fptr = pptr + 1, eptr = ext; /* match characters */\r | |
3599 | #if defined (VMS) /* VMS: stop at ; or null */\r | |
3600 | (*fptr != 0) && (*fptr != ';');\r | |
3601 | #else\r | |
3602 | *fptr != 0; /* others: stop at null */\r | |
3603 | #endif\r | |
3604 | fptr++, eptr++) {\r | |
3605 | if (toupper (*fptr) != toupper (*eptr))\r | |
3606 | return NULL;\r | |
3607 | }\r | |
3608 | if (*eptr != 0) return NULL; /* ext exhausted? */\r | |
3609 | }\r | |
3610 | return pptr;\r | |
3611 | }\r | |
3612 | \r | |
3613 | /* Get search specification\r | |
3614 | \r | |
3615 | Inputs:\r | |
3616 | cptr = pointer to input string\r | |
3617 | radix = radix for numbers\r | |
3618 | schptr = pointer to search table\r | |
3619 | Outputs:\r | |
3620 | return = NULL if error\r | |
3621 | schptr if valid search specification\r | |
3622 | */\r | |
3623 | \r | |
3624 | SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr)\r | |
3625 | {\r | |
3626 | int32 c, logop, cmpop;\r | |
3627 | t_value logval, cmpval;\r | |
3628 | char *sptr, *tptr;\r | |
3629 | const char logstr[] = "|&^", cmpstr[] = "=!><";\r | |
3630 | \r | |
3631 | logval = cmpval = 0;\r | |
3632 | if (*cptr == 0) return NULL; /* check for clause */\r | |
3633 | for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */\r | |
3634 | if (sptr = strchr (logstr, c)) { /* check for mask */\r | |
3635 | logop = sptr - logstr;\r | |
3636 | logval = strtotv (cptr, &tptr, radix);\r | |
3637 | if (cptr == tptr) return NULL;\r | |
3638 | cptr = tptr;\r | |
3639 | }\r | |
3640 | else if (sptr = strchr (cmpstr, c)) { /* check for boolop */\r | |
3641 | cmpop = sptr - cmpstr;\r | |
3642 | if (*cptr == '=') {\r | |
3643 | cmpop = cmpop + strlen (cmpstr);\r | |
3644 | cptr++;\r | |
3645 | }\r | |
3646 | cmpval = strtotv (cptr, &tptr, radix);\r | |
3647 | if (cptr == tptr) return NULL;\r | |
3648 | cptr = tptr;\r | |
3649 | }\r | |
3650 | else return NULL;\r | |
3651 | } /* end for */\r | |
3652 | if (logop >= 0) {\r | |
3653 | schptr->logic = logop;\r | |
3654 | schptr->mask = logval;\r | |
3655 | }\r | |
3656 | if (cmpop >= 0) {\r | |
3657 | schptr->boolop = cmpop;\r | |
3658 | schptr->comp = cmpval;\r | |
3659 | }\r | |
3660 | return schptr;\r | |
3661 | }\r | |
3662 | \r | |
3663 | /* Test value against search specification\r | |
3664 | \r | |
3665 | Inputs:\r | |
3666 | val = value to test\r | |
3667 | schptr = pointer to search table\r | |
3668 | Outputs:\r | |
3669 | return = 1 if value passes search criteria, 0 if not\r | |
3670 | */\r | |
3671 | \r | |
3672 | int32 test_search (t_value val, SCHTAB *schptr)\r | |
3673 | {\r | |
3674 | if (schptr == NULL) return 0;\r | |
3675 | \r | |
3676 | switch (schptr->logic) { /* case on logical */\r | |
3677 | \r | |
3678 | case SCH_OR:\r | |
3679 | val = val | schptr->mask;\r | |
3680 | break;\r | |
3681 | \r | |
3682 | case SCH_AND:\r | |
3683 | val = val & schptr->mask;\r | |
3684 | break;\r | |
3685 | \r | |
3686 | case SCH_XOR:\r | |
3687 | val = val ^ schptr->mask;\r | |
3688 | break;\r | |
3689 | }\r | |
3690 | \r | |
3691 | switch (schptr->boolop) { /* case on comparison */\r | |
3692 | \r | |
3693 | case SCH_E: case SCH_EE:\r | |
3694 | return (val == schptr->comp);\r | |
3695 | \r | |
3696 | case SCH_N: case SCH_NE:\r | |
3697 | return (val != schptr->comp);\r | |
3698 | \r | |
3699 | case SCH_G:\r | |
3700 | return (val > schptr->comp);\r | |
3701 | \r | |
3702 | case SCH_GE:\r | |
3703 | return (val >= schptr->comp);\r | |
3704 | \r | |
3705 | case SCH_L:\r | |
3706 | return (val < schptr->comp);\r | |
3707 | \r | |
3708 | case SCH_LE:\r | |
3709 | return (val <= schptr->comp);\r | |
3710 | }\r | |
3711 | \r | |
3712 | return 0;\r | |
3713 | }\r | |
3714 | \r | |
3715 | /* Radix independent input/output package\r | |
3716 | \r | |
3717 | strtotv - general radix input routine\r | |
3718 | \r | |
3719 | Inputs:\r | |
3720 | inptr = string to convert\r | |
3721 | endptr = pointer to first unconverted character\r | |
3722 | radix = radix for input\r | |
3723 | Outputs:\r | |
3724 | value = converted value\r | |
3725 | \r | |
3726 | On an error, the endptr will equal the inptr.\r | |
3727 | */\r | |
3728 | \r | |
3729 | t_value strtotv (char *inptr, char **endptr, uint32 radix)\r | |
3730 | {\r | |
3731 | int32 nodigit;\r | |
3732 | t_value val;\r | |
3733 | uint32 c, digit;\r | |
3734 | \r | |
3735 | *endptr = inptr; /* assume fails */\r | |
3736 | if ((radix < 2) || (radix > 36)) return 0;\r | |
3737 | while (isspace (*inptr)) inptr++; /* bypass white space */\r | |
3738 | val = 0;\r | |
3739 | nodigit = 1;\r | |
3740 | for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */\r | |
3741 | if (islower (c)) c = toupper (c);\r | |
3742 | if (isdigit (c)) digit = c - (uint32) '0'; /* digit? */\r | |
3743 | else if (radix <= 10) break; /* stop if not expected */\r | |
3744 | else digit = c + 10 - (uint32) 'A'; /* convert letter */\r | |
3745 | if (digit >= radix) return 0; /* valid in radix? */\r | |
3746 | val = (val * radix) + digit; /* add to value */\r | |
3747 | nodigit = 0;\r | |
3748 | }\r | |
3749 | if (nodigit) return 0; /* no digits? */\r | |
3750 | *endptr = inptr; /* result pointer */\r | |
3751 | return val;\r | |
3752 | }\r | |
3753 | \r | |
3754 | /* fprint_val - general radix printing routine\r | |
3755 | \r | |
3756 | Inputs:\r | |
3757 | stream = stream designator\r | |
3758 | val = value to print\r | |
3759 | radix = radix to print\r | |
3760 | width = width to print\r | |
3761 | format = leading zeroes format\r | |
3762 | Outputs:\r | |
3763 | status = error status\r | |
3764 | */\r | |
3765 | \r | |
3766 | t_stat fprint_val (FILE *stream, t_value val, uint32 radix,\r | |
3767 | uint32 width, uint32 format)\r | |
3768 | {\r | |
3769 | #define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value)))\r | |
3770 | t_value owtest, wtest;\r | |
3771 | int32 d, digit, ndigits;\r | |
3772 | char dbuf[MAX_WIDTH + 1];\r | |
3773 | \r | |
3774 | for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' ';\r | |
3775 | dbuf[MAX_WIDTH] = 0;\r | |
3776 | d = MAX_WIDTH;\r | |
3777 | do {\r | |
3778 | d = d - 1;\r | |
3779 | digit = (int32) (val % radix);\r | |
3780 | val = val / radix;\r | |
3781 | dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10);\r | |
3782 | } while ((d > 0) && (val != 0));\r | |
3783 | \r | |
3784 | if (format != PV_LEFT) {\r | |
3785 | wtest = owtest = radix;\r | |
3786 | ndigits = 1;\r | |
3787 | while ((wtest < width_mask[width]) && (wtest >= owtest)) {\r | |
3788 | owtest = wtest;\r | |
3789 | wtest = wtest * radix;\r | |
3790 | ndigits = ndigits + 1;\r | |
3791 | }\r | |
3792 | if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits;\r | |
3793 | }\r | |
3794 | if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR;\r | |
3795 | return SCPE_OK;\r | |
3796 | }\r | |
3797 | \r | |
3798 | /* Event queue package\r | |
3799 | \r | |
3800 | sim_activate add entry to event queue\r | |
3801 | sim_cancel remove entry from event queue\r | |
3802 | sim_process_event process entries on event queue\r | |
3803 | sim_is_active see if entry is on event queue\r | |
3804 | sim_atime return absolute time for an entry\r | |
3805 | sim_gtime return global time\r | |
3806 | sim_qcount return event queue entry count\r | |
3807 | \r | |
3808 | Asynchronous events are set up by queueing a unit data structure\r | |
3809 | to the event queue with a timeout (in simulator units, relative\r | |
3810 | to the current time). Each simulator 'times' these events by\r | |
3811 | counting down interval counter sim_interval. When this reaches\r | |
3812 | zero the simulator calls sim_process_event to process the event\r | |
3813 | and to see if further events need to be processed, or sim_interval\r | |
3814 | reset to count the next one.\r | |
3815 | \r | |
3816 | The event queue is maintained in clock order; entry timeouts are\r | |
3817 | RELATIVE to the time in the previous entry.\r | |
3818 | \r | |
3819 | sim_process_event - process event\r | |
3820 | \r | |
3821 | Inputs:\r | |
3822 | none\r | |
3823 | Outputs:\r | |
3824 | reason = reason code returned by any event processor,\r | |
3825 | or 0 (SCPE_OK) if no exceptions\r | |
3826 | */\r | |
3827 | \r | |
3828 | t_stat sim_process_event (void)\r | |
3829 | {\r | |
3830 | UNIT *uptr;\r | |
3831 | t_stat reason;\r | |
3832 | \r | |
3833 | if (stop_cpu) return SCPE_STOP; /* stop CPU? */\r | |
3834 | if (sim_clock_queue == NULL) { /* queue empty? */\r | |
3835 | UPDATE_SIM_TIME (noqueue_time); /* update sim time */\r | |
3836 | sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */\r | |
3837 | return SCPE_OK;\r | |
3838 | }\r | |
3839 | UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */\r | |
3840 | do {\r | |
3841 | uptr = sim_clock_queue; /* get first */\r | |
3842 | sim_clock_queue = uptr->next; /* remove first */\r | |
3843 | uptr->next = NULL; /* hygiene */\r | |
3844 | uptr->time = 0;\r | |
3845 | if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time;\r | |
3846 | else sim_interval = noqueue_time = NOQUEUE_WAIT;\r | |
3847 | if (uptr->action != NULL) reason = uptr->action (uptr);\r | |
3848 | else reason = SCPE_OK;\r | |
3849 | } while ((reason == SCPE_OK) && (sim_interval == 0));\r | |
3850 | \r | |
3851 | /* Empty queue forces sim_interval != 0 */\r | |
3852 | \r | |
3853 | return reason;\r | |
3854 | }\r | |
3855 | \r | |
3856 | /* sim_activate - activate (queue) event\r | |
3857 | \r | |
3858 | Inputs:\r | |
3859 | uptr = pointer to unit\r | |
3860 | event_time = relative timeout\r | |
3861 | Outputs:\r | |
3862 | reason = result (SCPE_OK if ok)\r | |
3863 | */\r | |
3864 | \r | |
3865 | t_stat sim_activate (UNIT *uptr, int32 event_time)\r | |
3866 | {\r | |
3867 | UNIT *cptr, *prvptr;\r | |
3868 | int32 accum;\r | |
3869 | \r | |
3870 | if (event_time < 0) return SCPE_IERR;\r | |
3871 | if (sim_is_active (uptr)) return SCPE_OK; /* already active? */\r | |
3872 | if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }\r | |
3873 | else { UPDATE_SIM_TIME (sim_clock_queue->time); } /* update sim time */\r | |
3874 | \r | |
3875 | prvptr = NULL;\r | |
3876 | accum = 0;\r | |
3877 | for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {\r | |
3878 | if (event_time < (accum + cptr->time)) break;\r | |
3879 | accum = accum + cptr->time;\r | |
3880 | prvptr = cptr;\r | |
3881 | }\r | |
3882 | if (prvptr == NULL) { /* insert at head */\r | |
3883 | cptr = uptr->next = sim_clock_queue;\r | |
3884 | sim_clock_queue = uptr;\r | |
3885 | }\r | |
3886 | else {\r | |
3887 | cptr = uptr->next = prvptr->next; /* insert at prvptr */\r | |
3888 | prvptr->next = uptr;\r | |
3889 | }\r | |
3890 | uptr->time = event_time - accum;\r | |
3891 | if (cptr != NULL) cptr->time = cptr->time - uptr->time;\r | |
3892 | sim_interval = sim_clock_queue->time;\r | |
3893 | return SCPE_OK;\r | |
3894 | }\r | |
3895 | \r | |
3896 | /* sim_activate_abs - activate (queue) event even if event already scheduled\r | |
3897 | \r | |
3898 | Inputs:\r | |
3899 | uptr = pointer to unit\r | |
3900 | event_time = relative timeout\r | |
3901 | Outputs:\r | |
3902 | reason = result (SCPE_OK if ok)\r | |
3903 | */\r | |
3904 | \r | |
3905 | t_stat sim_activate_abs (UNIT *uptr, int32 event_time)\r | |
3906 | {\r | |
3907 | sim_cancel (uptr);\r | |
3908 | return sim_activate (uptr, event_time);\r | |
3909 | }\r | |
3910 | \r | |
3911 | /* sim_cancel - cancel (dequeue) event\r | |
3912 | \r | |
3913 | Inputs:\r | |
3914 | uptr = pointer to unit\r | |
3915 | Outputs:\r | |
3916 | reason = result (SCPE_OK if ok)\r | |
3917 | \r | |
3918 | */\r | |
3919 | \r | |
3920 | t_stat sim_cancel (UNIT *uptr)\r | |
3921 | {\r | |
3922 | UNIT *cptr, *nptr;\r | |
3923 | \r | |
3924 | if (sim_clock_queue == NULL) return SCPE_OK;\r | |
3925 | UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */\r | |
3926 | nptr = NULL;\r | |
3927 | if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr->next;\r | |
3928 | else {\r | |
3929 | for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {\r | |
3930 | if (cptr->next == uptr) {\r | |
3931 | nptr = cptr->next = uptr->next;\r | |
3932 | break; /* end queue scan */\r | |
3933 | }\r | |
3934 | }\r | |
3935 | }\r | |
3936 | if (nptr != NULL) nptr->time = nptr->time + uptr->time;\r | |
3937 | uptr->next = NULL; /* hygiene */\r | |
3938 | uptr->time = 0;\r | |
3939 | if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time;\r | |
3940 | else sim_interval = noqueue_time = NOQUEUE_WAIT;\r | |
3941 | return SCPE_OK;\r | |
3942 | }\r | |
3943 | \r | |
3944 | /* sim_is_active - test for entry in queue, return activation time\r | |
3945 | \r | |
3946 | Inputs:\r | |
3947 | uptr = pointer to unit\r | |
3948 | Outputs:\r | |
3949 | result = absolute activation time + 1, 0 if inactive\r | |
3950 | */\r | |
3951 | \r | |
3952 | int32 sim_is_active (UNIT *uptr)\r | |
3953 | {\r | |
3954 | UNIT *cptr;\r | |
3955 | int32 accum;\r | |
3956 | \r | |
3957 | accum = 0;\r | |
3958 | for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {\r | |
3959 | if (cptr == sim_clock_queue) {\r | |
3960 | if (sim_interval > 0) accum = accum + sim_interval;\r | |
3961 | }\r | |
3962 | else accum = accum + cptr->time;\r | |
3963 | if (cptr == uptr) return accum + 1;\r | |
3964 | }\r | |
3965 | return 0;\r | |
3966 | }\r | |
3967 | \r | |
3968 | /* sim_gtime - return global time\r | |
3969 | sim_grtime - return global time with rollover\r | |
3970 | \r | |
3971 | Inputs: none\r | |
3972 | Outputs:\r | |
3973 | time = global time\r | |
3974 | */\r | |
3975 | \r | |
3976 | double sim_gtime (void)\r | |
3977 | {\r | |
3978 | if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }\r | |
3979 | else { UPDATE_SIM_TIME (sim_clock_queue->time); }\r | |
3980 | return sim_time;\r | |
3981 | }\r | |
3982 | \r | |
3983 | uint32 sim_grtime (void)\r | |
3984 | {\r | |
3985 | if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }\r | |
3986 | else { UPDATE_SIM_TIME (sim_clock_queue->time); }\r | |
3987 | return sim_rtime;\r | |
3988 | }\r | |
3989 | \r | |
3990 | /* sim_qcount - return queue entry count\r | |
3991 | \r | |
3992 | Inputs: none\r | |
3993 | Outputs:\r | |
3994 | count = number of entries on the queue\r | |
3995 | */\r | |
3996 | \r | |
3997 | int32 sim_qcount (void)\r | |
3998 | {\r | |
3999 | int32 cnt;\r | |
4000 | UNIT *uptr;\r | |
4001 | \r | |
4002 | cnt = 0;\r | |
4003 | for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) cnt++;\r | |
4004 | return cnt;\r | |
4005 | }\r | |
4006 | \r | |
4007 | /* Breakpoint package. This module replaces the VM-implemented one\r | |
4008 | instruction breakpoint capability.\r | |
4009 | \r | |
4010 | Breakpoints are stored in table sim_brk_tab, which is ordered by address for\r | |
4011 | efficient binary searching. A breakpoint consists of a four entry structure:\r | |
4012 | \r | |
4013 | addr address of the breakpoint\r | |
4014 | type types of breakpoints set on the address\r | |
4015 | a bit mask representing letters A-Z\r | |
4016 | cnt number of iterations before breakp is taken\r | |
4017 | action pointer command string to be executed\r | |
4018 | when break is taken\r | |
4019 | \r | |
4020 | sim_brk_summ is a summary of the types of breakpoints that are currently set (it\r | |
4021 | is the bitwise OR of all the type fields). A simulator need only check for\r | |
4022 | a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum.\r | |
4023 | \r | |
4024 | The package contains the following public routines:\r | |
4025 | \r | |
4026 | sim_brk_init initialize\r | |
4027 | sim_brk_set set breakpoint\r | |
4028 | sim_brk_clr clear breakpoint\r | |
4029 | sim_brk_clrall clear all breakpoints\r | |
4030 | sim_brk_show show breakpoint\r | |
4031 | sim_brk_showall show all breakpoints\r | |
4032 | sim_brk_test test for breakpoint\r | |
4033 | sim_brk_npc PC has been changed\r | |
4034 | sim_brk_getact get next action\r | |
4035 | sim_brk_clract clear pending actions\r | |
4036 | \r | |
4037 | Initialize breakpoint system.\r | |
4038 | */\r | |
4039 | \r | |
4040 | t_stat sim_brk_init (void)\r | |
4041 | {\r | |
4042 | sim_brk_lnt = SIM_BRK_INILNT;\r | |
4043 | sim_brk_tab = (BRKTAB *) calloc (sim_brk_lnt, sizeof (BRKTAB));\r | |
4044 | if (sim_brk_tab == NULL) return SCPE_MEM;\r | |
4045 | sim_brk_ent = sim_brk_ins = 0;\r | |
4046 | sim_brk_act = NULL;\r | |
4047 | sim_brk_npc (0);\r | |
4048 | return SCPE_OK;\r | |
4049 | }\r | |
4050 | \r | |
4051 | /* Search for a breakpoint in the sorted breakpoint table */\r | |
4052 | \r | |
4053 | BRKTAB *sim_brk_fnd (t_addr loc)\r | |
4054 | {\r | |
4055 | int32 lo, hi, p;\r | |
4056 | BRKTAB *bp;\r | |
4057 | \r | |
4058 | if (sim_brk_ent == 0) { /* table empty? */\r | |
4059 | sim_brk_ins = 0; /* insrt at head */\r | |
4060 | return NULL; /* sch fails */\r | |
4061 | }\r | |
4062 | lo = 0; /* initial bounds */\r | |
4063 | hi = sim_brk_ent - 1;\r | |
4064 | do {\r | |
4065 | p = (lo + hi) >> 1; /* probe */\r | |
4066 | bp = sim_brk_tab + p; /* table addr */\r | |
4067 | if (loc == bp->addr) return bp; /* match? */\r | |
4068 | else if (loc < bp->addr) hi = p - 1; /* go down? p is upper */\r | |
4069 | else lo = p + 1; /* go up? p is lower */\r | |
4070 | } while (lo <= hi);\r | |
4071 | if (loc < bp->addr) sim_brk_ins = p; /* insrt before or */\r | |
4072 | else sim_brk_ins = p + 1; /* after last sch */\r | |
4073 | return NULL;\r | |
4074 | }\r | |
4075 | \r | |
4076 | /* Insert a breakpoint */\r | |
4077 | \r | |
4078 | BRKTAB *sim_brk_new (t_addr loc)\r | |
4079 | {\r | |
4080 | int32 i, t;\r | |
4081 | BRKTAB *bp, *newp;\r | |
4082 | \r | |
4083 | if (sim_brk_ins < 0) return NULL;\r | |
4084 | if (sim_brk_ent >= sim_brk_lnt) { /* out of space? */\r | |
4085 | t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */\r | |
4086 | newp = (BRKTAB *) calloc (t, sizeof (BRKTAB)); /* new table */\r | |
4087 | if (newp == NULL) return NULL; /* can't extend */\r | |
4088 | for (i = 0; i < sim_brk_lnt; i++) /* copy table */\r | |
4089 | *(newp + i) = *(sim_brk_tab + i);\r | |
4090 | free (sim_brk_tab); /* free old table */\r | |
4091 | sim_brk_tab = newp; /* new base, lnt */\r | |
4092 | sim_brk_lnt = t;\r | |
4093 | }\r | |
4094 | if (sim_brk_ins != sim_brk_ent) { /* move needed? */\r | |
4095 | for (bp = sim_brk_tab + sim_brk_ent;\r | |
4096 | bp > sim_brk_tab + sim_brk_ins; bp--)\r | |
4097 | *bp = *(bp - 1);\r | |
4098 | }\r | |
4099 | bp = sim_brk_tab + sim_brk_ins;\r | |
4100 | bp->addr = loc;\r | |
4101 | bp->typ = 0;\r | |
4102 | bp->cnt = 0;\r | |
4103 | bp->act = NULL;\r | |
4104 | sim_brk_ent = sim_brk_ent + 1;\r | |
4105 | return bp;\r | |
4106 | }\r | |
4107 | \r | |
4108 | /* Set a breakpoint of type sw */\r | |
4109 | \r | |
4110 | t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act)\r | |
4111 | {\r | |
4112 | BRKTAB *bp;\r | |
4113 | \r | |
4114 | if (sw == 0) sw = sim_brk_dflt;\r | |
4115 | if ((sim_brk_types & sw) == 0) return SCPE_NOFNC;\r | |
4116 | bp = sim_brk_fnd (loc); /* present? */\r | |
4117 | if (!bp) bp = sim_brk_new (loc); /* no, allocate */\r | |
4118 | if (!bp) return SCPE_MEM; /* still no? mem err */\r | |
4119 | bp->typ = sw; /* set type */\r | |
4120 | bp->cnt = ncnt; /* set count */\r | |
4121 | if ((bp->act != NULL) && (act != NULL)) { /* replace old action? */\r | |
4122 | free (bp->act); /* deallocate */\r | |
4123 | bp->act = NULL; /* now no action */\r | |
4124 | }\r | |
4125 | if ((act != NULL) && (*act != 0)) { /* new action? */\r | |
4126 | char *newp = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc buf */\r | |
4127 | if (newp == NULL) return SCPE_MEM; /* mem err? */\r | |
4128 | strncpy (newp, act, CBUFSIZE); /* copy action */\r | |
4129 | bp->act = newp; /* set pointer */\r | |
4130 | }\r | |
4131 | sim_brk_summ = sim_brk_summ | sw;\r | |
4132 | return SCPE_OK;\r | |
4133 | }\r | |
4134 | \r | |
4135 | /* Clear a breakpoint */\r | |
4136 | \r | |
4137 | t_stat sim_brk_clr (t_addr loc, int32 sw)\r | |
4138 | {\r | |
4139 | BRKTAB *bp = sim_brk_fnd (loc);\r | |
4140 | \r | |
4141 | if (!bp) return SCPE_OK; /* not there? ok */\r | |
4142 | if (sw == 0) sw = SIM_BRK_ALLTYP;\r | |
4143 | bp->typ = bp->typ & ~sw;\r | |
4144 | if (bp->typ) return SCPE_OK; /* clear all types? */\r | |
4145 | if (bp->act != NULL) free (bp->act); /* deallocate action */\r | |
4146 | for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++) /* erase entry */\r | |
4147 | *bp = *(bp + 1);\r | |
4148 | sim_brk_ent = sim_brk_ent - 1; /* decrement count */\r | |
4149 | sim_brk_summ = 0; /* recalc summary */\r | |
4150 | for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++)\r | |
4151 | sim_brk_summ = sim_brk_summ | bp->typ;\r | |
4152 | return SCPE_OK;\r | |
4153 | }\r | |
4154 | \r | |
4155 | /* Clear all breakpoints */\r | |
4156 | \r | |
4157 | t_stat sim_brk_clrall (int32 sw)\r | |
4158 | {\r | |
4159 | BRKTAB *bp;\r | |
4160 | \r | |
4161 | if (sw == 0) sw = SIM_BRK_ALLTYP;\r | |
4162 | for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); ) {\r | |
4163 | if (bp->typ & sw) sim_brk_clr (bp->addr, sw);\r | |
4164 | else bp++;\r | |
4165 | }\r | |
4166 | return SCPE_OK;\r | |
4167 | }\r | |
4168 | \r | |
4169 | /* Show a breakpoint */\r | |
4170 | \r | |
4171 | t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)\r | |
4172 | {\r | |
4173 | BRKTAB *bp = sim_brk_fnd (loc);\r | |
4174 | DEVICE *dptr;\r | |
4175 | int32 i, any;\r | |
4176 | \r | |
4177 | if (sw == 0) sw = SIM_BRK_ALLTYP;\r | |
4178 | if (!bp || (!(bp->typ & sw))) return SCPE_OK;\r | |
4179 | dptr = sim_dflt_dev;\r | |
4180 | if (dptr == NULL) return SCPE_OK;\r | |
4181 | if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, loc);\r | |
4182 | else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT);\r | |
4183 | fprintf (st, ":\t");\r | |
4184 | for (i = any = 0; i < 26; i++) {\r | |
4185 | if ((bp->typ >> i) & 1) {\r | |
4186 | if (any) fprintf (st, ", ");\r | |
4187 | fputc (i + 'A', st);\r | |
4188 | any = 1;\r | |
4189 | }\r | |
4190 | }\r | |
4191 | if (bp->cnt > 0) fprintf (st, " [%d]", bp->cnt);\r | |
4192 | if (bp->act != NULL) fprintf (st, "; %s", bp->act);\r | |
4193 | fprintf (st, "\n");\r | |
4194 | return SCPE_OK;\r | |
4195 | }\r | |
4196 | \r | |
4197 | /* Show all breakpoints */\r | |
4198 | \r | |
4199 | t_stat sim_brk_showall (FILE *st, int32 sw)\r | |
4200 | {\r | |
4201 | BRKTAB *bp;\r | |
4202 | \r | |
4203 | if (sw == 0) sw = SIM_BRK_ALLTYP;\r | |
4204 | for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) {\r | |
4205 | if (bp->typ & sw) sim_brk_show (st, bp->addr, sw);\r | |
4206 | }\r | |
4207 | return SCPE_OK;\r | |
4208 | }\r | |
4209 | \r | |
4210 | /* Test for breakpoint */\r | |
4211 | \r | |
4212 | uint32 sim_brk_test (t_addr loc, uint32 btyp)\r | |
4213 | {\r | |
4214 | BRKTAB *bp;\r | |
4215 | uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1);\r | |
4216 | \r | |
4217 | if ((bp = sim_brk_fnd (loc)) && (btyp & bp->typ)) { /* in table, type match? */\r | |
4218 | if ((sim_brk_pend[spc] && (loc == sim_brk_ploc[spc])) || /* previous location? */\r | |
4219 | (--bp->cnt > 0)) return 0; /* count > 0? */\r | |
4220 | bp->cnt = 0; /* reset count */\r | |
4221 | sim_brk_ploc[spc] = loc; /* save location */\r | |
4222 | sim_brk_pend[spc] = TRUE; /* don't do twice */\r | |
4223 | sim_brk_act = bp->act; /* set up actions */\r | |
4224 | return (btyp & bp->typ);\r | |
4225 | }\r | |
4226 | sim_brk_pend[spc] = FALSE;\r | |
4227 | return 0;\r | |
4228 | }\r | |
4229 | \r | |
4230 | /* Get next pending action, if any */\r | |
4231 | \r | |
4232 | char *sim_brk_getact (char *buf, int32 size)\r | |
4233 | {\r | |
4234 | char *ep;\r | |
4235 | size_t lnt;\r | |
4236 | \r | |
4237 | if (sim_brk_act == NULL) return NULL; /* any action? */\r | |
4238 | while (isspace (*sim_brk_act)) sim_brk_act++; /* skip spaces */\r | |
4239 | if (*sim_brk_act == 0) return (sim_brk_act = NULL); /* now empty? */\r | |
4240 | if (ep = strchr (sim_brk_act, ';')) { /* cmd delimiter? */\r | |
4241 | lnt = ep - sim_brk_act; /* cmd length */\r | |
4242 | memcpy (buf, sim_brk_act, lnt + 1); /* copy with ; */\r | |
4243 | buf[lnt] = 0; /* erase ; */\r | |
4244 | sim_brk_act = sim_brk_act + lnt + 1; /* adv ptr */\r | |
4245 | }\r | |
4246 | else {\r | |
4247 | strncpy (buf, sim_brk_act, size); /* copy action */\r | |
4248 | sim_brk_act = NULL; /* no more */\r | |
4249 | }\r | |
4250 | return buf;\r | |
4251 | }\r | |
4252 | \r | |
4253 | /* Clear pending actions */\r | |
4254 | \r | |
4255 | void sim_brk_clract (void)\r | |
4256 | {\r | |
4257 | sim_brk_act = NULL;\r | |
4258 | }\r | |
4259 | \r | |
4260 | /* New PC */\r | |
4261 | \r | |
4262 | void sim_brk_npc (uint32 cnt)\r | |
4263 | {\r | |
4264 | uint32 i;\r | |
4265 | \r | |
4266 | if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC)) cnt = SIM_BKPT_N_SPC;\r | |
4267 | for (i = 0; i < cnt; i++) {\r | |
4268 | sim_brk_pend[i] = FALSE;\r | |
4269 | sim_brk_ploc[i] = 0;\r | |
4270 | }\r | |
4271 | return;\r | |
4272 | }\r | |
4273 | \r | |
4274 | /* Clear breakpoint space */\r | |
4275 | \r | |
4276 | void sim_brk_clrspc (uint32 spc)\r | |
4277 | {\r | |
4278 | if (spc < SIM_BKPT_N_SPC) {\r | |
4279 | sim_brk_pend[spc] = FALSE;\r | |
4280 | sim_brk_ploc[spc] = 0;\r | |
4281 | }\r | |
4282 | return;\r | |
4283 | }\r | |
4284 | \r | |
4285 | /* Debug printout routines, from Dave Hittner */\r | |
4286 | \r | |
4287 | const char* debug_bstates = "01_^";\r | |
4288 | const char* debug_fmt = "DBG> %s %s: ";\r | |
4289 | int32 debug_unterm = 0;\r | |
4290 | \r | |
4291 | /* Finds debug phrase matching bitmask from from device DEBTAB table */\r | |
4292 | \r | |
4293 | static char* get_dbg_verb (uint32 dbits, DEVICE* dptr)\r | |
4294 | {\r | |
4295 | static char* debtab_none = "DEBTAB_ISNULL";\r | |
4296 | static char* debtab_nomatch = "DEBTAB_NOMATCH";\r | |
4297 | int32 offset = 0;\r | |
4298 | \r | |
4299 | if (dptr->debflags == 0) return debtab_none;\r | |
4300 | \r | |
4301 | /* Find matching words for bitmask */\r | |
4302 | \r | |
4303 | while (dptr->debflags[offset].name && (offset < 32)) {\r | |
4304 | if (dptr->debflags[offset].mask & dbits)\r | |
4305 | return dptr->debflags[offset].name;\r | |
4306 | offset++;\r | |
4307 | }\r | |
4308 | return debtab_nomatch;\r | |
4309 | }\r | |
4310 | \r | |
4311 | /* Prints standard debug prefix unless previous call unterminated */\r | |
4312 | \r | |
4313 | static void sim_debug_prefix (uint32 dbits, DEVICE* dptr)\r | |
4314 | {\r | |
4315 | if (!debug_unterm) {\r | |
4316 | char* debug_type = get_dbg_verb (dbits, dptr);\r | |
4317 | fprintf(sim_deb, debug_fmt, dptr->name, debug_type);\r | |
4318 | }\r | |
4319 | }\r | |
4320 | \r | |
4321 | /* Prints state of a register: bit translation + state (0,1,_,^)\r | |
4322 | indicating the state and transition of the bit. States:\r | |
4323 | 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */\r | |
4324 | \r | |
4325 | void sim_debug_u16(uint32 dbits, DEVICE* dptr, const char* const* bitdefs,\r | |
4326 | uint16 before, uint16 after, int terminate)\r | |
4327 | {\r | |
4328 | if (sim_deb && (dptr->dctrl & dbits)) {\r | |
4329 | int32 i;\r | |
4330 | \r | |
4331 | sim_debug_prefix(dbits, dptr); /* print prefix if required */\r | |
4332 | for (i = 15; i >= 0; i--) { /* print xlation, transition */\r | |
4333 | int off = ((after >> i) & 1) + (((before ^ after) >> i) & 1) * 2;\r | |
4334 | fprintf(sim_deb, "%s%c ", bitdefs[i], debug_bstates[off]);\r | |
4335 | }\r | |
4336 | if (terminate) fprintf(sim_deb, "\r\n");\r | |
4337 | debug_unterm = terminate ? 0 : 1; /* set unterm for next */\r | |
4338 | }\r | |
4339 | }\r | |
4340 | \r | |
4341 | #if defined (_WIN32)\r | |
4342 | #define vsnprintf _vsnprintf\r | |
4343 | #endif\r | |
4344 | #if defined (__DECC) && defined (__VMS)\r | |
4345 | #define NO_vsnprintf\r | |
4346 | #endif\r | |
4347 | #if defined( NO_vsnprintf)\r | |
4348 | #define STACKBUFSIZE 16384\r | |
4349 | #else\r | |
4350 | #define STACKBUFSIZE 2048\r | |
4351 | #endif\r | |
4352 | \r | |
4353 | /* Inline debugging - will print debug message if debug file is\r | |
4354 | set and the bitmask matches the current device debug options.\r | |
4355 | Extra returns are added for un*x systems, since the output\r | |
4356 | device is set into 'raw' mode when the cpu is booted,\r | |
4357 | and the extra returns don't hurt any other systems. */\r | |
4358 | \r | |
4359 | void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...)\r | |
4360 | {\r | |
4361 | if (sim_deb && (dptr->dctrl & dbits)) {\r | |
4362 | \r | |
4363 | char stackbuf[STACKBUFSIZE];\r | |
4364 | int32 bufsize = sizeof(stackbuf);\r | |
4365 | char *buf = stackbuf;\r | |
4366 | va_list arglist;\r | |
4367 | int32 i, j, len;\r | |
4368 | \r | |
4369 | buf[bufsize-1] = '\0';\r | |
4370 | sim_debug_prefix(dbits, dptr); /* print prefix if required */\r | |
4371 | \r | |
4372 | while (1) { /* format passed string, args */\r | |
4373 | va_start (arglist, fmt);\r | |
4374 | #if defined(NO_vsnprintf)\r | |
4375 | #if defined(HAS_vsprintf_void)\r | |
4376 | \r | |
4377 | /* Note, this could blow beyond the buffer, and we couldn't tell */\r | |
4378 | /* That is a limitation of the C runtime library available on this platform */\r | |
4379 | \r | |
4380 | vsprintf (buf, fmt, arglist);\r | |
4381 | for (len = 0; len < bufsize-1; len++)\r | |
4382 | if (buf[len] == 0) break;\r | |
4383 | #else\r | |
4384 | len = vsprintf (buf, fmt, arglist);\r | |
4385 | #endif /* HAS_vsprintf_void */\r | |
4386 | #else /* NO_vsnprintf */\r | |
4387 | #if defined(HAS_vsnprintf_void)\r | |
4388 | vsnprintf (buf, bufsize-1, fmt, arglist);\r | |
4389 | for (len = 0; len < bufsize-1; len++)\r | |
4390 | if (buf[len] == 0) break;\r | |
4391 | #else\r | |
4392 | len = vsnprintf (buf, bufsize-1, fmt, arglist);\r | |
4393 | #endif /* HAS_vsnprintf_void */\r | |
4394 | #endif /* NO_vsnprintf */\r | |
4395 | va_end (arglist);\r | |
4396 | \r | |
4397 | /* If it didn't fit into the buffer, then grow it and try again */\r | |
4398 | \r | |
4399 | if ((len < 0) || (len >= bufsize-1)) {\r | |
4400 | if (buf != stackbuf) free (buf);\r | |
4401 | bufsize = bufsize * 2;\r | |
4402 | buf = (char *) malloc (bufsize);\r | |
4403 | if (buf == NULL) return; /* out of memory */\r | |
4404 | buf[bufsize-1] = '\0';\r | |
4405 | continue;\r | |
4406 | }\r | |
4407 | break;\r | |
4408 | }\r | |
4409 | \r | |
4410 | /* Output the formatted data expanding newlines where they exist */\r | |
4411 | \r | |
4412 | for (i = j = 0; i < len; ++i) {\r | |
4413 | if ('\n' == buf[i]) {\r | |
4414 | if (i > j) fwrite (&buf[j], 1, i-j, sim_deb);\r | |
4415 | j = i;\r | |
4416 | fputc('\r', sim_deb);\r | |
4417 | }\r | |
4418 | }\r | |
4419 | if (i > j) fwrite (&buf[j], 1, i-j, sim_deb);\r | |
4420 | \r | |
4421 | /* Set unterminated flag for next time */\r | |
4422 | \r | |
4423 | debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1;\r | |
4424 | if (buf != stackbuf) free (buf);\r | |
4425 | }\r | |
4426 | return;\r | |
4427 | }\r |