MODULE sym$advised_move_bytes;
?? PUSH (LISTEXT := ON) ??
*copyc osd$default_pragmats
*copyc i#move
*copyc mmp$advise_in
*copyc mmp$advise_out
*copyc mmp$advise_out_in
*copyc mmp$get_page_size
?? POP ??

{ PURPOSE:
{   This procedure will move the specified number of bytes from the given
{   source to the given destination.  The pages are advised in before the
{   move and advised out afterwards.
{ DESIGN:
{   This procedure will page align the source with the first advise in.
{   The destination is page aligned on the first advise out.
{

  PROCEDURE [XDCL, #GATE] syp$advised_move_bytes (source: ^cell;
        destination: ^cell;
        length: integer;
    VAR status: ost$status);

    VAR
      advise_size: integer,
      current: integer,
      from_byte_offset: integer,
      from_ptr: ^cell,
      move: integer,
      next_from_ptr: ^cell,
      next_to_ptr: ^cell,
      page_size: integer,
      to_bytes_left: integer,
      to_byte_offset: integer,
      to_out_bytes: integer,
      to_out_ptr: ^cell,
      to_ptr: ^cell;

    status.normal := TRUE;
    from_ptr := source;
    to_ptr := destination;
    advise_size := 131072;
    mmp$get_page_size (page_size);

{ Page align source on first advise in.

    from_byte_offset := #OFFSET (from_ptr) MOD  page_size;
    move := page_size - from_byte_offset;
    IF move > length THEN
      move := length;
    IFEND;

{ Page align destination on first advise out.

    to_byte_offset := #OFFSET (to_ptr) MOD page_size;
    to_bytes_left := page_size - to_byte_offset;
    IF move > to_bytes_left THEN
      to_out_bytes := to_bytes_left;
      to_bytes_left := page_size - move + to_bytes_left;
    ELSEIF move < to_bytes_left THEN
      to_out_bytes := 0;
      to_bytes_left := to_bytes_left - move;
    ELSE
      to_out_bytes := move;
      to_bytes_left := 0;
    IFEND;
    to_out_ptr := to_ptr;

    mmp$advise_in (from_ptr, move, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    current := 0;
    WHILE current < length DO
      I#MOVE (from_ptr, to_ptr, move);
      next_from_ptr := #ADDRESS (#RING (from_ptr), #SEGMENT (from_ptr), #OFFSET
            (from_ptr) + move);
      next_to_ptr := #ADDRESS (#RING (to_ptr), #SEGMENT (to_ptr), #OFFSET
            (to_ptr) + move);
      current := current + move;

{ This condition will typically be true twice, once to set advise_size to the
{ remaining portion of the length and then once to set advise_size to zero.  The
{ exception will be when the final portion is full size.

      IF (current + advise_size) > length THEN
        advise_size := length - current;

{ Adjust the final byte counts for the destination.

        IF advise_size = 0 THEN
          to_out_bytes := #OFFSET (next_to_ptr) - #OFFSET (to_out_ptr);
        IFEND;

      IFEND;

      mmp$advise_out_in (from_ptr, move, next_from_ptr, advise_size, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      mmp$advise_out (to_out_ptr, to_out_bytes, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      from_ptr := next_from_ptr;
      to_ptr := next_to_ptr;
      to_out_ptr := #ADDRESS (#RING (to_ptr), #SEGMENT (to_ptr), #OFFSET
            (to_out_ptr) + to_out_bytes);
      to_out_bytes := advise_size;
      move := advise_size;

    WHILEND;

  PROCEND syp$advised_move_bytes;
MODEND sym$advised_move_bytes;
