| 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 |