home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / apps / astronmy / strchart.arc / patch.arc / starst.c < prev   
Encoding:
C/C++ Source or Header  |  1989-03-23  |  17.8 KB  |  578 lines

  1. /*
  2. ** Atari ST driver for starchart.
  3. ** Works on color systems only, compiled with Mark Williams C.
  4. ** Written by Dave Yearke (dgy@sigmast), September 1988.
  5. ** Portions of this program (c) Mark Williams Company.
  6. ** Thanks to the authors of the other starchart drivers for giving
  7. ** me some examples to work from.
  8. */
  9.  
  10. extern int exit_and_save;
  11. extern char *getenv(), picturefile[];
  12.  
  13. #include <ctype.h>    /* isprint(), iscntrl(), etc. */
  14. #include <linea.h>    /* Line A (low-level graphics) routines. */
  15. #include <osbind.h>   /* Operating system bindings */
  16. #include <vdibind.h>  /* The virtual device interface routines */
  17. #include <xbios.h>    /* Extended BIOS bindings */
  18. #include "starchrt.h" /* Starchart information */
  19.  
  20. /* Global line A variables used by vdi; MUST be included */
  21. int contrl[12], intin[128], ptsin[128], intout[128], ptsout[128];
  22. /* Array used by vs_clip() */
  23. int cliparray[] = { 1, 1, 319, 199 };
  24. /* Arrays used by v_opvwk() */
  25. int work_in[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2 };
  26. int work_out[57];
  27.  
  28. #define TRUE 1
  29. #define NULL 0
  30.  
  31. /*
  32. ** Starchart was designed for 1024x768 pixels.  These macros scale the
  33. ** image to the 320x200 Atari ST low-resolution (16-color) mode.  Also,
  34. ** the y coord is subtracted from 200 so the image isn't upside-down.
  35. */
  36. #define xadjust(x) ((x) * 0.3125)
  37. #define yadjust(y) (200 - ((y) * 0.2604167))
  38.  
  39. struct la_font *fontp; /* Line A font header. */
  40. char line[100], linemode, *p;
  41. char scr_wrk[1024];
  42. int vdihandle; /* Virtual device's handle */
  43.  
  44. int CURRX, CURRY, currez, old_color[16], oldrez = 99; /* 99 is impossible! */
  45.  
  46. /*
  47. ** Settings for the palette, given as RGB, 3 bits for each color (512 total).
  48. ** I tried to use different registers for different types of objects so they
  49. ** could be set individually (for example, in DEGAS you could blink a register
  50. ** for an object you're trying to point out).
  51. */
  52. int new_color[16] = { 0x000,   /* Black      - Background color */
  53.                       0x777,   /* White      - 1st and higher mag. stars */
  54.                       0x666,   /* White      - 2nd mag. stars */
  55.                       0x555,   /* White      - 3rd mag. stars */
  56.                       0x444,   /* White      - 4th mag. stars */
  57.                       0x333,   /* White      - 5th mag. stars */
  58.                       0x222,   /* White      - 6th and lower mag. stars */
  59.                       0x222,   /* White      - Nebulae, Galaxies, Clusters */
  60.                       0x007,   /* Blue       - 1st and higher mag. stars */
  61.                       0x700,   /* Red        - 1st and higher mag. stars */
  62.                       0x770,   /* Yellow     - Sol */
  63.                       0x704,   /* Pink       - Inferior Planets */
  64.                       0x404,   /* Purple     - Superior Planets */
  65.                       0x020,   /* Dark Green - Dotted lines */
  66.                       0x200,   /* Dark Red   - Hyphenated lines */
  67.                       0x003 }; /* Deep Blue  - Text and borders */
  68.  
  69. /*
  70. ** These are reverse-image colors for the background and stars, used when the
  71. ** 'r' key is hit when the program pauses before exiting.  Switching these
  72. ** registers makes a nice output image for a screen dump to a printer.
  73. */
  74. int rev_color[8]  = { 0x777,   /* White      - Background color */
  75.                       0x000,   /* Black      - 1st and higher mag. stars */
  76.                       0x222,   /* Black      - 2nd mag. stars */
  77.                       0x333,   /* Black      - 3rd mag. stars */
  78.                       0x444,   /* Black      - 4th mag. stars */
  79.                       0x555,   /* Black      - 5th mag. stars */
  80.                       0x666,   /* Black      - 6th and lower mag. stars */
  81.                       0x666 }; /* Black      - Nebulae, Galaxies, Clusters */
  82.  
  83. /*
  84. ** Chart parameters (limiting magnitude and window x,y,w,h)
  85. */
  86.  
  87. mapblock thumbnail =    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  88.             3.2, 1.0, 2.05, 420, 35, 480, 195, 0.0 };
  89.  
  90. mapblock master =    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  91.             8.0, 2.0, 2.05, 20, 265, 880, 500, 0.0 };
  92.  
  93. mapblock bigmaster =    { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  94.             8.0, 2.5, 2.5, 20,  65, 880, 700, 0.0 };
  95.  
  96. /*
  97. ** Generic functions
  98. */
  99.  
  100. /*
  101. ** Initialize the graphics mode
  102. */
  103. vecopen()
  104. {
  105.      int i;
  106.  
  107.      Cconws("\033H\033J"); /* Clear the screen */
  108.      if ((oldrez = Getrez()) == GR_HIGH) {
  109.           die("%s\n", "Sorry, this works on color systems only.");
  110.           Pterm(1);
  111.      } else if (oldrez == GR_MED)
  112.           Setscreen((char *)-1, (char *)-1, GR_LOW); /* Set low resolution */
  113.      currez = GR_LOW;
  114.      appl_init(); /* Initialize the application and register it with AES */
  115.      vdihandle = graf_handle(&i, &i, &i, &i); /* Get a VDI handle */
  116.      v_opnvwk(work_in, &vdihandle, work_out); /* Open the virtual workscreen */
  117.      vs_clip(vdihandle, 1, cliparray); /* Set the clipping rectangle */
  118.      for (i = 0; i < 16; i++) /* Save and set the palette */
  119.           old_color[i] = Setcolor(i, new_color[i]);
  120.  
  121.      linea0(); /* Initialize the line A routines */
  122.      lineaa(); /* Hide mouse pointer */
  123.      Cursconf(0, 0); /* Hide cursor */
  124.  
  125.      WMODE = 0; /* Writing mode = replace */
  126.      STYLE = 0; /* Normal Letters */
  127.      SRCY = 0; /* Y coord of character in font */
  128.      LITEMSK = 0x5555; /* Lightening and skewing masks for text */
  129.      SKEWMSK = 0x1111;
  130.      TEXTFG = 15; /* Text foreground dark blue */
  131.      SCRTCHP = scr_wrk; /* Scratch area for graphics */
  132.      LNMASK = -1; /* Solid lines */
  133.      WEIGHT = 1; /* Thickening factor for lines */
  134.      LSTLIN = -1;
  135. }
  136.  
  137. /*
  138. ** Clean up
  139. */
  140. vecclose ()
  141. {
  142.      register int i;
  143.  
  144.      if (exit_and_save)
  145.           savepic();
  146.      else
  147.           while (1) { /* Loop if 'r' is pressed, return for anything else */
  148.                switch (i = Crawcin()) { /* Wait for keypress before quitting */
  149.                case 'r': /* Reverse the color registers */
  150.                     if (Setcolor(0, -1) == 0) { /* If normal, reverse it */
  151.                          for (i = 0; i < 8; i++)
  152.                               Setcolor(i, rev_color[i]);
  153.                     } else { /* Make it normal */
  154.                          for (i = 0; i < 8; i++)
  155.                               Setcolor(i, new_color[i]);
  156.                     }
  157.                     continue; /* Restart the loop */
  158.                     break;
  159.                case 's': /* Save as a DEGAS picture */
  160.                     strcpy(picturefile, "star.pi1");
  161.                     savepic();
  162.                     break;
  163.                case 'S': /* Save as a NeoChrome picture */
  164.                     strcpy(picturefile, "star.neo");
  165.                     savepic();
  166.                     break;
  167.                default:
  168.                     break;
  169.                }
  170.                break;
  171.           }
  172.      vecreset();
  173. }
  174.  
  175. /*
  176. ** Restore the screen
  177. */
  178. vecreset()
  179. {
  180.      int i;
  181.  
  182.      v_clsvwk(vdihandle); /* Close the virtual workscreen */
  183.      appl_exit(); /* Remove the application from AES */
  184.      Cconws("\033H\033J"); /* Clear the screen */
  185.      Setscreen((char *)-1, (char *)-1, oldrez); /* Set resolution */
  186.      for (i = 0; i < 16; i++) /* Set the colors back to their saved values */
  187.           Setcolor(i, old_color[i]);
  188.      if (((getenv("PATH")) == 0) || (strlen(getenv("PATH")) == 0))
  189.           linea9(); /* Show mouse pointer */
  190.      else /* If called from a shell */
  191.           Cursconf(1, 0); /* Show cursor */
  192. }
  193.  
  194. /*
  195. ** Save the screen as a DEGAS or NeoChrome picture
  196. */
  197. savepic()
  198. {
  199.      int neo = 0, file; /* Assume DEGAS file */
  200.  
  201.      if (strrchr(picturefile, '.') != NULL) { /* Extender given */
  202.           if ((!strncmp(picturefile + strrchr(picturefile, '.'), ".neo", 4)) ||
  203.              (!strncmp(picturefile + strrchr(picturefile, '.'), ".NEO", 4)))
  204.                ++neo; /* Save as a NeoChrome picture */
  205.      } else
  206.           strcat(picturefile, ".pi1");
  207.      if ((file = creat(picturefile, 0)) == -1)
  208.           die("Cannot open %s.", picturefile);
  209.      if (neo) /* NeoChrome has two extra null bytes at the beginning */
  210.           if (write(file, &currez, 2) != 2)
  211.                die("Write failure on file %s.", picturefile);
  212.      if (write(file, &currez, 2) != 2) /* Write the resolution */
  213.           die("Write failure on file %s.", picturefile);
  214.      if (write(file, &new_color[0], 32) != 32) /* Write the palette info */
  215.           die("Write failure on file %s.", picturefile);
  216.      if (neo) /* NeoChrome has a 128-byte header, so we'll pad with 94 bytes */
  217.           for (neo = 0; neo < 47; ++neo) /* Recycle the variable "neo" */
  218.                if (write(file, &currez, 2) != 2)
  219.                     die("Write failure on file %s.", picturefile);
  220.      if (write(file, Physbase(), 32000) != 32000) /* Write the screen */
  221.           die("Write failure on file %s.", picturefile);
  222.      close(file); /* Done!  (that wasn't too bad, was it?) */
  223. }
  224.  
  225. /*
  226. ** This function is duplicated in starchrt.c for non-ST versions, using
  227. ** the INTERACTIVE manifest constant.
  228. */
  229. die(a,b)
  230. char *a, *b;
  231. {
  232.      char buf[80];
  233.      int i;
  234.  
  235.      if (oldrez != 99) /* See if we ever initialized the screen. */
  236.          vecreset(); /* Reset the screen to something sane */
  237.      if (!strncmp(b, "\nusage", 6)) { /* Sub in the ST's usage */
  238.           Cconws("\n\rusage:\tstar* [ Ra Dcl Scale Title Maglim Labellim ]\n");
  239.           Cconws("\ror\tstar* [ -r Ra -d Dcl -s Scale -t Title -m Maglim -l ");
  240.           Cconws("Labellim -f x.str ]\n\ror\tstar* [ -c con (3 or 4 letters ");
  241.           Cconws("chosen from con.loc) -l ... ]\n\ror\tstarst -x [ file.PI1 ");
  242.           Cconws("| file.NEO ] ... (to exit and save screen)\n\n\r");
  243.           Cconws("Portions of this program, copyright 1984, ");
  244.           Cconws("Mark Williams Company");
  245.      } else {
  246.           sprintf(buf, a, b);
  247.           Cconws(buf);
  248.      }
  249.      Cconws("\n\r"); /* Cconws does not map \n into \n\r */
  250.      /* We want to hold the screen if invoked from the GEM desktop;
  251.         the desktop doesn't usually set any environment variables,
  252.         and if getenv() does find anything, it's probably a NULL string. */
  253.      if (((getenv("PATH")) == 0) || (strlen(getenv("PATH")) == 0)) {
  254.           Cconws("press any key to continue:  "); /* Hold screen if desktop */
  255.           i = Crawcin(); /* Read raw character input */
  256.      }
  257.     exit(1);
  258. }
  259.  
  260. /*
  261. ** Functions for manipulating text.
  262. */
  263.  
  264. /*
  265. ** This function sets the point size for text. (not applicable)
  266. */
  267. vecsize (points)
  268. int points;
  269. {
  270. }
  271.  
  272. /*
  273. ** This function puts text on the screen.
  274. */
  275. vecsyms (x, y, s)
  276. int x,y;
  277. char *s;
  278. {
  279.      register char *ptr;
  280.      register unsigned int tmp;
  281.  
  282.      if (*s == '\0') /* Don't bother if the string is empty */
  283.           return;   /* (A null char can make linea8() crash!) */
  284.      fontp = la_init.li_a1[0];  /* 6x6 system font */
  285.      FBASE = fontp->font_data; /* Pointer to start of font form */
  286.      FWIDTH = fontp->font_width; /* Width of font form */
  287.      DELY = fontp->font_height; /* Height of character in font */
  288.  
  289.      DSTX = xadjust(x);
  290.      if ((DSTY = yadjust(y)) > 2)
  291.           DSTY -= 2; /* This vertically centers the text on the line */
  292.      if (DSTY > 191)
  293.           DSTY = 191; /* Make sure the bottom line shows up */
  294.      ptr = s;
  295.      do { /* Output the characters until the end of the string is hit */
  296.           if (!iscntrl(*ptr)) {
  297.                tmp = *ptr - fontp->font_low_ade; /* Find the character data */
  298.                SRCX = fontp->font_char_off[tmp]; /* within the font data */
  299.                DELX = fontp->font_char_off[tmp + 1] - SRCX;
  300.                /* Check screen boundaries */
  301.                if ((DSTX > 0) && (DSTX < 311) && (DSTY > 0))
  302.                     if (isspace(*ptr)) {
  303.                          WMODE = 1; /* Writing mode = transparent */
  304.                          linea8();
  305.                          WMODE = 0; /* Writing mode = replace */
  306.                     } else 
  307.                          linea8(); /* Line A trap for text blit routine */
  308.           }
  309.      } while (*++ptr != '\0');
  310. }
  311.  
  312. /*
  313. ** This function puts text on the screen using the greek alphabet.
  314. ** (Well, not yet, but someday ... :-))
  315. */
  316. vecsymsgk(s, x, y)
  317. char *s;
  318. {
  319.      vecsyms(s, x, y);
  320. }
  321.  
  322. /*
  323. ** Functions for drawing points and lines.
  324. */
  325.  
  326. /*
  327. ** Change the current position of x and y.
  328. */
  329. vecmove (x, y)
  330. int x,y;
  331. {
  332.      CURRX = x;
  333.      CURRY = y;
  334. }
  335.  
  336. /*
  337. ** Move to a new location (x1, y1) and draw a solid line to (x2, y2).
  338. */
  339. vecmovedraw (x1, y1, x2, y2)
  340. int x1, y1, x2, y2;
  341. {
  342.      vecmove(x1, y1);
  343.      vecdraw(x2, y2);
  344. }
  345.  
  346. /*
  347. ** Draw a solid line to (x,y)
  348. */
  349. vecdraw (x, y)
  350. int x,y;
  351. {
  352.      if (linemode != 'S') {
  353.           linemode = 'S';
  354.           COLBIT0 = 1; /* Set the bit plane for line drawing (color 15) */
  355.           COLBIT1 = 1;
  356.           COLBIT2 = 1;
  357.           COLBIT3 = 1;
  358.      }
  359.      X1 = xadjust(CURRX);
  360.      Y1 = yadjust(CURRY);
  361.      X2 = xadjust(CURRX = x);
  362.      Y2 = yadjust(CURRY = y);
  363.      linea3(); /* Line blit routine */
  364. }
  365.  
  366. /*
  367. ** Draw a dotted line (dark green on the ST) to (x,y)
  368. */
  369. vecdrawdot(x, y)
  370. {
  371.      if (linemode != 'D') {
  372.           linemode = 'D';
  373.           COLBIT0 = 1; /* Set the bit plane for line drawing (color 13) */
  374.           COLBIT1 = 0;
  375.           COLBIT2 = 1;
  376.           COLBIT3 = 1;
  377.      }
  378.      X1 = xadjust(CURRX);
  379.      Y1 = yadjust(CURRY);
  380.      X2 = xadjust(CURRX = x);
  381.      Y2 = yadjust(CURRY = y);
  382.      linea3();
  383. }
  384.  
  385. /*
  386. ** Draw a hyphenated line (dark red on the ST) to (x,y)
  387. */
  388. vecdrawhyph(x, y)
  389. {
  390.      if (linemode != 'H') {
  391.           linemode = 'H';
  392.           COLBIT0 = 0; /* Set the bit plane for line drawing (color 14) */
  393.           COLBIT1 = 1;
  394.           COLBIT2 = 1;
  395.           COLBIT3 = 1;
  396.      }
  397.      X1 = xadjust(CURRX);
  398.      Y1 = yadjust(CURRY);
  399.      X2 = xadjust(CURRX = x);
  400.      Y2 = yadjust(CURRY = y);
  401.      linea3();
  402. }
  403.  
  404. /*
  405. ** Draw a horizontal line.
  406. */
  407. drawlen (x, y, dx, dy, len)
  408. int x, y, dx, dy, len;
  409. {
  410.      if (linemode != 'S') {
  411.           linemode = 'S';
  412.           COLBIT0 = 1; /* Set the bit plane for line drawing (color 15) */
  413.           COLBIT1 = 1;
  414.           COLBIT2 = 1;
  415.           COLBIT3 = 1;
  416.      }
  417.      X1 = xadjust(x + dx);
  418.      Y1 = yadjust(y + dy);
  419.      X2 = xadjust(CURRX = x + dx + len - 1);
  420.      Y2 = yadjust(CURRY = y + dy);
  421.      linea3();
  422. }
  423.  
  424. /*
  425. ** Functions for astronomical objects.
  426. */
  427.  
  428. /*
  429. ** Draw the sun or a planet
  430. */
  431. drawPlan(x, y, mag, type, color)
  432. int x, y, mag, type;
  433. char *color;
  434. {
  435.      if (type == 'S')
  436.           INTIN[0] = 10; /* Yellow for the Sun */
  437.      else if ((type == 'M') || (type == 'V'))
  438.           INTIN[0] = 11; /* Pink for inferior planets (Mercury or Venus) */
  439.      else
  440.           INTIN[0] = 12; /* Purple for superior planets (m, J, s, U, or N) */
  441.      PTSIN[0] = xadjust(CURRX = x);
  442.      PTSIN[1] = yadjust(CURRY = y);
  443.      if (linea2() == 0) /* See if there's anything there already */
  444.           linea1(); /* Put a pixel on the screen */
  445. }
  446.  
  447. /*
  448. ** Draw a star.
  449. */
  450. drawStar(x, y, mag, type, color)
  451. int x, y, mag, type;
  452. char *color;
  453. {
  454.      if (mag > 6)
  455.           INTIN[0] = 6;
  456.      else if (mag < 1)
  457.           INTIN[0] = 1;
  458.      else
  459.           INTIN[0] = mag; /* No conversion necessary, direct map into palette */
  460.      if (mag < 2) /* Brightest stars will be in color if possible */
  461.           if (color != NULL)
  462.                if ((color[0] == 'O') || (color[0] == 'B'))
  463.                     INTIN[0] = 8; /* Color register 8 holds blue */
  464.                else if ((color[0] == 'K') || (color[0] == 'M'))
  465.                     INTIN[0] = 9; /* Color register 9 holds red */
  466.      PTSIN[0] = xadjust(CURRX = x);
  467.      PTSIN[1] = yadjust(CURRY = y);
  468.      if (mag < 1)
  469.           drawBigStar(x, y); /* Bright stars clobber anything under them */
  470.      else if (linea2() == 0) /* See if there's anything there already */
  471.                linea1();
  472. }
  473.  
  474. /*
  475. ** Draw a bright star as a cross shape.
  476. */
  477. drawBigStar(x, y)
  478. int x, y;
  479. {
  480.      linea1(); /* x, y */
  481.      --PTSIN[0];
  482.      linea1(); /* x-1, y */
  483.      ++PTSIN[0];
  484.      --PTSIN[1];
  485.      linea1(); /* x, y-1 */
  486.      PTSIN[1] += 2;
  487.      linea1(); /* x, y+1 */
  488.      ++PTSIN[0];
  489.      --PTSIN[1];
  490.      linea1(); /* x+1, y */
  491. }
  492.  
  493. /*
  494. ** Draw a nebula. ('type' = 'D' for diffuse, 'P' for planetary)
  495. */
  496. drawNebu(x, y, mag, type, color)
  497. int x, y, mag, type;
  498. char *color;
  499. {
  500.      /* This isn't as clever as cstar() in starimag.c, but it's fast */
  501.      INTIN[0] = 7; /* Make 'em faint, with this shape: */
  502.      PTSIN[0] = xadjust(CURRX = x) - 1; /*         **       */
  503.      PTSIN[1] = yadjust(CURRY = y);     /*        *  *      */
  504.      linea1(); /* x-1, y                           **       */
  505.      ++PTSIN[1];
  506.      linea1(); /* x-1, y+1 */
  507.      ++PTSIN[0];
  508.      ++PTSIN[1];
  509.      linea1(); /* x, y+2 */
  510.      PTSIN[1] -= 3;
  511.      linea1(); /* x, y-1 */
  512.      ++PTSIN[0];
  513.      ++PTSIN[1];
  514.      linea1(); /* x+1, y */
  515.      ++PTSIN[1];
  516.      linea1(); /* x+1, y+1 */
  517. }
  518.  
  519. /*
  520. ** Draw a galaxy. ('type' = 'P' for sphere, 'S' for spiral)
  521. */
  522. drawGalx(x, y, mag, type, color)
  523. int x, y, mag, type;
  524. char *color;
  525. {
  526.      int i;
  527.  
  528.      CURRX = x;
  529.      CURRY = y;
  530.      INTIN[0] = 7; /* Make 'em faint, with this shape: */
  531.      PTSIN[0] = xadjust(CURRX = x) - 2; /*        **  *   */
  532.      PTSIN[1] = yadjust(CURRY = y);     /*      ******    */
  533.      for (i = 0; i < 6; ++i) {          /*     *  **      */
  534.           PTSIN[0] += 1;
  535.           linea1(); /* (x-2)+i, y */
  536.      }
  537.      ++PTSIN[0];
  538.      --PTSIN[1];
  539.      linea1(); /* x+4, y-1 */
  540.      PTSIN[0] -= 3;
  541.      linea1(); /* x+1, y-1 */
  542.      --PTSIN[0];
  543.      linea1(); /* x, y-1 */
  544.      PTSIN[1] += 2;
  545.      linea1(); /* x, y+1 */
  546.      ++PTSIN[0];
  547.      linea1(); /* x+1, y+1 */
  548.      PTSIN[0] -= 4;
  549.      linea1(); /* x-3, y+1 */
  550. }
  551.  
  552. /*
  553. ** Draw a cluster. ('type' = 'G' for glob., 'O' for open, 'C' for Galactic)
  554. */
  555. drawClus(x, y, mag, type, color)
  556. int x, y, mag, type;
  557. char *color;
  558. {
  559.      INTIN[0] = 7; /* Make 'em faint, with this shape: */
  560.      PTSIN[0] = xadjust(CURRX = x);     /*         * *      */
  561.      PTSIN[1] = yadjust(CURRY = y);     /*        * * *     */
  562.      linea1(); /* x, y                             * *      */
  563.      PTSIN[0] -= 2;
  564.      linea1(); /* x-2, y */
  565.      ++PTSIN[0];
  566.      --PTSIN[1];
  567.      linea1(); /* x-1, y-1 */
  568.      PTSIN[0] += 2;
  569.      linea1(); /* x+1, y-1 */
  570.      PTSIN[1] += 2;
  571.      linea1(); /* x+1, y+1 */
  572.      PTSIN[0] -= 2;
  573.      linea1(); /* x-1, y+1 */
  574.      PTSIN[0] += 3;
  575.      --PTSIN[1];
  576.      linea1(); /* x+2, y */
  577. }
  578.