home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / 1990 / 12 / praxis / grafik.mod < prev    next >
Encoding:
Modula Implementation  |  1990-08-15  |  15.5 KB  |  623 lines

  1. (* ------------------------------------------------------ *)
  2. (*                    GRAFIK.MOD                          *)
  3. (*           Grafik-Modul für Fitted-Modula               *)
  4. (* ------------------------------------------------------ *)
  5. IMPLEMENTATION MODULE Grafik;
  6.  
  7. (* $T-, $S-, $R- *)
  8.  
  9. FROM SYSTEM IMPORT
  10.   ADDRESS, BYTE, ADR, SEG, OFS, ASSEMBLER;
  11.  
  12. FROM System IMPORT
  13.   TermProcedure, Move;
  14.  
  15. FROM Strings IMPORT
  16.   Assign, (* Copy, *)  Length;
  17.  
  18. FROM Storage IMPORT
  19.   Available, ALLOCATE, DEALLOCATE;
  20.  
  21. CONST
  22.   MaxGrafWindow      = 10;
  23.   GrafikInterrupt    = 60H;
  24.   LF                 = 0AX;
  25.   CR                 = 0DX;
  26.                     (* Erkennungs-Code für Grafik-Befehle *)
  27.   PutPixelCode       = 1;
  28.   GetPixelCode       = 2;
  29.   LineCode           = 3;
  30.   CircleCode         = 4;
  31.   ArcCode            = 5;
  32.   SetColorCode       = 10;
  33.   SetGraphModeCode   = 11;
  34.   RestoreCrtModeCode = 12;
  35.   SetViewPortCode    = 13;
  36.   ClearViewPortCode  = 14;
  37.   OutTextXYCode      = 15;
  38.   ImageSizeCode      = 16;
  39.   GetImageCode       = 17;
  40.   PutImageCode       = 18;
  41.   InstallCode        = 19;
  42.  
  43. TYPE
  44.   Str80 = ARRAY[0..80] OF CHAR;
  45.   GrafWindowType  = RECORD              (* Grafik-Fenster *)
  46.                       x1, y1, x2, y2 : INTEGER;
  47.                       Buffer         : ADDRESS;
  48.                     END;
  49.   ParameterRecord = RECORD    (* Tabelle für Übergabe     *)
  50.                               (* aller nötigen Parameter  *)
  51.                               (* an UNIGRAF               *)
  52.                       BefehlsCode,
  53.                       x1, y1, x2, y2,
  54.                       w, Farbe        : INTEGER;
  55.                       Zeiger          : ADDRESS;
  56.                       Clipping        : BOOLEAN;
  57.                       Text            : Str80;
  58.                     END;
  59. VAR
  60.   AktivesFenster   : INTEGER;
  61.   GrafWindow       : ARRAY [1..MaxGrafWindow] OF
  62.                                              GrafWindowType;
  63.   FontDeltaX,
  64.   FontDeltaY       : INTEGER;
  65.   Parameter        : ParameterRecord;
  66.   ParameterPointer : ADDRESS;
  67.   ParameterSeg,
  68.   ParameterOfs     : CARDINAL;
  69.   StiftFarbe       : Colour;
  70.   GrafikInstalled  : BOOLEAN;
  71.   CA : ColArray;
  72.   CI : Colour;
  73.  
  74.   PROCEDURE UniGraf;
  75.   BEGIN
  76.     (* Zeiger auf Parameter-Tabelle in Register schreiben
  77.        und Interrupt an UNIGRAF ausführen                 *)
  78.     ASM
  79.       MOV   AX, ParameterSeg
  80.       MOV   BX, ParameterOfs
  81.       INT   GrafikInterrupt
  82.     END;
  83.   END UniGraf;
  84.  
  85.   (* für die folgenden Befehle wurde die Turbo-Pascal-
  86.      Bezeichnung beibehalten                              *)
  87.  
  88.   PROCEDURE PutPixel(x, y : INTEGER; Color : Colour);
  89.   BEGIN
  90.     (* Parameter in Parameter-Tabelle übertragen und
  91.        Ausführung starten                                 *)
  92.     WITH Parameter DO
  93.       BefehlsCode := PutPixelCode;
  94.       x1 := x;
  95.       y1 := y;
  96.       Farbe := ORD(Color);
  97.     END;
  98.     UniGraf;
  99.   END PutPixel;
  100.  
  101.   PROCEDURE GetPixel(x, y : INTEGER) : Colour;
  102.   VAR
  103.     c : Colour;
  104.   BEGIN
  105.     WITH Parameter DO
  106.       BefehlsCode := GetPixelCode;
  107.       x1 := x;
  108.       y1 := y;
  109.     END;
  110.     UniGraf;
  111.     c := CA[Parameter.Farbe];
  112.     RETURN c;           (* VAL(Colour, Parameter.Farbe); *)
  113.   END GetPixel;
  114.  
  115.   PROCEDURE DrawLine(x1, y1, x2, y2 : INTEGER);
  116.   BEGIN
  117.     Parameter.BefehlsCode := LineCode;
  118.     Parameter.x1 := x1;
  119.     Parameter.y1 := y1;
  120.     Parameter.x2 := x2;
  121.     Parameter.y2 := y2;
  122.     UniGraf;
  123.   END DrawLine;
  124.  
  125.   PROCEDURE TCircle(x, y, Radius : INTEGER);  (* Circle() *)
  126.   BEGIN
  127.     WITH Parameter DO
  128.       BefehlsCode := CircleCode;
  129.       x1 := x;
  130.       y1 := y;
  131.       x2 := Radius;
  132.     END;
  133.     UniGraf;
  134.   END TCircle;
  135.  
  136.   PROCEDURE TArc(x, y, StartWinkel, EndWinkel,
  137.                  Radius : INTEGER);              (* Arc() *)
  138.   BEGIN
  139.     WITH Parameter DO
  140.       BefehlsCode := ArcCode;
  141.       x1 := x;
  142.       y1 := y;
  143.       x2 := StartWinkel;
  144.       y2 := EndWinkel;
  145.       w  := Radius;
  146.     END;
  147.     UniGraf;
  148.   END TArc;
  149.  
  150.   PROCEDURE SetColor(Color : Colour);
  151.   BEGIN
  152.     WITH Parameter DO
  153.       BefehlsCode := SetColorCode;
  154.       Farbe := ORD(Color);
  155.     END;
  156.     StiftFarbe := Color;
  157.     UniGraf;
  158.   END SetColor;
  159.  
  160.   PROCEDURE SetViewPort(x1, y1, x2, y2 : INTEGER;
  161.                                             Clip : BOOLEAN);
  162.   BEGIN
  163.     Parameter.BefehlsCode := SetViewPortCode;
  164.     Parameter.x1 := x1;
  165.     Parameter.y1 := y1;
  166.     Parameter.x2 := x2;
  167.     Parameter.y2 := y2;
  168.     Parameter.Clipping := Clip;
  169.     UniGraf;
  170.   END SetViewPort;
  171.  
  172.   PROCEDURE ClearViewPort;
  173.   BEGIN
  174.     WITH Parameter DO
  175.       BefehlsCode := ClearViewPortCode;
  176.     END;
  177.     UniGraf;
  178.   END ClearViewPort;
  179.  
  180.   PROCEDURE OutTextXY(x, y : INTEGER; s : ARRAY OF CHAR);
  181.   BEGIN
  182.     WITH Parameter DO
  183.       BefehlsCode := OutTextXYCode;
  184.       x1 := x;
  185.       y1 := y;
  186.       Assign(s, Text);
  187.     END;
  188.     UniGraf;
  189.   END OutTextXY;
  190.  
  191.   PROCEDURE ImageSize(x1, y1, x2, y2 : INTEGER) : CARDINAL;
  192.   BEGIN
  193.     Parameter.BefehlsCode := ImageSizeCode;
  194.     Parameter.x1 := x1;
  195.     Parameter.y1 := y1;
  196.     Parameter.x2 := x2;
  197.     Parameter.y2 := y2;
  198.     UniGraf;
  199.     RETURN Parameter.x1;
  200.   END ImageSize;
  201.  
  202.   PROCEDURE GetImage(x1, y1, x2, y2 : INTEGER; p : ADDRESS);
  203.   BEGIN
  204.     Parameter.BefehlsCode := GetImageCode;
  205.     Parameter.x1 := x1;
  206.     Parameter.y1 := y1;
  207.     Parameter.x2 := x2;
  208.     Parameter.y2 := y2;
  209.     Parameter.Zeiger := p;
  210.     UniGraf;
  211.   END GetImage;
  212.  
  213.   PROCEDURE PutImage(x, y : INTEGER; p : ADDRESS);
  214.   BEGIN
  215.     WITH Parameter DO
  216.       BefehlsCode := PutImageCode;
  217.       x1 := x;
  218.       y1 := y;
  219.       Zeiger := p;
  220.     END;
  221.     UniGraf;
  222.   END PutImage;
  223.  
  224.   PROCEDURE InitGrafik(VAR xMax, yMax : INTEGER);
  225.   BEGIN
  226.     WITH Parameter DO
  227.       BefehlsCode := InstallCode;
  228.       UniGraf;
  229.       xMax := x1;
  230.       yMax := y1;
  231.     END;
  232.   END InitGrafik;
  233.  
  234.   PROCEDURE SetGraphMode;
  235.   BEGIN
  236.     WITH Parameter DO
  237.       BefehlsCode := SetGraphModeCode;
  238.     END;
  239.     UniGraf;
  240.   END SetGraphMode;
  241.  
  242.   PROCEDURE RestoreCrtMode;
  243.   BEGIN
  244.     WITH Parameter DO
  245.       BefehlsCode := RestoreCrtModeCode;
  246.     END;
  247.     UniGraf;
  248.   END RestoreCrtMode;
  249.  
  250.   (* jetzt folgen die Grafik-Befehle für MODULA-2.        *)
  251.   (* UNIGRAF darf nicht im Textmodus mit einem Grafik-    *)
  252.   (* Befehl aufgerufen werden, da Turbo-Pascal sonst      *)
  253.   (* sofort mit einem Laufzeitfehler abbricht             *)
  254.  
  255.   PROCEDURE Plot(x, y: INTEGER);
  256.   BEGIN
  257.     IF GrafikModus THEN
  258.       PutPixel(x, y, StiftFarbe);
  259.     END;
  260.   END Plot;
  261.  
  262.   PROCEDURE Line(x1, y1, x2, y2: INTEGER);
  263.   BEGIN
  264.     IF GrafikModus THEN
  265.       DrawLine(x1, y1, x2, y2);
  266.     END;
  267.   END Line;
  268.  
  269.   PROCEDURE DotColor(x, y : INTEGER) : Colour;
  270.   BEGIN
  271.     IF GrafikModus THEN
  272.       RETURN GetPixel(x, y);
  273.     ELSE
  274.       RETURN Black;
  275.     END;
  276.   END DotColor;
  277.  
  278.   PROCEDURE Circle(x, y, Radius : INTEGER);
  279.   BEGIN
  280.     IF GrafikModus THEN
  281.       TCircle(x, y, Radius);
  282.     END;
  283.   END Circle;
  284.  
  285.   PROCEDURE Arc(x, y, StartWinkel, EndWinkel,
  286.                                    Radius : INTEGER);
  287.   BEGIN
  288.     IF GrafikModus THEN
  289.       TArc(x, y, StartWinkel, EndWinkel, Radius);
  290.     END;
  291.   END Arc;
  292.  
  293.   PROCEDURE PenColor(Farbe : Colour);
  294.   BEGIN
  295.     IF GrafikModus THEN
  296.       SetColor(Farbe);
  297.     END;
  298.   END PenColor;
  299.  
  300.   (* liegt der x-Wert innerhalb des Grafikschirms?        *)
  301.   (* wenn nicht, dann ändern                              *)
  302.  
  303.   PROCEDURE ZulaessigerXWert(x: INTEGER): INTEGER;
  304.   BEGIN
  305.     IF x < 0 THEN
  306.       x := 0;
  307.     END;
  308.     IF x > xMaxScr THEN
  309.       x := xMaxScr;
  310.     END;
  311.     RETURN x;
  312.   END ZulaessigerXWert;
  313.  
  314.   (* liegt der y-Wert innerhalb des Grafikschirms?        *)
  315.   (* wenn nicht, dann ändern                              *)
  316.  
  317.   PROCEDURE ZulaessigerYWert(y: INTEGER): INTEGER;
  318.   BEGIN
  319.     IF y < 0 THEN
  320.       y := 0;
  321.     END;
  322.     IF y > yMaxScr THEN
  323.       y := yMaxScr;
  324.     END;
  325.     RETURN y;
  326.   END ZulaessigerYWert;
  327.  
  328.   (* MODULA-2-String ins Turbo-Pascal-Format konvertieren *)
  329.  
  330.   PROCEDURE TurboString(s : ARRAY OF CHAR; VAR t : Str80);
  331.     VAR
  332.       l : CARDINAL;
  333.   BEGIN
  334.     l := Length(s);
  335.     IF l > 80 THEN
  336.       l := 80;
  337.     END;
  338.     Move(ADR(s[0]), ADR(t[1]), l);
  339.     t[0] := CHR(l);
  340.   END TurboString;
  341.  
  342.   PROCEDURE WriteText(x, y: INTEGER;
  343.                       s : ARRAY OF CHAR);
  344.     VAR
  345.       OldWindow, Laenge, x1, x2, y1, y2: INTEGER;
  346.       t : Str80;
  347.       i : INTEGER;
  348.   BEGIN
  349.     IF  NOT GrafikModus THEN
  350.       RETURN;
  351.     END;
  352.     OldWindow := GetGrafWindow();
  353.     IF INTEGER(Length(s)) > 80 - x THEN
  354. (*      Copy(s, 0, 80 - x, s);
  355.                           aus STRINGS.DEF, wenn vorhanden *)
  356.       FOR i := 0 TO 80 - x DO
  357.         s[i] := s[i];
  358.       END;
  359.     END;
  360.     TurboString(s, t);
  361.     Laenge := ORD(t[0]);    (* jetzt Turbo-Pascal-Format! *)
  362.     x := x - 1;
  363.     y := y - 1;
  364.     x1 := ZulaessigerXWert(x * FontX);
  365.     x2 := ZulaessigerXWert(x1 + Laenge * FontX);
  366.     y1 := ZulaessigerYWert(y * FontY);
  367.     y2 := ZulaessigerYWert(y1 + FontY - 1);
  368.                               (* Text-Hintergrund löschen *)
  369.     SetViewPort(x1, y1, x2, y2, TRUE);
  370.     ClearViewPort;
  371.     SetViewPort(0, 0, xMaxScr, yMaxScr, TRUE);
  372.                                (* Text zentriert ausgeben *)
  373.     OutTextXY(x1 + FontDeltaX, y1 + FontDeltaY, t);
  374.     SelectGrafWindow(OldWindow);
  375.   END WriteText;
  376.  
  377.   PROCEDURE WriteGrafText(x, y: INTEGER;
  378.                           Farbe : Colour;
  379.                           s: ARRAY OF CHAR);
  380.     VAR
  381.       AlteFarbe : Colour;
  382.       t : Str80;
  383.   BEGIN
  384.     IF  NOT GrafikModus THEN
  385.       RETURN;
  386.     END;
  387.     AlteFarbe := StiftFarbe;
  388.     TurboString(s, t);
  389.     SetColor(Farbe);
  390.     OutTextXY(x, y, t);
  391.     SetColor(AlteFarbe);
  392.   END WriteGrafText;
  393.  
  394.   PROCEDURE ClearGrafScreen;
  395.     VAR
  396.       OldWindow: INTEGER;
  397.   BEGIN
  398.     IF  NOT GrafikModus THEN
  399.       RETURN;
  400.     END;
  401.     OldWindow := GetGrafWindow();
  402.     SetViewPort(0, 0, xMaxScr, yMaxScr, TRUE);
  403.     ClearViewPort;
  404.     SelectGrafWindow(OldWindow);
  405.   END ClearGrafScreen;
  406.  
  407.   PROCEDURE ClearGrafWindow;
  408.   BEGIN
  409.     IF  NOT GrafikModus THEN
  410.       RETURN;
  411.     END;
  412.     ClearViewPort;
  413.   END ClearGrafWindow;
  414.  
  415.   PROCEDURE WindowSize(Nr: INTEGER): CARDINAL;
  416.   BEGIN
  417.     IF  NOT GrafikModus THEN
  418.       RETURN 0;
  419.     END;
  420.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  421.       RETURN 0;
  422.     ELSE
  423.       WITH GrafWindow[Nr] DO
  424.          RETURN ImageSize(x1, y1, x2, y2);
  425.       END;
  426.     END;
  427.   END WindowSize;
  428.  
  429.   PROCEDURE DefineGrafWindow(Nr, xl, yu, xr, yo: INTEGER);
  430.   BEGIN
  431.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  432.       RETURN;
  433.     END;
  434.     WITH GrafWindow[Nr] DO
  435.          (* für altes Fenster Speicherbereich freigeben,  *)
  436.          (* sofern benötigt                               *)
  437.       IF Buffer <> NIL THEN
  438.         DEALLOCATE(Buffer, WindowSize(Nr));
  439.       END;
  440.         (* nur Eck-Koordinaten innerhalb des Bildschirms  *)
  441.         (* zulassen                                       *)
  442.       x1 := ZulaessigerXWert(xl);
  443.       y1 := ZulaessigerYWert(yu);
  444.       x2 := ZulaessigerXWert(xr);
  445.       y2 := ZulaessigerYWert(yo);
  446.     END;
  447.   END DefineGrafWindow;
  448.  
  449.   PROCEDURE SelectGrafWindow(Nr: INTEGER);
  450.   BEGIN
  451.     IF  NOT GrafikModus THEN
  452.       RETURN;
  453.     END;
  454.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  455.       RETURN;
  456.     END;
  457.     AktivesFenster := Nr;
  458.     WITH GrafWindow[Nr] DO
  459.       SetViewPort(x1, y1, x2, y2, TRUE);
  460.     END;
  461.   END SelectGrafWindow;
  462.  
  463.   PROCEDURE FullGrafScreen;
  464.   BEGIN
  465.     SetViewPort(0, 0, xMaxScr, yMaxScr, TRUE);
  466.   END FullGrafScreen;
  467.  
  468.   PROCEDURE StoreGrafWindow(Nr: INTEGER);
  469.     VAR
  470.       OldWindow: INTEGER;
  471.   BEGIN
  472.     IF  NOT GrafikModus THEN
  473.       RETURN;
  474.     END;
  475.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  476.       RETURN;
  477.     END;
  478.     OldWindow := GetGrafWindow();
  479.     SelectGrafWindow(Nr);
  480.     WITH GrafWindow[Nr] DO
  481.       (* Speicher-Bereich anfordern, wenn nicht genügend  *)
  482.       (* zur Verfügung steht, nicht Speichern             *)
  483.       IF Buffer = NIL THEN
  484.         IF NOT Available(WindowSize(Nr)) THEN
  485.           RETURN
  486.         END;
  487.         ALLOCATE(Buffer, WindowSize(Nr));
  488.       END;
  489.       FullGrafScreen;
  490.       GetImage(x1, y1, x2, y2, Buffer);
  491.     END;
  492.     SelectGrafWindow(OldWindow);
  493.   END StoreGrafWindow;
  494.  
  495.   PROCEDURE RestoreGrafWindow(Nr: INTEGER);
  496.     VAR
  497.       OldWindow: INTEGER;
  498.   BEGIN
  499.     IF  NOT GrafikModus THEN
  500.       RETURN;
  501.     END;
  502.     OldWindow := GetGrafWindow();
  503.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  504.       RETURN;
  505.     END;
  506.     SelectGrafWindow(Nr);
  507.     FullGrafScreen;
  508.     WITH GrafWindow[Nr] DO
  509.       PutImage(x1, y1, Buffer);
  510.     END;
  511.     SelectGrafWindow(OldWindow);
  512.   END RestoreGrafWindow;
  513.  
  514.   PROCEDURE MoveGrafWindow(Nr, x, y: INTEGER);
  515.     VAR
  516.       OldWindow: INTEGER;
  517.   BEGIN
  518.     IF  NOT GrafikModus THEN
  519.       RETURN;
  520.     END;
  521.     OldWindow := GetGrafWindow();
  522.     IF (Nr < 1) OR (Nr > MaxGrafWindow) THEN
  523.       RETURN;
  524.     END;
  525.     FullGrafScreen;
  526.     WITH GrafWindow[Nr] DO
  527.       PutImage(x, y, Buffer);
  528.     END;
  529.     SelectGrafWindow(OldWindow);
  530.   END MoveGrafWindow;
  531.  
  532.   PROCEDURE GetGrafWindow(): CARDINAL;
  533.   BEGIN
  534.     RETURN AktivesFenster;
  535.   END GetGrafWindow;
  536.  
  537.   PROCEDURE InitGraphic;
  538.     VAR
  539.       n : CARDINAL;
  540.   BEGIN
  541.     IF GrafikInstalled THEN
  542.       RETURN;
  543.     END;
  544.     (* BGI-Treiber laden und Variablen initialisieren     *)
  545.     (* ACHTUNG: xMaxScr und yMaxScr sind erst NACH dem    *)
  546.     (* ersten Umschalten auf den Grafikschirm definiert!  *)
  547.     InitGrafik(xMaxScr, yMaxScr);
  548.     FontX := 8;
  549.     FontY := (yMaxScr + 1) DIV 25;
  550.     FontDeltaX := (FontX - 8) DIV 2;
  551.     FontDeltaY := (FontY - 8) DIV 2;
  552.                             (* alle Fenster vordefinieren *)
  553.     FOR n := 1 TO MaxGrafWindow DO
  554.       WITH GrafWindow[n] DO
  555.         Buffer := NIL;
  556.         DefineGrafWindow(n, 0, 0, xMaxScr, yMaxScr);
  557.       END;
  558.     END;
  559.     SelectGrafWindow(1);
  560.     GrafikInstalled := TRUE;
  561.   END InitGraphic;
  562.  
  563.   PROCEDURE GrafScreen;
  564.   BEGIN
  565.     IF GrafikModus THEN
  566.       RETURN;
  567.     END;
  568.     IF NOT GrafikInstalled THEN
  569.       InitGraphic;
  570.     END;
  571.     SetGraphMode;
  572.     GrafikModus := TRUE;
  573.   END GrafScreen;
  574.  
  575.   PROCEDURE TextScreen;
  576.   BEGIN
  577.     IF GrafikModus THEN
  578.       RestoreCrtMode;
  579.       GrafikModus := FALSE;
  580.     END;
  581.   END TextScreen;
  582.  
  583.   (* in den Textschirm zurückschalten und Bildspeicher    *)
  584.   (* wieder freigeben                                     *)
  585.  
  586.   PROCEDURE GrafExit;
  587.     VAR
  588.       n : CARDINAL;
  589.   BEGIN
  590.     TextScreen;
  591.     FOR n := 1 TO MaxGrafWindow DO
  592.       WITH GrafWindow[n] DO
  593.         IF Buffer # NIL THEN
  594.         DEALLOCATE(Buffer, WindowSize(n));
  595.         END;
  596.       END;
  597.     END;
  598.   END GrafExit;
  599.  
  600. BEGIN
  601.                                   (* Variablen vorbelegen *)
  602.   AktivesFenster   := 1;
  603.   GrafikInstalled  := FALSE;
  604.   GrafikModus      := FALSE;
  605.   StiftFarbe       := White;
  606.   ParameterPointer := ADR(Parameter);
  607.   ParameterSeg     := ParameterPointer.SEG;
  608.   ParameterOfs     := ParameterPointer.OFS;
  609.   CA[0]  := Black;         CA[1]  := Blue;
  610.   CA[2]  := Green;         CA[3]  := Cyan;
  611.   CA[4]  := Red;           CA[5]  := Magenta;
  612.   CA[6]  := Brown;         CA[7]  := LightGray;
  613.   CA[8]  := DarkGray;      CA[9]  := LightBlue;
  614.   CA[10] := LightGreen;    CA[11] := LightCyan;
  615.   CA[12] := LightRed;      CA[13] := LightMagenta;
  616.   CA[14] := Yellow;        CA[15] := White;
  617.   (* Programm-Abbruch muß in den Textschirm zurückführen  *)
  618.   TermProcedure(GrafExit);
  619. END Grafik.
  620. (* ------------------------------------------------------ *)
  621. (*                  Ende von GRAFIK.MOD                   *)
  622.  
  623.