home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Boot_Images / 2.11_on_rl02 / pdpsim.tz / pdpsim / scp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-29  |  44.3 KB  |  1,549 lines

  1. /* Simulator control program
  2.  
  3.    Copyright (c) 1993, 1994, 1995, 1996, Robert M Supnik,
  4.    Digital Equipment Corporation
  5.    Commercial use prohibited
  6.  
  7.    13-Apr-95    RMS    Added symbolic printouts
  8.    22-May-95    RMS    Added symbolic input
  9.    11-Dec-95    RMS    Fixed ordering bug in save/restore
  10.    07-Jan-96    RMS    Added register buffers in save/restore
  11. */
  12.  
  13. #include "sim_defs.h"
  14. #include <limits.h>
  15. #include <signal.h>
  16. #include <ctype.h>
  17. #define EX_D    0                    /* deposit */
  18. #define EX_E    1                    /* examine */
  19. #define EX_I    2                    /* interactive */
  20. #define SWHIDE    (1u << 26)                /* enable hiding */
  21. #define RU_RUN    0                    /* run */
  22. #define RU_GO    1                    /* go */
  23. #define RU_STEP 2                    /* step */
  24. #define RU_CONT 3                    /* continue */
  25. #define RU_BOOT 4                    /* boot */
  26. #define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \
  27.     x = sim_interval
  28.  
  29. extern char sim_name[];
  30. extern DEVICE *sim_devices[];
  31. extern REG *sim_PC;
  32. extern char *sim_stop_messages[];
  33. extern int sim_instr (void);
  34. extern int sim_load (FILE *ptr);
  35. extern int sim_emax;
  36. extern int print_sym (unsigned int addr, unsigned int *val, int flag, int sw);
  37. extern int parse_sym (char *cptr, unsigned int addr, int flag,
  38.     unsigned int *val);
  39. extern int ttinit (void);
  40. extern int ttrunstate (void);
  41. extern int ttcmdstate (void);
  42. extern int ttclose (void);
  43. UNIT *sim_clock_queue = NULL;
  44. int sim_interval = 0;
  45. static double sim_time;
  46. static int noqueue_time;
  47. volatile int stop_cpu = 0;
  48. unsigned int *sim_eval = NULL;
  49.  
  50. #define print_val(a,b,c,d) fprint_val (stdout, (a), (b), (c), (d))
  51. #define SZ_D(dp) (size_map[((dp) -> dwidth + CHAR_BIT - 1) / CHAR_BIT])
  52. #define SZ_R(rp) \
  53.     (size_map[((rp) -> width + (rp) -> offset + CHAR_BIT - 1) / CHAR_BIT])
  54.  
  55. char *get_glyph (char *iptr, char *optr, char mchar);
  56. int get_switches (char *cptr);
  57. int get_unum (char *cptr, int radix, unsigned int max, unsigned int *val);
  58. unsigned int get_rval (REG *rptr, int idx);
  59. void put_rval (REG *rptr, int idx, unsigned int val, unsigned int mask);
  60. int fprint_val (FILE *stream, unsigned int val, int rdx, int wid, int fmt);
  61. char *read_line (char *ptr, int size, FILE *stream);
  62. DEVICE *find_device (char *ptr, int *iptr);
  63. DEVICE *find_dev_from_unit (UNIT *uptr);
  64. REG *find_reg (char *ptr, char **optr, DEVICE *dptr);
  65. int reset_all (int start_device);
  66. int attach_unit (UNIT *uptr, char *cptr);
  67. int detach_all (int start_device);
  68. int detach_unit (UNIT *uptr);
  69. int ex_reg (int flag, int sw, REG *rptr);
  70. int dep_reg (int flag, int sw, char *cptr, REG *rptr);
  71. int ex_addr (int flag, int sw, unsigned int addr, DEVICE *dptr, UNIT *uptr);
  72. int dep_addr (int flag, int sw, char *cptr, unsigned int addr, DEVICE *dptr,
  73.     UNIT *uptr);
  74. int sim_activate (UNIT *uptr, int interval);
  75. int sim_cancel (UNIT *uptr);
  76. int sim_is_active (UNIT *uptr);
  77. int step_svc (UNIT *ptr);
  78.  
  79. UNIT step_unit = { UDATA (&step_svc, 0, 0)  };
  80. const char *scp_error_messages[] = {
  81.     "Address space exceeded",
  82.     "Unit not attached",
  83.     "I/O error",
  84.     "Checksum error",
  85.     "Format error",
  86.     "Unit not attachable",
  87.     "File open error",
  88.     "Memory exhausted",
  89.     "Invalid argument",
  90.     "Step expired",
  91.     "Unknown command",
  92.     "Read only argument",
  93.     "Command not completed",
  94.     "Simulation stopped",
  95.     "Goodbye",
  96.     "Console input I/O error",
  97.     "Console output I/O error",
  98.     "End of file",
  99.     "Relocation error",
  100.     "No settable parameters",
  101.     "Unit already attached"  };
  102.  
  103. const size_t size_map[] = { sizeof (char),
  104.     sizeof (char), sizeof (short), sizeof (int), sizeof (int),
  105.     sizeof (long), sizeof (long), sizeof (long), sizeof (long)  };
  106.  
  107. int main (int argc, char *argv[])
  108. {
  109. char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr;
  110. int i, stat;
  111. FILE *fpin;
  112. int reset_cmd (int flag, char *ptr);
  113. int exdep_cmd (int flag, char *ptr);
  114. int load_cmd (int flag, char *ptr);
  115. int run_cmd (int flag, char *ptr);
  116. int attach_cmd (int flag, char *ptr);
  117. int detach_cmd (int flag, char *ptr);
  118. int save_cmd (int flag, char *ptr);
  119. int restore_cmd (int flag, char *ptr);
  120. int exit_cmd (int flag, char *ptr);
  121. int set_cmd (int flag, char *ptr);
  122. int show_cmd (int flag, char *ptr);
  123. int help_cmd (int flag, char *ptr);
  124.  
  125. static CTAB cmd_table[] = {
  126.     { "RESET", &reset_cmd, 0 },
  127.     { "EXAMINE", &exdep_cmd, EX_E },
  128.     { "IEXAMINE", &exdep_cmd, EX_E+EX_I },
  129.     { "DEPOSIT", &exdep_cmd, EX_D },
  130.     { "IDEPOSIT", &exdep_cmd, EX_D+EX_I },
  131.     { "RUN", &run_cmd, RU_RUN },
  132.     { "GO", &run_cmd, RU_GO }, 
  133.     { "STEP", &run_cmd, RU_STEP },
  134.     { "CONT", &run_cmd, RU_CONT },
  135.     { "BOOT", &run_cmd, RU_BOOT },
  136.     { "ATTACH", &attach_cmd, 0 },
  137.     { "DETACH", &detach_cmd, 0 },
  138.     { "SAVE", &save_cmd, 0 },
  139.     { "RESTORE", &restore_cmd, 0 },
  140.     { "GET", &restore_cmd, 0 },
  141.     { "LOAD", &load_cmd, 0 },
  142.     { "EXIT", &exit_cmd, 0 },
  143.     { "QUIT", &exit_cmd, 0 },
  144.     { "BYE", &exit_cmd, 0 },
  145.     { "SET", &set_cmd, 0 },
  146.     { "SHOW", &show_cmd, 0 },
  147.     { "HELP", &help_cmd, 0 },
  148.     { NULL, NULL, 0 }  };
  149.  
  150. /* Main command loop */
  151.  
  152. printf ("\n%s simulator V2.2\n", sim_name);
  153. if (sim_emax <= 0) sim_emax = 1;
  154. if ((sim_eval = calloc (sim_emax, sizeof (unsigned int))) == NULL) {
  155.     printf ("Unable to allocate examine buffer\n");
  156.     exit (0);  };
  157. if (ttinit () != SCPE_OK) {
  158.     printf ("Fatal terminal initialization error\n");
  159.     exit (0);  }
  160. stop_cpu = 0;
  161. sim_interval = 0;
  162. sim_time = 0;
  163. noqueue_time = 0;
  164. sim_clock_queue = NULL;
  165. reset_all (0);
  166.  
  167. if ((argc > 1) && (argv[1] != NULL) &&
  168.     ((fpin = fopen (argv[1], "r")) != NULL)) {        /* command file? */
  169.     do {    cptr = read_line (cbuf, CBUFSIZE, fpin);
  170.         if (cptr == NULL) break;        /* exit on eof */
  171.         if (*cptr == 0) continue;        /* ignore blank */
  172.         cptr = get_glyph (cptr, gbuf, 0);    /* get command glyph */
  173.         for (i = 0; cmd_table[i].name != NULL; i++) {
  174.             if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) {
  175.             stat = cmd_table[i].action (cmd_table[i].arg, cptr);
  176.             break;  }  }
  177.         if (stat >= SCPE_BASE)
  178.             printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
  179.     } while (stat != SCPE_EXIT);  }            /* end if cmd file */
  180.  
  181. do {    printf ("sim> ");                /* prompt */
  182.     cptr = read_line (cbuf, CBUFSIZE, stdin);    /* read command line */
  183.     stat = SCPE_UNK;
  184.     if (cptr == NULL) continue;            /* ignore EOF */
  185.     if (*cptr == 0) continue;            /* ignore blank */
  186.     cptr = get_glyph (cptr, gbuf, 0);        /* get command glyph */
  187.     for (i = 0; cmd_table[i].name != NULL; i++) {
  188.         if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) {
  189.             stat = cmd_table[i].action (cmd_table[i].arg, cptr);
  190.             break;  }  }
  191.     if (stat >= SCPE_BASE)
  192.         printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
  193. } while (stat != SCPE_EXIT);
  194.  
  195. detach_all (0);
  196. ttclose ();
  197. exit (0);
  198. }
  199.  
  200. /* Exit command */
  201.  
  202. int exit_cmd (int flag, char *cptr)
  203. {
  204. return SCPE_EXIT;
  205. }
  206.  
  207. /* Help command */
  208.  
  209. int help_cmd (int flag, char *cptr)
  210. {
  211. printf ("r{eset} {ALL|<device>}   reset simulator\n");
  212. printf ("e{xamine} <list>         examine memory or registers\n");
  213. printf ("ie{xamine} <list>        interactive examine memory or registers\n");
  214. printf ("d{eposit} <list> <val>   deposit in memory or registers\n");
  215. printf ("id{eposit} <list>        interactive deposit in memory or registers\n");
  216. printf ("l{oad} <file>            load binary file\n");
  217. printf ("ru{n} {new PC}           reset and start simulation\n");
  218. printf ("go {new PC}              start simulation\n");
  219. printf ("c{ont}                   continue simulation\n");
  220. printf ("s{tep} {n}               simulate n instructions\n");
  221. printf ("b(oot) <device>|<unit>   bootstrap device\n");
  222. printf ("at{tach} <unit> <file>   attach file to simulated unit\n");
  223. printf ("det{ach} <unit>          detach file from simulated unit\n");
  224. printf ("sa{ve} <file>            save simulator to file\n");
  225. printf ("rest{ore}|ge{t} <file>   restore simulator from file\n");
  226. printf ("exi{t}|q{uit}|by{e}      exit from simulation\n");
  227. printf ("set <unit> <val>         set unit parameter\n");
  228. printf ("show <device>            show device parameters\n");
  229. printf ("sh{ow} c{onfiguration}   show configuration\n");
  230. printf ("sh{ow} q{ueue}           show event queue\n");
  231. printf ("sh{ow} t{ime}            show simulated time\n");
  232. printf ("h{elp}                   type this message\n");
  233. return SCPE_OK;
  234. }
  235.  
  236. /* Set command */
  237.  
  238. int set_cmd (int flag, char *cptr)
  239. {
  240. int unitno, r;
  241. char gbuf[CBUFSIZE];
  242. DEVICE *dptr;
  243. UNIT *uptr;
  244. MTAB *mptr;
  245.  
  246. cptr = get_glyph (cptr, gbuf, 0);            /* get glyph */
  247. dptr = find_device (gbuf, &unitno);            /* find device */
  248. if ((dptr == NULL) || (*cptr == 0)) return SCPE_ARG;    /* argument? */
  249. cptr = get_glyph (cptr, gbuf, 0);            /* get glyph */
  250. if (*cptr != 0) return SCPE_ARG;            /* now eol? */
  251. uptr = dptr -> units + unitno;
  252. if (dptr -> modifiers == NULL) return SCPE_NOPARAM;    /* any modifiers? */
  253. for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
  254.     if ((mptr -> mstring != NULL) &&
  255.         (MATCH_CMD (gbuf, mptr -> mstring) == 0)) {
  256.         if ((mptr -> valid != NULL) &&
  257.            ((r = mptr -> valid (uptr, mptr -> match)) != SCPE_OK))
  258.             return r;            /* invalid? */
  259.         uptr -> flags = (uptr -> flags & ~(mptr -> mask)) |
  260.             (mptr -> match & mptr -> mask);    /* set new value */
  261.         return SCPE_OK;  }  }
  262. return SCPE_ARG;                    /* no match */
  263. }
  264.  
  265. /* Show command */
  266.  
  267. int show_cmd (int flag, char *cptr)
  268. {
  269. int i;
  270. char gbuf[CBUFSIZE];
  271. DEVICE *dptr;
  272. int show_config (int flag, char *ptr);
  273. int show_queue (int flag, char *ptr);
  274. int show_time (int flag, char *ptr);
  275. int show_device (DEVICE *dptr, int flag);
  276. static CTAB show_table[] = {
  277.     { "CONFIGURATION", &show_config, 0 },
  278.     { "QUEUE", &show_queue, 0 },
  279.     { "TIME", &show_time, 0 },
  280.     { NULL, NULL, 0 }  };
  281.  
  282. cptr = get_glyph (cptr, gbuf, 0);            /* get glyph */
  283. for (i = 0; show_table[i].name != NULL; i++) {        /* find command */
  284.     if (MATCH_CMD (gbuf, show_table[i].name) == 0)
  285.         return show_table[i].action (show_table[i].arg, cptr);  }
  286. dptr = find_device (gbuf, NULL);            /* find device */
  287. if (dptr == NULL) return SCPE_ARG;
  288. return show_device (dptr, dptr != sim_devices[0]);
  289. }
  290.  
  291. /* Show processors */
  292.  
  293. int show_device (DEVICE *dptr, int flag)
  294. {
  295. int j;
  296. UNIT *uptr;
  297. MTAB *mptr;
  298.  
  299. printf ("%s", dptr -> name);
  300. if (dptr -> numunits > 1) printf (", %d units\n", dptr -> numunits);
  301. for (j = 0; j < dptr -> numunits; j++) {
  302.     uptr = (dptr -> units) + j;
  303.     if (dptr -> numunits > 1) printf ("  unit %d", j);
  304.     if (uptr -> flags & UNIT_FIX)
  305.         printf (", %dK%s", uptr -> capac / (flag? 1000: 1024),
  306.         ((dptr -> dwidth / dptr -> aincr) > 8)? "W": "B");
  307.     if (uptr -> flags & UNIT_ATT)
  308.         printf (", attached to %s", uptr -> filename);
  309.     else if (uptr -> flags & UNIT_ATTABLE) printf (", not attached");
  310.     if (dptr -> modifiers != NULL) {
  311.         for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
  312.             if ((mptr -> pstring != NULL) &&
  313.                ((uptr -> flags & mptr -> mask) == mptr -> match))
  314.                 printf (", %s", mptr -> pstring);  }  }
  315.     printf ("\n");  }
  316. return SCPE_OK;
  317. }
  318.  
  319. int show_config (int flag, char *cptr)
  320. {
  321. int i;
  322. DEVICE *dptr;
  323.  
  324. printf ("%s simulator configuration\n\n", sim_name);
  325. for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_device (dptr, i);
  326. return SCPE_OK;
  327. }
  328.  
  329. int show_queue (int flag, char *cptr)
  330. {
  331. DEVICE *dptr;
  332. UNIT *uptr;
  333. int accum;
  334.  
  335. if (sim_clock_queue == NULL) {
  336.     printf ("%s event queue empty, time = %-16.0f\n", sim_name, sim_time);
  337.     return SCPE_OK;  }
  338. printf ("%s event queue status, time = %-16.0f\n", sim_name, sim_time);
  339. accum = 0;
  340. for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr -> next) {
  341.     if (uptr == &step_unit) printf ("  Step timer");
  342.     else if ((dptr = find_dev_from_unit (uptr)) != NULL) {
  343.         printf ("  %s", dptr -> name);
  344.         if (dptr -> numunits > 1) printf (" unit %d",
  345.             uptr - dptr -> units);  }
  346.     else printf ("  Unknown");
  347.     printf (" at %d\n", accum + uptr -> time);
  348.     accum = accum + uptr -> time;  }
  349. return SCPE_OK;
  350. }
  351.  
  352. int show_time (int flag, char *cptr)
  353. {
  354. printf ("Time:    %-16.0f\n", sim_time);
  355. return SCPE_OK;
  356. }
  357.  
  358. /* Reset command and routines
  359.  
  360.    re[set]        reset all devices
  361.    re[set] all        reset all devices
  362.    re[set] device    reset specific device
  363. */
  364.  
  365. int reset_cmd (int flag, char *cptr)
  366. {
  367. char gbuf[CBUFSIZE];
  368. DEVICE *dptr;
  369.  
  370. if (*cptr == 0) return (reset_all (0));            /* reset(cr) */
  371. cptr = get_glyph (cptr, gbuf, 0);            /* get next glyph */
  372. if (*cptr != 0) return SCPE_ARG;            /* now (cr)? */
  373. if (strcmp (gbuf, "ALL") == 0) return (reset_all (0));
  374. if ((dptr = find_device (gbuf, NULL)) == NULL) return SCPE_ARG;
  375. if (dptr -> reset != NULL) return dptr -> reset (dptr);
  376. else return SCPE_OK;
  377. }
  378.  
  379. /* Reset devices start..end
  380.  
  381.    Inputs:
  382.     start    =    number of starting device
  383.    Outputs:
  384.     status    =    error status
  385. */
  386.  
  387. int reset_all (int start)
  388. {
  389. DEVICE *dptr;
  390. int i;
  391.  
  392. if ((start < 0) || (start > 1)) return SCPE_ARG;
  393. for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
  394.     if (dptr -> reset != NULL) dptr -> reset (dptr);  }
  395. return SCPE_OK;
  396. }
  397.  
  398. /* Load command
  399.  
  400.    lo[ad] filename        load specified file
  401. */
  402.  
  403. int load_cmd (int flag, char *cptr)
  404. {
  405. FILE *loadfile;
  406. int reason;
  407.  
  408. if (*cptr == 0) return SCPE_ARG;
  409. loadfile = fopen (cptr, "r");
  410. if (loadfile == NULL) return SCPE_OPENERR;
  411. reason = sim_load (loadfile);
  412. fclose (loadfile);
  413. return reason;
  414. }
  415.  
  416. /* Attach command
  417.  
  418.    at[tach] unit file    attach specified unit to file
  419. */
  420.  
  421. int attach_cmd (int flag, char *cptr)
  422. {
  423. char gbuf[CBUFSIZE];
  424. int unitno;
  425. DEVICE *dptr;
  426. UNIT *uptr;
  427.  
  428. if (*cptr == 0) return SCPE_ARG;
  429. cptr = get_glyph (cptr, gbuf, 0);
  430. if (*cptr == 0) return SCPE_ARG;
  431. if ((dptr = find_device (gbuf, &unitno)) == NULL) return SCPE_ARG;
  432. uptr = (dptr -> units) + unitno;
  433. if (dptr -> attach != NULL) return dptr -> attach (uptr, cptr);
  434. return attach_unit (uptr, cptr);
  435. }
  436.  
  437. int attach_unit (UNIT *uptr, char *cptr)
  438. {
  439. DEVICE *dptr;
  440. int reason;
  441.  
  442. if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT;
  443. if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT;
  444. if (uptr -> flags & UNIT_ATT) {
  445.     reason = detach_unit (uptr);
  446.     if (reason != SCPE_OK) return reason;  }
  447. uptr -> filename = calloc (CBUFSIZE, sizeof (char));
  448. if (uptr -> filename == NULL) return SCPE_MEM;
  449. strncpy (uptr -> filename, cptr, CBUFSIZE);
  450. uptr -> fileref = fopen (cptr, "r+");
  451. if (uptr -> fileref == NULL) {
  452.     uptr -> fileref = fopen (cptr, "w+");
  453.     if (uptr -> fileref == NULL) return SCPE_OPENERR;
  454.     printf ("%s: creating new file\n", dptr -> name);  }
  455. if (uptr -> flags & UNIT_BUFABLE) {
  456.     if ((uptr -> filebuf = calloc (SZ_D (dptr), uptr -> capac)) != NULL) {
  457.         printf ("%s: buffering file in memory\n", dptr -> name);
  458.         uptr -> hwmark = fread (uptr -> filebuf, SZ_D (dptr),
  459.                     uptr -> capac, uptr -> fileref);
  460.         uptr -> flags = uptr -> flags | UNIT_BUF;  }
  461.     else if (uptr -> flags & UNIT_MUSTBUF) return SCPE_MEM;  }
  462. uptr -> flags = uptr -> flags | UNIT_ATT;
  463. uptr -> pos = 0;
  464. return SCPE_OK;
  465. }
  466.  
  467. /* Detach command
  468.  
  469.    det[ach] all        detach all units
  470.    det[ach] unit    detach specified unit
  471. */
  472.  
  473. int detach_cmd (int flag, char *cptr)
  474. {
  475. char gbuf[CBUFSIZE];
  476. int unitno;
  477. DEVICE *dptr;
  478. UNIT *uptr;
  479.  
  480. if (*cptr == 0) return SCPE_ARG;
  481. cptr = get_glyph (cptr, gbuf, 0);
  482. if (*cptr != 0) return SCPE_ARG;
  483. if (strcmp (gbuf, "ALL") == 0) return (detach_all (0));
  484. if ((dptr = find_device (gbuf, &unitno)) == NULL) return SCPE_ARG;
  485. uptr = (dptr -> units) + unitno;
  486. if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT;
  487. if (dptr -> detach != NULL) return dptr -> detach (uptr);
  488. return detach_unit (uptr);
  489. }
  490.  
  491. /* Detach devices start..end
  492.  
  493.    Inputs:
  494.     start    =    number of starting device
  495.    Outputs:
  496.     status    =    error status
  497. */
  498.  
  499. int detach_all (int start)
  500. {
  501. int i, j, reason;
  502. DEVICE *dptr;
  503. UNIT *uptr;
  504.  
  505. if ((start < 0) || (start > 1)) return SCPE_ARG;
  506. for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
  507.     for (j = 0; j < dptr -> numunits; j++) {
  508.         uptr = (dptr -> units) + j;
  509.         if (dptr -> detach != NULL) reason = dptr -> detach (uptr);
  510.         else reason = detach_unit (uptr);
  511.         if (reason != SCPE_OK) return reason;  }  }
  512. return SCPE_OK;
  513. }
  514.  
  515. int detach_unit (UNIT *uptr)
  516. {
  517. DEVICE *dptr;
  518.  
  519. if (uptr == NULL) return SCPE_ARG;
  520. if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK;
  521. if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK;
  522. uptr -> flags = uptr -> flags & ~UNIT_ATT;
  523. if (uptr -> flags & UNIT_BUF) {
  524.     printf ("%s: writing buffer to file\n", dptr -> name);
  525.     uptr -> flags = uptr -> flags & ~UNIT_BUF;
  526.     rewind (uptr -> fileref);
  527.     fwrite (uptr -> filebuf, SZ_D (dptr), uptr -> hwmark, uptr -> fileref);
  528.     if (ferror (uptr -> fileref)) perror ("I/O error");
  529.     free (uptr -> filebuf);
  530.     uptr -> filebuf = NULL;  }
  531. free (uptr -> filename);
  532. uptr -> filename = NULL;
  533. return (fclose (uptr -> fileref) == EOF)? SCPE_IOERR: SCPE_OK;
  534. }
  535.  
  536. /* Save command
  537.  
  538.    sa[ve] filename        save state to specified file
  539. */
  540.  
  541. int save_cmd (int flag, char *cptr)
  542. {
  543. FILE *sfile;
  544. int i, j, k, t, reason, zerocnt, high;
  545. unsigned int val;
  546. DEVICE *dptr;
  547. UNIT *uptr;
  548. REG *rptr;
  549.  
  550. #define WRITE_I(xx) fwrite (&(xx), sizeof (int), 1, sfile)
  551.  
  552. if (*cptr == 0) return SCPE_ARG;
  553. if ((sfile = fopen (cptr, "w")) == NULL) return SCPE_OPENERR;
  554. fputs (sim_name, sfile);                /* sim name */
  555. fputc ('\n', sfile);
  556. fwrite (&sim_time, sizeof (double), 1, sfile);        /* sim time */
  557.  
  558. for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {    /* loop thru devices */
  559.     fputs (dptr -> name, sfile);            /* device name */
  560.     fputc ('\n', sfile);
  561.     for (j = 0; j < dptr -> numunits; j++) {
  562.         uptr = (dptr -> units) + j;
  563.         t = sim_is_active (uptr);
  564.         WRITE_I (j);                /* unit number */
  565.         WRITE_I (t);                /* activation time */
  566.         WRITE_I (uptr -> u3);            /* unit specific */
  567.         WRITE_I (uptr -> u4);
  568.         if (uptr -> flags & UNIT_ATT) fputs (uptr -> filename, sfile);
  569.         fputc ('\n', sfile);
  570.         if (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&
  571.              (dptr -> examine != NULL) &&
  572.             ((high = uptr -> capac) != 0)) {    /* memory-like unit? */
  573.             WRITE_I (high);            /* memory limit */
  574.             zerocnt = 0;
  575.             for (k = 0; k < high; k = k + (dptr -> aincr)) {
  576.                 reason = dptr -> examine (&val, k, uptr, 0);
  577.                 if (reason != SCPE_OK) return reason;
  578.                 if (val == 0) zerocnt = zerocnt - 1;
  579.                 else {    if (zerocnt) WRITE_I (zerocnt);
  580.                     zerocnt = 0;
  581.                     WRITE_I (val);  }  }
  582.             if (zerocnt) WRITE_I (zerocnt);  }
  583.         else {    k = 0;
  584.             WRITE_I (k);  }  }
  585.     t = -1; WRITE_I (t);                /* end units */
  586.     for (rptr = dptr -> registers;            /* loop thru regs */
  587.         (rptr != NULL) && (rptr -> name != NULL); rptr++) {
  588.         fputs (rptr -> name, sfile);        /* name */
  589.         fputc ('\n', sfile);
  590.         for (k = 0; k < rptr -> depth; k++) {    /* loop thru values */
  591.             val = get_rval (rptr, k);    /* get value */
  592.             WRITE_I (val);  }  }        /* store */
  593.     fputc ('\n', sfile);  }                /* end registers */
  594. fputc ('\n', sfile);                    /* end devices */
  595. reason = (ferror (sfile))? SCPE_IOERR: SCPE_OK;        /* error during save? */
  596. fclose (sfile);
  597. return reason;
  598. }
  599.  
  600. /* Restore command
  601.  
  602.    re[store] filename        restore state from specified file
  603. */
  604.  
  605. int restore_cmd (int flag, char *cptr)
  606. {
  607. char buf[CBUFSIZE];
  608. FILE *rfile;
  609. int i, j, k, data, reason, unitno, time, high;
  610. unsigned int val, mask;
  611. DEVICE *dptr;
  612. UNIT *uptr;
  613. REG *rptr;
  614.  
  615. #define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \
  616.     { fclose (rfile); return SCPE_IOERR;  }
  617. #define READ_I(xx) if (fread (&xx, sizeof (int), 1, rfile) <= 0) \
  618.     { fclose (rfile); return SCPE_IOERR;  }
  619.  
  620. if (*cptr == 0) return SCPE_ARG;
  621. if ((rfile = fopen (cptr, "r")) == NULL) return SCPE_OPENERR;
  622. READ_S (buf);                        /* sim name */
  623. if (strcmp (buf, sim_name)) {
  624.     printf ("Wrong system type: %s\n", buf);
  625.     fclose (rfile);
  626.     return SCPE_OK;  }
  627. fread (&sim_time, sizeof (double), 1, rfile);        /* sim time */
  628.  
  629. for ( ;; ) {                        /* device loop */
  630.     READ_S (buf);                    /* read device name */
  631.     if (buf[0] == 0) break;                /* last? */
  632.     if ((dptr = find_device (buf, NULL)) == NULL) {
  633.         printf ("Invalid device name: %s\n", buf);
  634.         fclose (rfile);
  635.         return SCPE_INCOMP;  }
  636.     for ( ;; ) {                    /* unit loop */
  637.         READ_I (unitno);            /* unit number */
  638.         if (unitno < 0) break;
  639.         if (unitno >= dptr -> numunits) {
  640.             printf ("Invalid unit number %s%d\n", dptr -> name,
  641.                 unitno);
  642.             fclose (rfile);
  643.             return SCPE_INCOMP;  }
  644.         READ_I (time);                /* event time */
  645.         uptr = (dptr -> units) + unitno;
  646.         sim_cancel (uptr);
  647.         if (time > 0) sim_activate (uptr, time - 1);
  648.         READ_I (uptr -> u3);            /* device specific */
  649.         READ_I (uptr -> u4);
  650.         READ_S (buf);                /* attached file */
  651.         if (buf[0] != 0) {
  652.             reason = attach_unit (uptr, buf);
  653.             if (reason != SCPE_OK) return reason;  }
  654.         READ_I (high);                /* memory capacity */
  655.         if ((high > 0) &&            /* validate if > 0 */
  656.            (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||
  657.              (high > uptr -> capac) || (dptr -> deposit == NULL))) {
  658.             printf ("Invalid memory bound: %u\n", high);
  659.             fclose (rfile);
  660.             return SCPE_INCOMP;  }
  661.         for (i = 0; i < high; i = i + (dptr -> aincr)) {
  662.             READ_I (data);
  663.             if (data < 0) {
  664.                 for (j = data + 1; j < 0; j++) {
  665.                     reason = dptr -> deposit (0, i, uptr, 0);
  666.                     if (reason != SCPE_OK) return reason;
  667.                     i = i + (dptr -> aincr);  }
  668.                 data = 0;  }
  669.             reason = dptr -> deposit (data, i, uptr, 0);
  670.             if (reason != SCPE_OK) return reason;  }
  671.         }                    /* end unit loop */
  672.     for ( ;; ) {                    /* register loop */
  673.         READ_S (buf);                /* read reg name */
  674.         if (buf[0] == 0) break;            /* last? */
  675.         if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {
  676.             printf ("Invalid register name: %s\n", buf);
  677.             fclose (rfile);
  678.             return SCPE_INCOMP;  }
  679.         mask = (1u << rptr -> width) - 1;
  680.         for (k = 0; k < rptr -> depth; k++) {    /* loop thru values */
  681.             READ_I (val);            /* read value */
  682.             if (val > mask)
  683.                 printf ("Invalid register value: %s\n", buf);
  684.             else put_rval (rptr, k, val, mask);  }  }
  685.     }                        /* end device loop */
  686. fclose (rfile);
  687. return SCPE_OK;
  688. }
  689.  
  690. /* Run, go, cont, step commands
  691.  
  692.    ru[n] [new PC]    reset and start simulation
  693.    go [new PC]        start simulation
  694.    co[nt]        start simulation
  695.    s[tep] [step limit]    start simulation for 'limit' instructions
  696.    b[oot] device    bootstrap from device and start simulation
  697. */
  698.  
  699. int run_cmd (int flag, char *cptr)
  700. {
  701. char gbuf[CBUFSIZE];
  702. int i, j, addr, unitno, r;
  703. unsigned int step;
  704. DEVICE *dptr;
  705. UNIT *uptr;
  706. void int_handler (int signal);
  707.  
  708. step = 0;
  709. if (((flag == RU_RUN) || (flag == RU_GO)) && (*cptr != 0)) {    /* run or go */
  710.     cptr = get_glyph (cptr, gbuf, 0);        /* get PC, store */
  711.     if ((r = dep_reg (0, 0, gbuf, sim_PC)) != SCPE_OK) return r;  }
  712.  
  713. if (flag == RU_STEP) {                    /* step */
  714.     if (*cptr == 0) step = 1;
  715.     else {    cptr = get_glyph (cptr, gbuf, 0);
  716.         if ((get_unum (gbuf, 10, -1, &step) != SCPE_OK) ||
  717.             (step == 0)) return SCPE_ARG;  }  }
  718.  
  719. if (flag == RU_BOOT) {                    /* boot */
  720.     if (*cptr == 0) return SCPE_ARG;
  721.     cptr = get_glyph (cptr, gbuf, 0);
  722.     if ((dptr = find_device (gbuf, &unitno)) == NULL) return SCPE_ARG;
  723.     if (dptr -> boot == NULL) return SCPE_ARG;
  724.     uptr = dptr -> units + unitno;
  725.     if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT;
  726.     if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
  727.     if ((r = dptr -> boot (unitno)) != SCPE_OK) return r;  }
  728.  
  729. if (*cptr != 0) return SCPE_ARG;
  730.  
  731. if ((flag == RU_RUN) || (flag == RU_BOOT)) {        /* run or boot */
  732.     sim_interval = 0;                /* reset queue */
  733.     sim_time = 0;
  734.     noqueue_time = 0;
  735.     sim_clock_queue = NULL;
  736.     reset_all (0);  }                /* reset devices */
  737. for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {
  738.     for (j = 0; j < dptr -> numunits; j++) {
  739.         uptr = (dptr -> units) + j;
  740.         if ((uptr -> flags & (UNIT_ATT + UNIT_SEQ)) ==
  741.             (UNIT_ATT + UNIT_SEQ))
  742.             fseek (uptr -> fileref, uptr -> pos, SEEK_SET);  }  }
  743. stop_cpu = 0;
  744. if ((int) signal (SIGINT, int_handler) == -1) {        /* set WRU */
  745.     printf ("Simulator interrupt handler setup failed\n");
  746.     return SCPE_OK;  }
  747. if (ttrunstate () != SCPE_OK) {                /* set console */
  748.     ttcmdstate ();
  749.     printf ("Simulator terminal setup failed\n");
  750.     return SCPE_OK;  }
  751. if (step) sim_activate (&step_unit, step);        /* set step timer */
  752. r = sim_instr();
  753.  
  754. ttcmdstate ();                        /* restore console */
  755. signal (SIGINT, SIG_DFL);                /* cancel WRU */
  756. sim_cancel (&step_unit);                /* cancel step timer */
  757. if (sim_clock_queue != NULL) {                /* update sim time */
  758.     UPDATE_SIM_TIME (sim_clock_queue -> time);  }
  759. else {    UPDATE_SIM_TIME (noqueue_time);  }
  760. #ifdef VMS
  761. printf ("\n");
  762. #endif
  763. if (r >= SCPE_BASE) printf ("\n%s, PC: ", scp_error_messages[r - SCPE_BASE]);
  764. else printf ("\n%s, PC: ", sim_stop_messages[r]);
  765. print_val (addr = get_rval (sim_PC, 0), sim_PC -> radix,
  766.     sim_PC -> width, sim_PC -> flags & REG_FMT);
  767. if (((dptr = sim_devices[0]) != NULL) && (dptr -> examine != NULL)) {
  768.     for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
  769.     for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr -> aincr) {
  770.         if (r = dptr -> examine (&sim_eval[i], j, dptr -> units,
  771.             SWMASK ('V')) != SCPE_OK) break;  }
  772.     if ((r == SCPE_OK) || (i > 0)) {
  773.         printf (" (");
  774.         if (print_sym (addr, sim_eval, TRUE, SWMASK('M')) > 0)
  775.             print_val (sim_eval[0], dptr -> dradix,
  776.                 dptr -> dwidth, PV_RZRO);
  777.         printf (")");  }  }
  778. printf ("\n");
  779. return SCPE_OK;
  780. }
  781.  
  782. /* Run time routines */
  783.  
  784. /* Unit service for step timeout, originally scheduled by STEP n command
  785.  
  786.    Return step timeout SCP code, will cause simulation to stop
  787. */
  788.  
  789. int step_svc (UNIT *uptr)
  790. {
  791. return SCPE_STEP;
  792. }
  793.  
  794. /* Signal handler for ^C signal
  795.  
  796.    Set stop simulation flag
  797. */
  798.  
  799. void int_handler (int sig)
  800. {
  801. stop_cpu = 1;
  802. return;
  803. }
  804.  
  805. /* Examine/deposit commands
  806.  
  807.    ex[amine] [unit] list        examine
  808.    de[posit] [unit] list val        deposit
  809.    ie[xamine] [unit] list        interactive examine
  810.    id[eposit] [unit] list        interactive deposit
  811.  
  812.    list                    list of addresses and registers
  813.     addr[:addr|-addr]        address range
  814.     ALL                all addresses
  815.     register[:register|-register]    register range
  816.     STATE                all registers
  817. */
  818.  
  819. int exdep_cmd (int flag, char *cptr)
  820. {
  821. char gbuf[CBUFSIZE], *gptr, *tptr;
  822. int sw, reason, unitno;
  823. unsigned int low, high;
  824. DEVICE *dptr;
  825. UNIT *uptr;
  826. REG *lowr, *highr;
  827. int exdep_addr_loop (int flag, int sw, char *ptr, unsigned int low,
  828.     unsigned int high, DEVICE *dptr, UNIT *uptr);
  829. int exdep_reg_loop (int flag, int sw, char *ptr, REG *lptr, REG *hptr);
  830.  
  831. if (*cptr == 0) return SCPE_ARG;            /* err if no args */
  832. cptr = get_glyph (cptr, gbuf, 0);
  833. if ((sw = get_switches (gbuf)) != 0) {            /* try for switches */
  834.     if ((sw < 0) || (*cptr == 0)) return SCPE_ARG;    /* err if no args */
  835.     cptr = get_glyph (cptr, gbuf, 0);  }        /* if found, advance */
  836. if ((dptr = find_device (gbuf, &unitno)) != NULL) {    /* try for unit */
  837.     if (*cptr == 0) return SCPE_ARG;        /* err if no args */
  838.     cptr = get_glyph (cptr, gbuf, 0);  }        /* if found, advance */
  839. else {    dptr = sim_devices[0];                /* CPU is default */
  840.     unitno = 0;  }
  841. if ((*cptr == 0) == (flag == 0)) return SCPE_ARG;    /* eol if needed? */
  842.  
  843. gptr = gbuf;
  844. uptr = (dptr -> units) + unitno;
  845. while (*gptr != 0) {
  846.     errno = 0;
  847.     low = strtoul (gptr, &tptr, dptr -> aradix);
  848.     if ((errno == 0) && (gptr != tptr)) {
  849.         high = low;
  850.         if ((*tptr == '-') || (*tptr == ':')) {
  851.             gptr = tptr + 1;
  852.             errno = 0;
  853.             high = strtoul (gptr, &tptr, dptr -> aradix);
  854.             if (errno || (gptr == tptr)) return SCPE_ARG;  }
  855.         if (*tptr == ',') tptr++;
  856.         else if (*tptr != 0) return SCPE_ARG;
  857.         reason = exdep_addr_loop (flag, sw, cptr, low, high,
  858.             dptr, uptr);
  859.         if (reason != SCPE_OK) return reason; }
  860.  
  861.     else if (strncmp (gptr, "ALL", strlen ("ALL")) == 0) {
  862.         tptr = gptr + strlen ("ALL");
  863.         if (*tptr == ',') tptr++;
  864.         else if (*tptr != 0) return SCPE_ARG;
  865.         if ((uptr -> capac == 0) | (flag == EX_E)) return SCPE_ARG;
  866.         high = (uptr -> capac) - (dptr -> aincr);
  867.         reason = exdep_addr_loop (flag, sw, cptr, 0, high, dptr, uptr);
  868.         if (reason != SCPE_OK) return reason; }
  869.  
  870.     else if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
  871.         tptr = gptr + strlen ("STATE");
  872.         if (*tptr == ',') tptr++;
  873.         else if (*tptr != 0) return SCPE_ARG;
  874.         if ((lowr = dptr -> registers) == NULL) return SCPE_ARG;
  875.         for (highr = lowr; highr -> name != NULL; highr++) ;
  876.         reason = exdep_reg_loop (flag, sw+SWHIDE, cptr, lowr, --highr);
  877.         if (reason != SCPE_OK) return reason; }
  878.  
  879.     else {    lowr = find_reg (gptr, &tptr, dptr);
  880.         if (lowr == NULL) return SCPE_ARG;
  881.         highr = lowr;
  882.         if ((*tptr == '-') || (*tptr == ':')) {
  883.             highr = find_reg (tptr + 1, &tptr, dptr);
  884.             if (highr == NULL) return SCPE_ARG;  }
  885.         if (*tptr == ',') tptr++;
  886.         else if (*tptr != 0) return SCPE_ARG;
  887.         reason = exdep_reg_loop (flag, sw, cptr, lowr, highr);
  888.         if (reason != SCPE_OK) return reason; }
  889.  
  890.     gptr = tptr;  }                    /* end while */
  891. return SCPE_OK;
  892. }
  893.  
  894. /* Loop controllers for examine/deposit
  895.  
  896.    exdep_reg_loop    examine/deposit range of registers
  897.    exdep_addr_loop    examine/deposit range of addresses
  898. */
  899.  
  900. int exdep_reg_loop (int flag, int sw, char *cptr, REG *lowr, REG *highr)
  901. {
  902. int reason;
  903. REG *rptr;
  904.  
  905. if ((lowr == NULL) || (highr == NULL)) return SCPE_ARG;
  906. if (lowr > highr) return SCPE_ARG;
  907. for (rptr = lowr; rptr <= highr; rptr++) {
  908.     if ((sw & SWHIDE) && (rptr -> flags & REG_HIDDEN)) continue;
  909.     if (flag != EX_D) {
  910.         reason = ex_reg (flag, sw, rptr);
  911.         if (reason != SCPE_OK) return reason; }
  912.     if (flag != EX_E) {
  913.         reason = dep_reg (flag, sw, cptr, rptr);
  914.         if (reason != SCPE_OK) return reason;  }  }
  915. return SCPE_OK;
  916. }
  917.  
  918. int exdep_addr_loop (int flag, int sw, char *cptr, unsigned int low,
  919.     unsigned int high, DEVICE *dptr, UNIT *uptr)
  920. {
  921. int reason;
  922. unsigned int i, mask;
  923.  
  924. reason = 0;
  925. mask = (1u << dptr -> awidth) - 1;
  926. if ((low > mask) || (high > mask)) return SCPE_ARG;
  927. if (low > high) return SCPE_ARG;
  928. for (i = low; i <= high; i = i + (dptr -> aincr)) {
  929.     if (flag != EX_D) {
  930.         reason = ex_addr (flag, sw, i, dptr, uptr);
  931.         if (reason > SCPE_OK) return reason;  }
  932.     if (flag != EX_E) {
  933.         reason = dep_addr (flag, sw, cptr, i, dptr, uptr);
  934.         if (reason > SCPE_OK) return reason;  }
  935.     if (reason < SCPE_OK) i = i - (reason * dptr -> aincr);  }
  936. return SCPE_OK;
  937. }
  938.  
  939. /* Examine register routine
  940.  
  941.    Inputs:
  942.     flag    =    type of ex/mod command (ex, iex, idep)
  943.     sw    =    switches
  944.     rptr    =    pointer to register descriptor
  945.    Outputs:
  946.     return    =    error status
  947. */
  948.  
  949. int ex_reg (int flag, int sw, REG *rptr)
  950. {
  951. unsigned int val;
  952.  
  953. if (rptr == NULL) return SCPE_ARG;
  954. printf ("%s:    ", rptr -> name);
  955. if (!(flag & EX_E)) return SCPE_OK;
  956. val = get_rval (rptr, 0);
  957. print_val (val, rptr -> radix, rptr -> width, rptr -> flags & REG_FMT);
  958. if (flag & EX_I) printf ("    ");
  959. else printf ("\n");
  960. return SCPE_OK;
  961. }
  962.  
  963. /* Get register value
  964.  
  965.    Inputs:
  966.     rptr    =    pointer to register descriptor
  967.     idx    =    index (SAVE register buffers only)
  968.    Outputs:
  969.     return    =    register value
  970. */
  971.  
  972. unsigned int get_rval (REG *rptr, int idx)
  973. {
  974. unsigned int val;
  975. size_t sz;
  976.  
  977. sz = SZ_R (rptr);
  978. if (sz == sizeof (char)) val = *(((unsigned char *) rptr -> loc) + idx);
  979. else if (sz == sizeof (short)) val = *(((unsigned short *) rptr -> loc) + idx);
  980. else if (sz == sizeof (int)) val = *(((unsigned int *) rptr -> loc) + idx);
  981. else if (sz == sizeof (long)) val = *(((unsigned long *) rptr -> loc) + idx);
  982. val = (val >> rptr -> offset) & ((1u << rptr -> width) - 1);
  983. return val;
  984. }
  985.  
  986. /* Deposit register routine
  987.  
  988.    Inputs:
  989.     flag    =    type of deposit (normal/interactive)
  990.     sw    =    switches
  991.     cptr    =    pointer to input string
  992.     rptr    =    pointer to register descriptor
  993.    Outputs:
  994.     return    =    error status
  995. */
  996.  
  997. int dep_reg (int flag, int sw, char *cptr, REG *rptr)
  998. {
  999. char gbuf[CBUFSIZE];
  1000. unsigned int val, mask;
  1001.  
  1002. if ((cptr == NULL) || (rptr == NULL)) return SCPE_ARG;
  1003. if (rptr -> flags & REG_RO) return SCPE_RO;
  1004. if (flag & EX_I) {
  1005.     cptr = read_line (gbuf, CBUFSIZE, stdin);
  1006.     if (cptr == NULL) return 1;            /* force exit */
  1007.     if (*cptr == 0) return SCPE_OK;     }        /* success */
  1008. errno = 0;
  1009. mask = (1u << rptr -> width) - 1;
  1010. if (get_unum (cptr, rptr -> radix, mask, &val) != SCPE_OK) return SCPE_ARG;
  1011. if ((rptr -> flags & REG_NZ) && (val == 0)) return SCPE_ARG;
  1012. put_rval (rptr, 0, val, mask);
  1013. return SCPE_OK;
  1014. }
  1015.  
  1016. /* Put register value
  1017.  
  1018.    Inputs:
  1019.     rptr    =    pointer to register descriptor
  1020.     idx    =    index (RESTORE reg buffers only)
  1021.     val    =    new value
  1022.     mask    =    mask
  1023.    Outputs:
  1024.     none
  1025. */
  1026.  
  1027. void put_rval (REG *rptr, int idx, unsigned int val, unsigned int mask)
  1028. {
  1029. size_t sz;
  1030. #define PUT_RVAL(sz,rp,id,val,msk) \
  1031.     *(((unsigned sz *) rp -> loc) + id) = \
  1032.         (*(((unsigned sz *) rp -> loc) + id) & \
  1033.         ~((msk) << (rp) -> offset)) | ((val) << (rp) -> offset)
  1034.  
  1035. sz = SZ_R (rptr);
  1036. if (sz == sizeof (char)) PUT_RVAL (char, rptr, idx, val, mask);
  1037. else if (sz == sizeof (short)) PUT_RVAL (short, rptr, idx, val, mask);
  1038. else if (sz == sizeof (int)) PUT_RVAL (int, rptr, idx, val, mask);
  1039. else if (sz == sizeof (long)) PUT_RVAL (long, rptr, idx, val, mask);
  1040. return;
  1041. }
  1042.  
  1043. /* Examine address routine
  1044.  
  1045.    Inputs:
  1046.     flag    =    type of ex/mod command (ex, iex, idep)
  1047.     sw    =    switches
  1048.     addr    =    address to examine
  1049.     dptr    =    pointer to device
  1050.     uptr    =    pointer to unit
  1051.    Outputs:
  1052.     return    =    if >= 0, error status
  1053.             if < 0, number of extra words retired
  1054. */
  1055.  
  1056. int ex_addr (int flag, int sw, unsigned int addr, DEVICE *dptr, UNIT *uptr)
  1057. {
  1058. unsigned int mask, loc;
  1059. int i, j, reason;
  1060. size_t sz;
  1061.  
  1062. if (dptr == NULL) return SCPE_ARG;
  1063. print_val (addr, dptr -> aradix, dptr -> awidth, PV_LEFT);
  1064. printf (":    ");
  1065. if (!(flag & EX_E)) return SCPE_OK;
  1066.  
  1067. mask = (1u << dptr -> dwidth) - 1;
  1068. for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
  1069. for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr -> aincr) {
  1070.     if (dptr -> examine != NULL) {
  1071.         reason = dptr -> examine (&sim_eval[i], j, uptr, sw);
  1072.         if (reason != SCPE_OK) break;  }
  1073.     else {    if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
  1074.         if ((uptr -> flags & UNIT_FIX) && (addr >= uptr -> capac)) {
  1075.             reason = SCPE_NXM;
  1076.             break;  }
  1077.         sz = SZ_D (dptr);
  1078.         loc = j / dptr -> aincr;
  1079.         if (uptr -> flags & UNIT_BUF) {
  1080.             if (sz == sizeof (char)) sim_eval[i] =
  1081.                 *(((unsigned char *) uptr -> filebuf) + loc);
  1082.             else if (sz == sizeof (short)) sim_eval[i] =
  1083.                 *(((unsigned short *) uptr -> filebuf) + loc);
  1084.             else if (sz == sizeof (int)) sim_eval[i] =
  1085.                 *(((unsigned int *) uptr -> filebuf) + loc);
  1086.             else if (sz == sizeof (long)) sim_eval[i] =
  1087.                 *(((unsigned long *) uptr -> filebuf) + loc);  }
  1088.         else {    fseek (uptr -> fileref, sz * loc, SEEK_SET);
  1089.             fread (&sim_eval[i], sz, 1, uptr -> fileref);
  1090.             if ((feof (uptr -> fileref)) &&
  1091.                !(uptr -> flags & UNIT_FIX)) {
  1092.                 reason = SCPE_EOF;
  1093.                 break;  }
  1094.              else if (ferror (uptr -> fileref)) {
  1095.                 clearerr (uptr -> fileref);
  1096.                 reason = SCPE_IOERR;
  1097.                 break;  }  }  }
  1098.     sim_eval[i] = sim_eval[i] & mask;  }
  1099. if ((reason != SCPE_OK) && (i == 0)) return reason;
  1100.  
  1101. if ((reason = print_sym (addr, sim_eval, dptr == sim_devices[0], sw)) > 0)
  1102.     reason = print_val (sim_eval[0], dptr -> dradix, dptr -> dwidth, PV_RZRO);
  1103. if (flag & EX_I) printf ("    ");
  1104. else printf ("\n");
  1105. return reason;
  1106. }
  1107.  
  1108. /* Deposit address routine
  1109.  
  1110.    Inputs:
  1111.     flag    =    type of deposit (normal/interactive)
  1112.     sw    =    switches
  1113.     cptr    =    pointer to input string
  1114.     addr    =    address to examine
  1115.     dptr    =    pointer to device
  1116.     uptr    =    pointer to unit
  1117.    Outputs:
  1118.     return    =    if >= 0, error status
  1119.             if < 0, number of extra words retired
  1120. */
  1121.  
  1122. int dep_addr (int flag, int sw, char *cptr, unsigned int addr,
  1123.     DEVICE *dptr, UNIT *uptr)
  1124. {
  1125. unsigned int mask, loc;
  1126. int i, j, count, r, reason;
  1127. size_t sz;
  1128. char *tptr, gbuf[CBUFSIZE];
  1129.  
  1130. if (dptr == NULL) return SCPE_ARG;
  1131. if (flag & EX_I) {
  1132.     cptr = read_line (gbuf, CBUFSIZE, stdin);
  1133.     if (cptr == NULL) return 1;            /* force exit */
  1134.     if (*cptr == 0) return SCPE_OK;     }        /* success */
  1135. mask = (1u << dptr -> dwidth) - 1;
  1136.  
  1137. if ((reason = parse_sym (cptr, addr, dptr == sim_devices[0], sim_eval)) > 0) {
  1138.     if ((reason = get_unum (cptr, dptr -> dradix, mask, sim_eval)) != SCPE_OK)
  1139.     return reason;  }
  1140. count = 1 - reason;
  1141.  
  1142. for (i = 0, j = addr; i < count; i++, j = j + dptr -> aincr) {
  1143.     sim_eval[i] = sim_eval[i] & mask;
  1144.     if (dptr -> deposit != NULL) {
  1145.         r = dptr -> deposit (sim_eval[i], j, uptr, sw);
  1146.         if (r != SCPE_OK) return r;  }
  1147.     else {    if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
  1148.         if ((uptr -> flags & UNIT_FIX) && (j >= uptr -> capac))
  1149.             return SCPE_NXM;
  1150.         sz = SZ_D (dptr);
  1151.         loc = j / dptr -> aincr;
  1152.         if (uptr -> flags & UNIT_BUF) {
  1153.             if (sz == sizeof (char)) *(((unsigned char *)
  1154.                 uptr -> filebuf) + loc) = sim_eval[i];
  1155.             else if (sz == sizeof (short)) *(((unsigned short *)
  1156.                 uptr -> filebuf) + loc) = sim_eval[i];
  1157.             else if (sz == sizeof (int)) *(((unsigned int *)
  1158.                 uptr -> filebuf) + loc) = sim_eval[i];
  1159.             else if (sz == sizeof (long)) *(((unsigned long *)
  1160.                 uptr -> filebuf) + loc) = sim_eval[i];
  1161.             if (loc >= uptr -> hwmark) uptr -> hwmark = loc + 1;  }
  1162.         else {    fseek (uptr -> fileref, sz * loc, SEEK_SET);
  1163.             fwrite (sim_eval, sz, 1, uptr -> fileref);
  1164.             if (ferror (uptr -> fileref)) {
  1165.                 clearerr (uptr -> fileref);
  1166.                 return SCPE_IOERR;  }  }  }  }
  1167. return reason;
  1168. }
  1169.  
  1170. /* String processing routines
  1171.  
  1172.    read_line        read line
  1173.  
  1174.    Inputs:
  1175.     cptr    =    pointer to buffer
  1176.     size    =    maximum size
  1177.     stream    =    pointer to input stream
  1178.    Outputs:
  1179.     optr    =    pointer to first non-blank character
  1180.             NULL if EOF
  1181. */
  1182.  
  1183. char *read_line (char *cptr, int size, FILE *stream)
  1184. {
  1185. char *tptr;
  1186.  
  1187. cptr = fgets (cptr, size, stream);            /* get cmd line */
  1188. if (cptr == NULL) return NULL;                /* ignore EOF */
  1189. for (tptr = cptr; tptr < (cptr + size); tptr++)        /* remove cr */
  1190.     if (*tptr == '\n') *tptr = 0; 
  1191. while (isspace (*cptr)) cptr++;                /* absorb spaces */
  1192. return cptr;
  1193. }
  1194.  
  1195. /* get_glyph        get next glyph
  1196.  
  1197.    Inputs:
  1198.     iptr    =    pointer to input string
  1199.     optr    =    pointer to output string
  1200.     mchar    =    optional end of glyph character
  1201.    Outputs
  1202.     result    =    pointer to next character in input string
  1203. */
  1204.  
  1205. char *get_glyph (char *iptr, char *optr, char mchar)
  1206. {
  1207. while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) {
  1208.     if (islower (*iptr)) *optr = toupper (*iptr);
  1209.     else *optr = *iptr;
  1210.     iptr++; optr++;  }
  1211. *optr = 0;
  1212. if (mchar && (*iptr == mchar)) iptr++;            /* skip terminator */
  1213. while (isspace (*iptr)) iptr++;                /* absorb spaces */
  1214. return iptr;
  1215. }
  1216.  
  1217. /* get_yn        yes/no question
  1218.  
  1219.    Inputs:
  1220.     cptr    =    pointer to question
  1221.     deflt    =    default answer
  1222.    Outputs:
  1223.     result    =    true if yes, false if no
  1224. */
  1225.  
  1226. int get_yn (char *ques, int deflt)
  1227. {
  1228. char cbuf[CBUFSIZE], *cptr;
  1229.  
  1230. printf ("%s ", ques);
  1231. cptr = read_line (cbuf, CBUFSIZE, stdin);
  1232. if ((cptr == NULL) || (*cptr == 0)) return deflt;
  1233. if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE;
  1234. return FALSE;
  1235. }
  1236.  
  1237. /* get_unum        unsigned number
  1238.  
  1239.    Inputs:
  1240.     cptr    =    pointer to input string
  1241.     radix    =    input radix
  1242.     max    =    maximum acceptable value
  1243.     val    =    pointer to output value
  1244.    Outputs:
  1245.     status    =    error status
  1246. */
  1247.  
  1248. int get_unum (char *cptr, int radix, unsigned int max, unsigned int *val)
  1249. {
  1250. char *tptr;
  1251.  
  1252. errno = 0;
  1253. *val = strtoul (cptr, &tptr, radix);
  1254. if (errno || (cptr == tptr) || (*val > max) || (*tptr != 0)) return SCPE_ARG;
  1255. return SCPE_OK;
  1256. }
  1257.  
  1258. /* Find_device        find device matching input string
  1259.  
  1260.    Inputs:
  1261.     cptr    =    pointer to input string
  1262.     iptr    =    pointer to unit number (can be null)
  1263.    Outputs:
  1264.     result    =    pointer to device
  1265.     *iptr    =    unit number, if valid
  1266. */
  1267.  
  1268. DEVICE *find_device (char *cptr, int *iptr)
  1269. {
  1270. int i, lenn;
  1271. unsigned int unitno;
  1272. char *tptr;
  1273. DEVICE *dptr;
  1274.  
  1275. for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
  1276.     lenn = strlen (dptr -> name);
  1277.     if (strncmp (cptr, dptr -> name, lenn) != 0) continue;
  1278.     cptr = cptr + lenn;
  1279.     if (*cptr == 0) unitno = 0;
  1280.     else if (get_unum (cptr, 10, dptr -> numunits - 1, &unitno) != SCPE_OK)
  1281.         return NULL;
  1282.     if (iptr != NULL) *iptr = unitno;
  1283.     return sim_devices[i];  }    
  1284. return NULL;
  1285. }
  1286.  
  1287. /* Find_dev_from_unit    find device for unit
  1288.  
  1289.    Inputs:
  1290.     uptr    =    pointer to unit
  1291.    Outputs:
  1292.     result    =    pointer to device
  1293. */
  1294.  
  1295. DEVICE *find_dev_from_unit (UNIT *uptr)
  1296. {
  1297. DEVICE *dptr;
  1298. int i,j;
  1299.  
  1300. for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
  1301.     for (j = 0; j < dptr -> numunits; j++) {
  1302.         if (uptr == (dptr -> units + j)) return dptr;  }  }
  1303. return NULL;
  1304. }
  1305.  
  1306. /* find_reg        find register matching input string
  1307.  
  1308.    Inputs:
  1309.     cptr    =    pointer to input string
  1310.     optr    =    pointer to output pointer (can be null)
  1311.     dptr    =    pointer to device
  1312.    Outputs:
  1313.     result    =    pointer to register, NULL if error
  1314.     *optr    =    pointer to next character in input string
  1315. */
  1316.  
  1317. REG *find_reg (char *cptr, char **optr, DEVICE *dptr)
  1318. {
  1319. char *tptr;
  1320. REG *rptr;
  1321.  
  1322. if ((cptr == NULL) || (dptr == NULL)) return NULL;
  1323. if (dptr -> registers == NULL) return NULL;
  1324. tptr = cptr;
  1325. do { tptr++; } while (isalnum (*tptr) || (*tptr == '_'));
  1326. for (rptr = dptr -> registers; rptr -> name != NULL; rptr++) {
  1327.     if (strncmp (cptr, rptr -> name, tptr - cptr) == 0) {
  1328.         if (optr != NULL) *optr = tptr;
  1329.         return rptr;  }  }
  1330. return NULL;
  1331. }
  1332.  
  1333. /* get_switches        get switches from input string
  1334.  
  1335.    Inputs:
  1336.     cptr    =    pointer to input string
  1337.    Outputs:
  1338.     sw    =    switch bit mask
  1339.             0 if no switches, -1 if error
  1340. */
  1341.  
  1342. int get_switches (char *cptr)
  1343. {
  1344. int sw;
  1345.  
  1346. if (*cptr != '-') return 0;
  1347. sw = 0;
  1348. for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
  1349.     if (isalpha (*cptr) == 0) return -1;
  1350.     sw = sw | SWMASK (*cptr);  }
  1351. return sw;
  1352. }
  1353.  
  1354. /* General radix printing routine
  1355.  
  1356.    Inputs:
  1357.     stream    =    stream designator
  1358.     val    =    value to print
  1359.     radix    =    radix to print
  1360.     width    =    width to print
  1361.     format    =    leading zeroes format
  1362.    Outputs:
  1363.     status    =    error status
  1364. */
  1365.  
  1366. int fprint_val (FILE *stream, unsigned int val, int radix,
  1367.     int width, int format)
  1368. {
  1369. #define MAX_WIDTH (CHAR_BIT * sizeof (unsigned int))
  1370. unsigned int mask, digit;
  1371. int d, ndigits;
  1372. double fptest;
  1373. char dbuf[MAX_WIDTH + 1];
  1374.  
  1375. for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' ';
  1376. dbuf[MAX_WIDTH] = 0;
  1377. d = MAX_WIDTH;
  1378. do {    d = d - 1;
  1379.     digit = val % (unsigned) radix;
  1380.     val = val / (unsigned) radix;
  1381.     dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10);
  1382.    } while ((d > 0) && (val != 0));
  1383.  
  1384. if (format != PV_LEFT) {
  1385.     mask = (1u << width) - 1;
  1386.     fptest = (double) radix;
  1387.     ndigits = 1;
  1388.     while (fptest < (double) mask) {
  1389.         fptest = fptest * (double) radix;
  1390.         ndigits = ndigits + 1; }
  1391.     if (MAX_WIDTH - ndigits < d) d = MAX_WIDTH - ndigits;  }
  1392. if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR;
  1393. return SCPE_OK;
  1394. }
  1395.  
  1396. /* Event queue routines
  1397.  
  1398.     sim_activate        add entry to event queue
  1399.     sim_cancel        remove entry from event queue
  1400.     sim_process_event    process entries on event queue
  1401.     sim_is_active        see if entry is on event queue
  1402.     sim_atime        return absolute time for an entry
  1403.     sim_gtime        return global time
  1404.  
  1405.    Asynchronous events are set up by queueing a unit data structure
  1406.    to the event queue with a timeout (in simulator units, relative
  1407.    to the current time).  Each simulator 'times' these events by
  1408.    counting down interval counter sim_interval.  When this reaches
  1409.    zero the simulator calls sim_process_event to process the event
  1410.    and to see if further events need to be processed, or sim_interval
  1411.    reset to count the next one.
  1412.  
  1413.    The event queue is maintained in clock order; entry timeouts are
  1414.    RELATIVE to the time in the previous entry.
  1415.  
  1416.    Sim_process_event - process event
  1417.  
  1418.    Inputs:
  1419.     none
  1420.    Outputs:
  1421.     reason        reason code returned by any event processor,
  1422.             or 0 (SCPE_OK) if no exceptions
  1423. */
  1424.  
  1425. int sim_process_event (void)
  1426. {
  1427. UNIT *uptr;
  1428. int reason;
  1429.  
  1430. if (stop_cpu) return SCPE_STOP;                /* stop CPU? */
  1431. if (sim_clock_queue == NULL) {                /* queue empty? */
  1432.     UPDATE_SIM_TIME (noqueue_time);            /* update sim time */
  1433.     sim_interval = noqueue_time = NOQUEUE_WAIT;    /* flag queue empty */
  1434.     return SCPE_OK;  }
  1435. UPDATE_SIM_TIME (sim_clock_queue -> time);        /* update sim time */
  1436. do {    uptr = sim_clock_queue;                /* get first */
  1437.     sim_clock_queue = uptr -> next;            /* remove first */
  1438.     uptr -> next = NULL;                /* hygiene */
  1439.     uptr -> time = 0;
  1440.     if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time;
  1441.     else sim_interval = noqueue_time = NOQUEUE_WAIT;
  1442.     if (uptr -> action != NULL) reason = uptr -> action (uptr);
  1443.     else reason = SCPE_OK;
  1444.    } while ((reason == SCPE_OK) && (sim_interval == 0));
  1445.  
  1446. /* Empty queue forces sim_interval != 0 */
  1447.  
  1448. return reason;
  1449. }                            /* end sim_process */
  1450.  
  1451. /* Activate (queue) event
  1452.  
  1453.    Inputs:
  1454.     uptr    =    pointer to unit
  1455.     event_time =    relative timeout
  1456.    Outputs:
  1457.     reason    =    result (SCPE_OK if ok)
  1458. */
  1459.  
  1460. int sim_activate (UNIT *uptr, int event_time)
  1461. {
  1462. UNIT *cptr, *prvptr;
  1463. int accum;
  1464.  
  1465. if (event_time < 0) return SCPE_ARG;
  1466. if (sim_is_active (uptr)) return SCPE_OK;        /* already active? */
  1467. if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time);  }
  1468. else  {    UPDATE_SIM_TIME (sim_clock_queue -> time);  }    /* update sim time */
  1469.  
  1470. prvptr = NULL;
  1471. accum = 0;
  1472. for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
  1473.     if (event_time < accum + cptr -> time) break;
  1474.     accum = accum + cptr -> time;
  1475.     prvptr = cptr;  }
  1476. if (prvptr == NULL) {                    /* insert at head */
  1477.     cptr = uptr -> next = sim_clock_queue;
  1478.     sim_clock_queue = uptr;  }
  1479. else {    cptr = uptr -> next = prvptr -> next;        /* insert at prvptr */
  1480.     prvptr -> next = uptr;  }
  1481. uptr -> time = event_time - accum;
  1482. if (cptr != NULL) cptr -> time = cptr -> time - uptr -> time;
  1483. sim_interval = sim_clock_queue -> time;
  1484. return SCPE_OK;
  1485. }
  1486.  
  1487. /* Cancel (dequeue) event
  1488.  
  1489.    Inputs:
  1490.     uptr    =    pointer to unit
  1491.    Outputs:
  1492.     reason    =    result (SCPE_OK if ok)
  1493.  
  1494. */
  1495.  
  1496. int sim_cancel (UNIT *uptr)
  1497. {
  1498. UNIT *cptr, *nptr;
  1499.  
  1500. if (sim_clock_queue == NULL) return SCPE_OK;
  1501. UPDATE_SIM_TIME (sim_clock_queue -> time);        /* update sim time */
  1502. nptr = NULL;
  1503. if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr -> next;
  1504. else {    for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
  1505.         if (cptr -> next == uptr) {
  1506.             nptr = cptr -> next = uptr -> next;
  1507.             break;  }  }  }            /* end queue scan */
  1508. if (nptr != NULL) nptr -> time = nptr -> time + uptr -> time;
  1509. uptr -> next = NULL;                    /* hygiene */
  1510. uptr -> time = 0;
  1511. if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time;
  1512. else sim_interval = noqueue_time = NOQUEUE_WAIT;
  1513. return SCPE_OK;
  1514. }
  1515.  
  1516. /* Test for entry in queue, return activation time
  1517.  
  1518.    Inputs:
  1519.     uptr    =    pointer to unit
  1520.    Outputs:
  1521.     result =    absolute activation time + 1, 0 if inactive
  1522. */
  1523.  
  1524. int sim_is_active (UNIT *uptr)
  1525. {
  1526. UNIT *cptr;
  1527. int accum;
  1528.  
  1529. accum = 0;
  1530. for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
  1531.     accum = accum + cptr -> time;
  1532.     if (cptr == uptr) return accum + 1;  }
  1533. return 0;
  1534. }
  1535.  
  1536. /* Return global time
  1537.  
  1538.    Inputs: none
  1539.    Outputs:
  1540.     time    =    global time
  1541. */
  1542.  
  1543. double sim_gtime (void)
  1544. {
  1545. if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time);  }
  1546. else  {    UPDATE_SIM_TIME (sim_clock_queue -> time);  }
  1547. return sim_time;
  1548. }
  1549.