package body Bit_Functions is
    --
    --  Implementation notes:
    --      this package uses integer arithmetic (mult by 2 and divide by 2)
    --      to accomplish most of the work involved.
    --
    --  The ideal implementation would be similar to the following:
    --
    --      OBJECT : INTEGER;
    --      type BIT_WORD is array (1..16) of BOOLEAN;
    --      pragma PACK (BIT_WORD)
    --      BIT_OBJECT : BIT_WORD;
    --      for BIT_OBJECT use at OBJECT'ADDRESS;
    --
    --      This effectively defined BIT_OBJECT as a bit array, physically
    --      located at the same memory location as OBJECT.  As a bit array,
    --      slices and boolean operations can be used!  Unfortunately,
    --      the DG/Rolm ADE software does not support the address rep spec.
    --
    --
    Word_Size : constant := 16; -- ASSUME 16 BIT WORDS!

    function Bit_Extract (Item, Start_At, Nbits : Integer) return Integer is
        Temp : Integer;
        Bit_Value : Integer;
        Result : Integer;
    begin
        Temp := Shift_Right (Item, Start_At);
        Bit_Value := (Temp mod 2 ** Nbits);

        if Bit_Value <= Integer'Last then
            Result := Bit_Value;
        else
            Result := Bit_Value - Integer'Last;
        end if;

        return Result;
    end Bit_Extract;

    function Ubit_Extract (Item, Start_At, Nbits : Integer) return Integer is
        Temp : Integer;
    begin
        Temp := Shift_Right (Item, Start_At);
        return Temp mod (2 ** Nbits);
    end Ubit_Extract;

    function Bit_Insert (This_Item, Nbits, Into_Item, Start_At : Integer)
                        return Integer is
        Item : Integer;
    begin
        Item := This_Item mod (2 ** Nbits); -- restrict value to size
        return Bit_Remove (Into_Item, Start_At, Nbits) +
                  Shift_Left (Item, Start_At);
    end Bit_Insert;

    function Bit_Remove (Item, Start_At, Nbits : Integer) return Integer is
        Keep : Integer := 0;
        Temp : Integer;
    begin
        if Start_At /= 0 then
            Keep := Item mod (2 ** Start_At);
        end if;

        Temp := Shift_Right (Item, Start_At + Nbits);
        return Shift_Left (Temp, Start_At + Nbits) + Keep;
    end Bit_Remove;

    function Shift_Left (Item, Nbits : Integer) return Integer is
    begin
        return Item * (2 ** Nbits);
    end Shift_Left;

    function Shift_Right (Item, Nbits : Integer) return Integer is
    begin
        return Item / (2 ** Nbits);
    end Shift_Right;

    function Bit_And (Word1, Word2 : Integer) return Integer is
        Spare1 : Integer := Word1;
        Spare2 : Integer := Word2;
        New_Word : Integer := 0;
        Bit1, Bit2, New_Bit : Integer;

    begin
        --
        --  the approach here to extract a single bit at a time from each
        --  word, and then decide upon the logical property.  The loop
        --  continues until all bits of the word have been considered,
        --  or until the words become zero in the shifting process.
        --

        for Index in 1 .. Word_Size loop
            exit when Spare1 = 0 and Spare2 = 0;
            Bit1 := Spare1 mod 2; -- get rightmost bit
            Bit2 := Spare2 mod 2;

            if Bit1 = 1 and Bit2 = 1 then
                New_Bit := 1;     -- decide upon new bit value
            else
                New_Bit := 0;
            end if;

            New_Word := New_Word + Shift_Left (New_Bit, Index - 1);
            Spare1 := Shift_Right (Spare1, 1);
            Spare2 := Shift_Right (Spare2, 1);
        end loop;

        return New_Word;
    end Bit_And;

    function Bit_Or (Word1, Word2 : Integer) return Integer is
        Spare1 : Integer := Word1;
        Spare2 : Integer := Word2;
        New_Word : Integer := 0;
        Bit1, Bit2, New_Bit : Integer;

    begin
        --  processing is identical to BIT_AND, except the logical test is changed
        for Index in 1 .. Word_Size loop
            exit when Spare1 = 0 and Spare2 = 0;
            Bit1 := Spare1 mod 2;
            Bit2 := Spare2 mod 2;

            if Bit1 = 1 or Bit2 = 1 then
                New_Bit := 1;
            else
                New_Bit := 0;
            end if;

            New_Word := Bit_Insert (New_Bit, 1, New_Word, Index - 1);
            Spare1 := Shift_Right (Spare1, 1);
            Spare2 := Shift_Right (Spare2, 1);
        end loop;

        return New_Word;
    end Bit_Or;

    function Bit_Mask (Nbits : Integer) return Integer is
        Result : Integer := 0;
    begin
        for Index in 1 .. Nbits loop
            Result := Result * 2 + 1;
        end loop;

        return Result;
    end Bit_Mask;

end Bit_Functions;
