(*----------------------------------------------------------------------*)

IMPLEMENTATION MODULE Fast; (* $T-, $R- module is self checking *)

(* This module implements some fast operations in machine code.
   These actual implementaion procedures are in the assembler file mod.FastAsm
   This file just describes the equivalent modula-2 algorithm.
*)
(*----------------------------------------------------------------------*)

FROM Universe IMPORT WindowI, EndOfLineCh, BYTE, BYTESET;
FROM Universe IMPORT Debugging, DebugFlags;
FROM Memory IMPORT DynamicArray;
FROM Buffers IMPORT AreaR, BufferP;
FROM Errors IMPORT Panic;
IMPORT Sets;

IMPORT Debug;

(*----------------------------------------------------------------------*)

VAR
   boundChecking		: BOOLEAN ;
   DisplayableCharactersMap	: ARRAY [ 0..255 ] OF BYTE ;

(*----------------------------------------------------------------------*)

PROCEDURE SkipUntilEndOfLineCh( Array : DynamicArray ;
			    VAR Area : AreaR ) : BOOLEAN ;

   (* Moves Area.Start forward until an EndOfLineCh found or Area.End reached *)

   BEGIN
      IF boundChecking THEN
         IF Area.End > Array.Size THEN
	    Panic( "Fast.SkipUntilEndOfLineCh : Dynamic array index too big" ) ;
         END (* if *) ;
      END (* if *) ;

      WHILE Area.Start < Area.End DO
         IF Array.Data^[ Area.Start ] = EndOfLineCh THEN RETURN TRUE END;
         INC( Area.Start ) ;
      END (* while *) ;

      RETURN FALSE ;
   END SkipUntilEndOfLineCh ;

(*----------------------------------------------------------------------*)

PROCEDURE BackUntilEndOfLineCh( Array : DynamicArray ;
			    VAR Area : AreaR ) : BOOLEAN ;

   (* Moves Area.End backward until an EndOfLineCh found or Area.Start reached *)

   BEGIN
      IF boundChecking THEN
         IF Area.End > Array.Size THEN
	    Panic( "Fast.BackUntilEndOfLineCh : Dynamic array index too big" ) ;
         END (* if *) ;
      END (* if *) ;

      WHILE Area.End > Area.Start DO
         IF Array.Data^[ Area.End - 1 ] = EndOfLineCh THEN RETURN TRUE END;
         DEC( Area.End ) ;
      END (* while *) ;

      RETURN FALSE ;
   END BackUntilEndOfLineCh ;

(*----------------------------------------------------------------------*)

PROCEDURE Move( Array : DynamicArray ; Length , From , To : CARDINAL ) ;

   (* Moves a data block within an array, takes care of overlap *)

   VAR I: CARDINAL ;

   BEGIN
      IF boundChecking THEN
         IF ((From + Length) > Array.Size) OR ((To + Length) > Array.Size) THEN
  	    Panic ("Fast.Move: bound check fails");
         END (* if *) ;
      END (* if *) ;

      IF From > To THEN
         FOR I := From TO From + Length - 1 DO
	    Array.Data^[ To ] := Array.Data^[ I ] ;
 	    INC( To ) ;
         END (* for *) ;
      ELSE
         INC( To , Length ) ;
         FOR I := From + Length - 1 TO From BY -1 DO
  	    DEC( To ) ;
	    Array.Data^[ To ] := Array.Data^[ I ] ;
         END (* for *) ;
      END (* if *) ;
   END Move ;

(*----------------------------------------------------------------------*)

PROCEDURE Copy( Length : CARDINAL ;
		FromArray : DynamicArray ; FromOffset : CARDINAL ;
		ToArray : DynamicArray ; ToOffset : CARDINAL ) ;

   (* Moves a data block between two different arrays *)

   VAR I: CARDINAL;

   BEGIN
      IF boundChecking THEN
         IF ((FromOffset + Length) > FromArray.Size) OR ((ToOffset + Length) > ToArray.Size) THEN
	    Panic ("Fast.Copy: bound check fails");
         END (* if *) ;
      END (* if *) ;

      FOR I := FromOffset TO FromOffset + Length - 1  DO
         ToArray.Data^[ ToOffset ] := FromArray.Data^[ I ] ;
         INC( ToOffset ) ;
      END (* for *) ;
   END Copy ;

(*----------------------------------------------------------------------*)

PROCEDURE SetDisplayableCharacters( DisplayableCharacters : BYTESET ) ;
VAR
   Index		     : [ 0..255 ] ;
BEGIN
   FOR Index := 0 TO 255 DO
      IF Sets.In( Index , DisplayableCharacters ) THEN
	 DisplayableCharactersMap[ Index ] := BYTE( Index ) ;
      ELSE
	 DisplayableCharactersMap[ Index ] := BYTE( 1 ) ;
      END (* if *) ;
   END (* for *) ;
END SetDisplayableCharacters ;

(*----------------------------------------------------------------------*)

PROCEDURE WhereDifferent (VAR wantOffset  : CARDINAL;
	 	          VAR haveOffset  : CARDINAL ): WhereDifferentDescription;

   VAR
      ch	 : BYTE;
      haveIndex	 : CARDINAL;
      wantIndex	 : CARDINAL;

   BEGIN
      IF boundChecking THEN

         (* Note: We allow Offsets to be greater then the Array.Size as a
         special case. The Array element is never accessed and
         xxxxRanOutOfChars is returned as the result *)

         IF (cache.wantLimit > cache.wantArray.Size) OR (cache.haveLast >= cache.haveArray.Size) THEN
	    Panic ("Fast.WhereDifferent: Dynamic array index too big")
         END (* if *);

      END (* if *);

      haveIndex := haveOffset;
      wantIndex := wantOffset;
      WHILE haveIndex <= cache.haveLast DO
         IF wantIndex < cache.wantLimit THEN
	    ch := cache.wantArray.Data^ [wantIndex];
	    IF DisplayableCharactersMap [CARDINAL (ch)] # cache.haveArray.Data^ [haveIndex] THEN
	       IF ch = EndOfLineCh THEN
	          haveOffset := haveIndex;
	          wantOffset := wantIndex;
	          RETURN EndOfLineHere
	       ELSE
	          IF CARDINAL (cache.havePriorities.Data^ [haveIndex]) # cache.wantPriority THEN
		     (* Ignore this character position *)
	          ELSE
		     haveOffset := haveIndex;
		     wantOffset := wantIndex;
		     RETURN DifferentHere
	          END (* if *)
	       END (* if *)
	    END (* if *);
	    INC (wantIndex);
	    INC (haveIndex)
         ELSE
	    haveOffset := haveIndex;
	    wantOffset := wantIndex;
	    RETURN WantRanOutOfChars
         END (* if *)
      END (* while *);
      haveOffset := cache.haveLast;
      wantOffset := wantIndex;
      RETURN HaveRanOutOfChars
   END WhereDifferent;

(*----------------------------------------------------------------------*)

PROCEDURE WhereNotBlank( HaveArray : DynamicArray ;
		     VAR HaveOffset : CARDINAL ;
			 HaveLast : CARDINAL ;
			 Priorities : DynamicArray ;
			 ThisWindowPriority : CARDINAL
		       ) : WhereDifferentDescription ;
(* Used by the display manager *)
VAR
   HaveIndex		   : CARDINAL ;
BEGIN
   IF boundChecking THEN
      (* Note : We allow Offsets to be greater then the Array.Size as a special
		case. The Array element is never accessed and xxxxRanOutOfChars
		is returned as the result.
      *)
      IF HaveLast >= HaveArray.Size THEN
	 Panic( "Fast.WhereDifferent : Dynamic array index too big" ) ;
      END (* if *) ;
   END (* if *) ;
   HaveIndex := HaveOffset ;
   WHILE HaveIndex <= HaveLast DO
      IF CARDINAL( Priorities.Data^[ HaveIndex ] ) <> ThisWindowPriority THEN
	 HaveOffset := HaveIndex ;
	 RETURN DifferentWindowHere ;
      END (* if *) ;
      IF HaveArray.Data^[ HaveIndex ] <> BYTE( ' ' ) THEN
	 HaveOffset := HaveIndex ;
	 RETURN DifferentHere ;
      END (* if *) ;
      INC( HaveIndex ) ;
   END (* while *) ;
   HaveOffset := HaveLast ;
   RETURN HaveRanOutOfChars ;
END WhereNotBlank ;

(*----------------------------------------------------------------------*)

PROCEDURE WhereDifferentWindow( VAR Offset : CARDINAL ;
				Last : CARDINAL ;
				Priorities : DynamicArray ;
				ThisWindowPriority : CARDINAL
			      ) : WhereDifferentDescription ;
(* Used by the display manager *)
VAR
   Index	       : CARDINAL ;
BEGIN
   IF boundChecking THEN
      IF Last >= Priorities.Size THEN
	 Panic( "Fast.WhereDifferentWindow : Dynamic array index too big" ) ;
      END (* if *) ;
   END (* if *) ;
   Index := Offset ;
   WHILE Index <= Last DO
      IF CARDINAL( Priorities.Data^[ Index ] ) <> ThisWindowPriority THEN
	 Offset := Index ;
	 RETURN DifferentWindowHere ;
      END (* if *) ;
      INC( Index ) ;
   END (* while *) ;
   Offset := Last ;
   RETURN HaveRanOutOfChars ;
END WhereDifferentWindow ;

(*----------------------------------------------------------------------*)

PROCEDURE Initialise;

   BEGIN
      boundChecking := TRUE
   END Initialise;

(*----------------------------------------------------------------------*)

PROCEDURE Terminate;

   BEGIN
   END Terminate;

(*----------------------------------------------------------------------*)

BEGIN
END Fast.

(*----------------------------------------------------------------------*)

