--  Debugger for VHDL simulation
--  Copyright (C) 2022 Tristan Gingold
--
--  This file is part of GHDL.
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation, either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program.  If not, see <gnu.org/licenses>.

with System;
with Ada.Unchecked_Conversion;

with GNAT.OS_Lib;

with Types; use Types;
with Name_Table; use Name_Table;
with Simple_IO; use Simple_IO;
with Utils_IO; use Utils_IO;

with Vhdl.Nodes; use Vhdl.Nodes;
with Vhdl.Utils; use Vhdl.Utils;
with Vhdl.Errors;

with Elab.Memtype;
with Elab.Vhdl_Objtypes; use Elab.Vhdl_Objtypes;
with Elab.Debugger; use Elab.Debugger;
with Elab.Vhdl_Debug; use Elab.Vhdl_Debug;
with Elab.Vhdl_Values; use Elab.Vhdl_Values;
with Elab.Vhdl_Values.Debug; use Elab.Vhdl_Values.Debug;
with Simul.Vhdl_Elab; use Simul.Vhdl_Elab;
with Simul.Vhdl_Simul;

with Grt.Types; use Grt.Types;
with Grt.Vhdl_Types; use Grt.Vhdl_Types;
with Grt.Options;
with Grt.Processes;
with Grt.Signals; use Grt.Signals;
with Grt.Disp_Signals;
with Grt.Rtis_Addr;

package body Simul.Vhdl_Debug is

   procedure Put_Time (Time : Std_Time)
   is
      use Grt.Options;
      Unit : Natural_Time_Scale;
      T : Std_Time;
   begin
      if Time = Std_Time'First then
         Put ("-Inf");
      else
         --  Do not bother with sec, min, and hr.
         Unit := Time_Resolution_Scale;
         T := Time;
         while Unit > 1 and then (T mod 1_000) = 0 loop
            T := T / 1000;
            Unit := Unit - 1;
         end loop;
         Put_Int64 (Int64 (T));
         case Unit is
            when 0 =>
               Put ("sec");
            when 1 =>
               Put ("ms");
            when 2 =>
               Put ("us");
            when 3 =>
               Put ("ns");
            when 4 =>
               Put ("ps");
            when 5 =>
               Put ("fs");
         end case;
      end if;
   end Put_Time;

   procedure Disp_Driver_Entry (D : Driver_Entry) is
   begin
      Put (" [");
      Put_Uns32 (Uns32 (D.Proc));
      Put ("] ");
      Disp_Instance_Path (Processes_Table.Table (D.Proc).Inst);
      New_Line;
      Put ("    noff: ");
      Put_Uns32 (D.Off.Net_Off);
      Put (", moff: ");
      Put_Uns32 (Uns32 (D.Off.Mem_Off));
      Put (", len: ");
      Put_Uns32 (D.Typ.W);
      Put (", typ: ");
      Debug_Type_Short (D.Typ);
      New_Line;
   end Disp_Driver_Entry;

   procedure Disp_Conn_Endpoint (Ep : Connect_Endpoint) is
   begin
      Put ("sig: ");
      Put_Uns32 (Uns32 (Ep.Base));
      Put (", noff: ");
      Put_Uns32 (Ep.Offs.Net_Off);
      Put (", moff: ");
      Put_Uns32 (Uns32 (Ep.Offs.Mem_Off));
      Put (", typ: ");
      Debug_Type_Short (Ep.Typ);
   end Disp_Conn_Endpoint;

   procedure Disp_Conn_Entry (Idx : Connect_Index_Type)
   is
      C : Connect_Entry renames Connect_Table.Table (Idx);
   begin
      Put ("    ");
      Put_Uns32 (Uns32 (Idx));
      Put (": ");
      if C.Collapsed then
         Put ("[collapsed]");
      end if;
      New_Line;
      Put ("     formal: ");
      Disp_Conn_Endpoint (C.Formal);
      if C.Drive_Formal then
         Put (" [drive]");
      end if;
      New_Line;
      Put ("     actual: ");
      Disp_Conn_Endpoint (C.Actual);
      if C.Drive_Actual then
         Put (" [drive]");
      end if;
      New_Line;
   end Disp_Conn_Entry;

   function Read_Value (Value_Ptr : Ghdl_Value_Ptr; Mode : Mode_Type)
     return Int64 is
   begin
      case Mode is
         when Mode_B1 =>
            return Ghdl_B1'Pos (Value_Ptr.B1);
         when Mode_E8 =>
            return Int64 (Value_Ptr.E8);
         when Mode_E32 =>
            return Int64 (Value_Ptr.E32);
         when Mode_I32 =>
            return Int64 (Value_Ptr.I32);
         when Mode_I64 =>
            return Int64 (Value_Ptr.I64);
         when Mode_F64 =>
            raise Internal_Error;
      end case;
   end Read_Value;

   function Read_Value (Value : Value_Union; Mode : Mode_Type)
     return Int64 is
   begin
      case Mode is
         when Mode_B1 =>
            return Ghdl_B1'Pos (Value.B1);
         when Mode_E8 =>
            return Int64 (Value.E8);
         when Mode_E32 =>
            return Int64 (Value.E32);
         when Mode_I32 =>
            return Int64 (Value.I32);
         when Mode_I64 =>
            return Int64 (Value.I64);
         when Mode_F64 =>
            raise Internal_Error;
      end case;
   end Read_Value;

   procedure Disp_Transaction (Trans : Transaction_Acc;
                               Sig_Type : Node;
                               Mode : Mode_Type)
   is
      T : Transaction_Acc;
   begin
      T := Trans;
      loop
         case T.Kind is
            when Trans_Value =>
               Disp_Discrete_Value (Read_Value (T.Val, Mode), Sig_Type);
            when Trans_Direct =>
               Disp_Discrete_Value (Read_Value (T.Val_Ptr, Mode), Sig_Type);
            when Trans_Null =>
               Put ("NULL");
            when Trans_Error =>
               Put ("ERROR");
         end case;
         if T.Kind = Trans_Direct then
            --  The Time field is not updated for direct transaction.
            Put ("[DIRECT]");
         else
            Put ("@");
            Put_Time (T.Time);
         end if;
         T := T.Next;
         exit when T = null;
         Put (", ");
      end loop;
   end Disp_Transaction;

   procedure Info_Scalar_Signal_Driver (S : Memtyp; Stype : Node)
   is
      function To_Address is new Ada.Unchecked_Conversion
        (Source => Resolved_Signal_Acc, Target => System.Address);
      Sig : Ghdl_Signal_Ptr;
   begin
      Sig := Simul.Vhdl_Simul.Read_Sig (S.Mem);
      Put_Addr (Sig.all'Address);
      Put (' ');
      Grt.Disp_Signals.Disp_Single_Signal_Attributes (Sig);
      Put (" val=");
      Disp_Discrete_Value (Read_Value (Sig.Value_Ptr, Sig.Mode), Stype);
      Put ("; drv=");
      Disp_Discrete_Value (Read_Value (Sig.Driving_Value, Sig.Mode), Stype);
      if Sig.Nbr_Ports > 0 then
         Put (';');
         Put_Int32 (Int32 (Sig.Nbr_Ports));
         Put (" ports");
      end if;
      case Sig.S.Mode_Sig is
         when Mode_Signal_User =>
            if Sig.S.Resolv /= null then
               Put (" resolver=");
               Put_Addr (To_Address (Sig.S.Resolv));
            end if;
            if Sig.S.Nbr_Drivers = 0 then
               Put ("; no driver");
            elsif Sig.S.Nbr_Drivers = 1 then
               Put ("; trans=");
               Disp_Transaction
                 (Sig.S.Drivers (0).First_Trans, Stype, Sig.Mode);
            else
               for I in 0 .. Sig.S.Nbr_Drivers - 1 loop
                  New_Line;
                  Put ("   ");
--                  Disp_Context
--                    (Processes.Get_Rti_Context (Sig.S.Drivers (I).Proc));
                  Put (": ");
                  Disp_Transaction
                    (Sig.S.Drivers (I).First_Trans, Stype, Sig.Mode);
               end loop;
            end if;

         when Mode_Delayed =>
            Put ("; trans=");
            Disp_Transaction (Sig.S.Attr_Trans, Stype, Sig.Mode);

         when others =>
            null;
      end case;
      New_Line;
   end Info_Scalar_Signal_Driver;

   generic
      with procedure For_Scalar_Signal (S : Memtyp; Stype : Node);
   procedure For_Each_Scalar_Signal (S : Memtyp; Stype : Node);

   procedure For_Each_Scalar_Signal (S : Memtyp; Stype : Node) is
   begin
      case S.Typ.Kind is
         when Type_Bit
           | Type_Logic
           | Type_Discrete =>
            For_Scalar_Signal (S, Get_Base_Type (Stype));
         when Type_Vector
           | Type_Array =>
            declare
               use Simul.Vhdl_Simul;
               Len : constant Uns32 := S.Typ.Abound.Len;
               El_Type : Node;
               Stride : Uns32;
            begin
               if S.Typ.Alast then
                  El_Type := Get_Element_Subtype (Stype);
               else
                  El_Type := Stype;
               end if;
               Stride := S.Typ.Arr_El.W;

               for I in 1 .. Len loop
                  For_Each_Scalar_Signal
                    ((S.Typ.Arr_El, Sig_Index (S.Mem, (Len - I) * Stride)),
                     El_Type);
               end loop;
            end;
         when Type_Record =>
            declare
               use Simul.Vhdl_Simul;
               El_List : constant Iir_Flist :=
                 Get_Elements_Declaration_List (Stype);
               El : Node;
            begin
               for I in S.Typ.Rec.E'Range loop
                  El := Get_Nth_Element (El_List, Natural (I - 1));
                  Put (Image (Get_Identifier (El)));
                  Put (": ");
                  For_Each_Scalar_Signal
                    ((S.Typ.Rec.E (I).Typ,
                      Sig_Index (S.Mem, S.Typ.Rec.E (I).Offs.Net_Off)),
                     Get_Type (El));
               end loop;
            end;
         when Type_Float
           | Type_Unbounded_Vector
           | Type_Unbounded_Record
           | Type_Unbounded_Array
           | Type_Slice
           | Type_Protected
           | Type_File
           | Type_Access =>
            raise Internal_Error;
      end case;
   end For_Each_Scalar_Signal;

   procedure Info_Signal_Driver is new For_Each_Scalar_Signal
     (Info_Scalar_Signal_Driver);

   procedure Info_Scalar_Signal_Action (S : Memtyp; Stype : Node)
   is
      pragma Unreferenced (Stype);
      use Grt.Rtis_Addr;
      use Grt.Processes;
      Sig : Ghdl_Signal_Ptr;
      Ev : Action_List_Acc;
      Ctxt : Rti_Context;
   begin
      Sig := Simul.Vhdl_Simul.Read_Sig (S.Mem);
      Put_Addr (Sig.all'Address);
      Put (' ');
      Ev := Sig.Event_List;
      while Ev /= null loop
         Ctxt := Get_Rti_Context (Ev.Proc);
         Put (' ');
         Put_Addr (Ctxt.Base);
         Ev := Ev.Next;
      end loop;
      New_Line;
   end Info_Scalar_Signal_Action;

   procedure Info_Signal_Action is new For_Each_Scalar_Signal
     (Info_Scalar_Signal_Action);

   type Info_Signal_Options is record
      Value : Boolean;
      Drivers : Boolean;
      Actions : Boolean;
   end record;

   procedure Info_Signal_Opts (Idx : Signal_Index_Type;
                               Opts : Info_Signal_Options)
   is
      use Elab.Memtype;
      S : Signal_Entry renames Signals_Table.Table (Idx);
      Nbr_Drv : Int32;
      Nbr_Conn_Drv : Int32;
      Nbr_Sens : Int32;
      Sens : Sensitivity_Index_Type;
      Driver : Driver_Index_Type;
      Conn : Connect_Index_Type;
   begin
      Put_Int32 (Int32 (Idx));
      Put (": ");
      if S.Decl = Null_Iir then
         Put_Line ("??");
         return;
      end if;

      Disp_Instance_Path (S.Inst, True);
      Put ('/');
      Put (Image (Get_Identifier (S.Decl)));

      case Get_Kind (S.Decl) is
         when Iir_Kind_Signal_Declaration =>
            Put (" [sig]");
         when Iir_Kind_Interface_Signal_Declaration =>
            case Get_Mode (S.Decl) is
               when Iir_In_Mode =>
                  Put (" [in]");
               when Iir_Out_Mode =>
                  Put (" [out]");
               when Iir_Buffer_Mode =>
                  Put (" [buffer]");
               when Iir_Linkage_Mode =>
                  Put (" [linkage]");
               when Iir_Inout_Mode =>
                  Put (" [inout]");
               when Iir_Unknown_Mode =>
                  Put (" [??]");
            end case;
         when others =>
            raise Internal_Error;
      end case;
      New_Line;

      Put ("  type: ");
      Debug_Type_Short (S.Typ);
      Put (", len: ");
      Put_Uns32 (S.Typ.W);
      New_Line;

      Nbr_Conn_Drv := 0;
      Conn := S.Connect;
      while Conn /= No_Connect_Index loop
         declare
            C : Connect_Entry renames Connect_Table.Table (Conn);
         begin
            if C.Formal.Base = Idx then
               if C.Drive_Formal then
                  Nbr_Conn_Drv := Nbr_Conn_Drv + 1;
               end if;
               Conn := C.Formal_Link;
            else
               pragma Assert (C.Actual.Base = Idx);
               if C.Drive_Actual then
                  Nbr_Conn_Drv := Nbr_Conn_Drv + 1;
               end if;
               Conn := C.Actual_Link;
            end if;
         end;
      end loop;

      Nbr_Drv := 0;
      Driver := S.Drivers;
      while Driver /= No_Driver_Index loop
         Nbr_Drv := Nbr_Drv + 1;
         Driver := Drivers_Table.Table (Driver).Prev_Sig;
      end loop;
      Put ("  nbr drivers: ");
      Put_Int32 (Nbr_Drv);
      Put (", nbr conn srcs: ");
      Put_Int32 (Nbr_Conn_Drv);

      Nbr_Sens := 0;
      Sens := S.Sensitivity;
      while Sens /= No_Sensitivity_Index loop
         Nbr_Sens := Nbr_Sens + 1;
         Sens := Sensitivity_Table.Table (Sens).Prev_Sig;
      end loop;

      Put (", nbr sensitivity: ");
      Put_Int32 (Nbr_Sens);

      Put (", collapsed_by: ");
      Put_Uns32 (Uns32 (S.Collapsed_By));
      New_Line;

      if Boolean'(True) then
         Put ("  nbr sources (drv + conn : total):");
         New_Line;
         for I in 0 .. S.Typ.W - 1 loop
            Put ("    ");
            Put_Uns32 (I);
            Put (": ");
            Put_Uns32 (S.Nbr_Sources (I).Nbr_Drivers);
            Put (" + ");
            Put_Uns32 (S.Nbr_Sources (I).Nbr_Conns);
            Put (" : ");
            Put_Uns32 (S.Nbr_Sources (I).Total);
            New_Line;
         end loop;
      end if;

      if Opts.Value then
         Driver := S.Drivers;
         while Driver /= No_Driver_Index loop
            declare
               D : Driver_Entry renames Drivers_Table.Table (Driver);
            begin
               Put ("  driver:");
               Disp_Driver_Entry (D);

               Driver := D.Prev_Sig;
            end;
         end loop;

         Conn := S.Connect;
         if Conn /= No_Connect_Index then
            Put ("  connections:");
            New_Line;
            while Conn /= No_Connect_Index loop
               declare
                  C : Connect_Entry renames Connect_Table.Table (Conn);
               begin
                  Disp_Conn_Entry (Conn);
                  if C.Formal.Base = Idx then
                     Conn := C.Formal_Link;
                  else
                     pragma Assert (C.Actual.Base = Idx);
                     Conn := C.Actual_Link;
                  end if;
               end;
            end loop;
         end if;

         Sens := S.Sensitivity;
         while Sens /= No_Sensitivity_Index loop
            declare
               D : Driver_Entry renames Sensitivity_Table.Table (Sens);
            begin
               Put ("  sensitivity:");
               Disp_Driver_Entry (D);

               Sens := D.Prev_Sig;
            end;
         end loop;

         Put ("value (");
         Put_Addr (S.Val.all'Address);
         Put ("): ");
         Disp_Memtyp ((S.Typ, S.Val), Get_Type (S.Decl));
         New_Line;
      end if;

      if Opts.Drivers and then S.Sig /= null then
         Put ("drivers (");
         Put_Addr (S.Sig.all'Address);
         Put ("): ");
         New_Line;
         Info_Signal_Driver ((S.Typ, S.Sig), Get_Type (S.Decl));
      end if;

      if Opts.Actions and then S.Sig /= null then
         Put_Line ("actions:");
         Info_Signal_Action ((S.Typ, S.Sig), Get_Type (S.Decl));
      end if;
   end Info_Signal_Opts;

   procedure Info_Signal (Idx : Signal_Index_Type) is
   begin
      Info_Signal_Opts (Idx, (others => True));
   end Info_Signal;

   --  For gdb.
   pragma Unreferenced (Info_Signal);

   procedure Info_Signal_Proc (Line : String)
   is
      Opts : Info_Signal_Options;
      F, L : Natural;
      Idx : Uns32;
      Valid : Boolean;
   begin
      Opts := (others => False);
      Idx := 0;

      F := Line'First;
      loop
         F := Skip_Blanks (Line, F);
         exit when F > Line'Last;
         L := Get_Word (Line, F);
         if Line (F .. L) = "-h" then
            Put_Line ("info sig [OPTS] [SIG]");
            Put_Line (" -h   disp this help");
            Put_Line (" -v   disp values");
            Put_Line (" -d   disp drivers");
            Put_Line (" -a   disp actions");
            return;
         elsif Line (F .. L) = "-v" then
            Opts.Value := True;
         elsif Line (F .. L) = "-d" then
            Opts.Drivers := True;
         elsif Line (F .. L) = "-a" then
            Opts.Actions := True;
         elsif Line (F) in '0' .. '9' then
            To_Num (Line (F .. L), Idx, Valid);
            if not Valid
              or else Signal_Index_Type (Idx) > Signals_Table.Last
            then
               Put_Line ("invalid signal index");
               return;
            end if;
         else
            Put_Line ("unknown option");
            return;
         end if;
         F := L + 1;
      end loop;

      if Idx = 0 then
         for I in Signals_Table.First .. Signals_Table.Last loop
            Info_Signal_Opts (I, Opts);
         end loop;
      else
         Info_Signal_Opts (Signal_Index_Type (Idx), Opts);
      end if;
   end Info_Signal_Proc;

   procedure Disp_Quantity_Prefix (Decl : Node) is
   begin
      case Get_Kind (Decl) is
         when Iir_Kind_Free_Quantity_Declaration =>
            Put (Image (Get_Identifier (Decl)));
            Put (" (free)");
         when Iir_Kind_Across_Quantity_Declaration =>
            Put (Image (Get_Identifier (Decl)));
            Put (" (across)");
         when Iir_Kind_Through_Quantity_Declaration =>
            Put (Image (Get_Identifier (Decl)));
            Put (" (through)");
         when Iir_Kind_Dot_Attribute =>
            Disp_Quantity_Prefix (Get_Prefix (Decl));
            Put ("'dot");
         when Iir_Kinds_Denoting_Name =>
            Disp_Quantity_Prefix (Get_Named_Entity (Decl));
         when others =>
            Vhdl.Errors.Error_Kind ("disp_quantity_prefix", Decl);
      end case;
   end Disp_Quantity_Prefix;

   procedure Info_Scalar_Quantity (First : Scalar_Quantity_Index; Len : Uns32)
   is
      Idx : Scalar_Quantity_Index;
   begin
      if First = No_Scalar_Quantity then
         return;
      end if;
      Idx := First;
      for I in 1 .. Len loop
         declare
            use Simul.Vhdl_Simul;
            Sq : Scalar_Quantity_Record renames
              Scalar_Quantities_Table.Table (Idx);
         begin
            Put ("  scal #");
            Put_Uns32 (Uns32 (Idx));
            Put ("  idx: ");
            Put_Int32 (Int32 (Sq.Idx));
            Put (", deriv: ");
            Put_Uns32 (Uns32 (Sq.Deriv));
            Put (", integ: ");
            Put_Uns32 (Uns32 (Sq.Integ));
            New_Line;
         end;
         Idx := Idx + 1;
      end loop;
   end Info_Scalar_Quantity;

   procedure Info_Quantity_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      for I in Quantity_Table.First .. Quantity_Table.Last loop
         declare
            Q : Quantity_Entry renames Quantity_Table.Table (I);
         begin
            Put_Int32 (Int32 (I));
            Put (": ");

            Disp_Instance_Path (Q.Inst, True);
            Put ('/');
            Disp_Quantity_Prefix (Q.Decl);
            Put ("  type: ");
            Debug_Type_Short (Q.Typ);
            Put (", len: ");
            Put_Uns32 (Q.Typ.W);
            Put (", Idx: ");
            Put_Uns32 (Uns32 (Q.Idx));
            Put (", val: ");
            Disp_Memtyp ((Q.Typ, Q.Val), Get_Type (Q.Decl));
            New_Line;
            Info_Scalar_Quantity (Q.Idx, Q.Typ.W);
         end;
      end loop;
   end Info_Quantity_Proc;

   procedure Info_Terminal_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      for I in Terminal_Table.First .. Terminal_Table.Last loop
         declare
            T : Terminal_Entry renames Terminal_Table.Table (I);
         begin
            Put_Int32 (Int32 (I));
            Put (": ");

            Disp_Instance_Path (T.Inst, True);
            Put ('/');
            Put (Image (Get_Identifier (T.Decl)));
            Put ("  across: ");
            Debug_Type_Short (T.Across_Typ);
            Put ("  through: ");
            Debug_Type_Short (T.Through_Typ);
            Put (", len: ");
            Put_Uns32 (T.Across_Typ.W);
            Put (", val: ");
            Disp_Memtyp ((T.Across_Typ, T.Ref_Val),
                         Get_Across_Type (Get_Nature (T.Decl)));
            New_Line;
            Info_Scalar_Quantity (T.Ref_Idx, T.Across_Typ.W);
         end;
      end loop;
   end Info_Terminal_Proc;

   procedure Info_Equations_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      for I in Simultaneous_Table.First .. Simultaneous_Table.Last loop
         declare
            S : Simultaneous_Record renames Simultaneous_Table.Table (I);
         begin
            Put_Int32 (Int32 (I));
            Put (": ");

            Disp_Instance_Path (S.Inst, True);
            New_Line;
         end;
      end loop;
   end Info_Equations_Proc;

   procedure Info_Time (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      Put ("now: ");
      Put_Time (Current_Time);
      if Grt.Processes.Flag_AMS then
         Put ("  ");
         Put_Fp64 (Fp64 (Current_Time_AMS));
      end if;
      New_Line;
      Put ("next time: ");
      Put_Time (Grt.Processes.Next_Time);
      New_Line;
   end Info_Time;

   procedure Run_Proc (Line : String)
   is
      Delta_Time : Std_Time;
      P : Positive;
   begin
      P := Skip_Blanks (Line);
      if P <= Line'Last then
         Delta_Time := Grt.Options.Parse_Time (Line (P .. Line'Last));
         if Delta_Time = -1 then
            return;
         end if;
         Simul.Vhdl_Simul.Break_Time := Current_Time + Delta_Time;
         Grt.Processes.Next_Time := Current_Time + Delta_Time;
      end if;

      Elab.Debugger.Prepare_Continue;
   end Run_Proc;

   procedure Ps_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      for I in Processes_Table.First .. Processes_Table.Last loop
         Put_Uns32 (Uns32 (I));
         Put (": ");
         Disp_Instance_Path (Processes_Table.Table (I).Inst);
         Put ("  (");
         Put (Vhdl.Errors.Disp_Location (Processes_Table.Table (I).Proc));
         Put_Line (")");
      end loop;
   end Ps_Proc;

   procedure Trace_Proc (Line : String)
   is
      Fn, Ln : Natural;
      Fv, Lv : Natural;
      State : Boolean;
   begin
      Fn := Skip_Blanks (Line, Line'First);
      if Fn > Line'Last then
         Put ("missing trace name");
         return;
      end if;
      Ln := Get_Word (Line, Fn);

      Fv := Skip_Blanks (Line, Ln + 1);
      if Fv > Line'Last then
         Put ("missing on/off/0/1");
         return;
      end if;
      Lv := Get_Word (Line, Fv);
      if Line (Fv .. Lv) = "on" or Line (Fv .. Lv) = "1" then
         State := True;
      elsif Line (Fv .. Lv) = "off" or Line (Fv .. Lv) = "0" then
         State := False;
      else
         Put ("expect on/off/0/1");
         return;
      end if;

      if Line (Fn .. Ln) = "residues" then
         Simul.Vhdl_Simul.Trace_Residues := State;
      else
         Put_Line ("usage: trace residues on|off|0|1");
      end if;
   end Trace_Proc;

   procedure Quit_Proc (Line : String)
   is
      pragma Unreferenced (Line);
   begin
      GNAT.OS_Lib.OS_Exit (0);
   end Quit_Proc;

   procedure Init is
   begin
      Elab.Vhdl_Debug.Append_Commands;
      Append_Info_Command
        (new String'("sig*nal"),
         new String'("display info about a signal"),
         Info_Signal_Proc'Access);
      Append_Info_Command
        (new String'("quan*tity"),
         new String'("display info about quantities"),
         Info_Quantity_Proc'Access);
      Append_Info_Command
        (new String'("term*inal"),
         new String'("display info about terminals"),
         Info_Terminal_Proc'Access);
      Append_Info_Command
        (new String'("equ*ations"),
         new String'("display info about equations"),
         Info_Equations_Proc'Access);
      Append_Info_Command
        (new String'("t*ime"),
         new String'("display current time"),
         Info_Time'Access);
      Append_Menu_Command
        (new String'("r*un"),
         new String'("resume execution for an amount of time"),
         Run_Proc'Access);
      Append_Menu_Command
        (new String'("ps"),
         new String'("print all processes"),
         Ps_Proc'Access);
      Append_Menu_Command
        (new String'("trace"),
         new String'("enable/disable a trace"),
         Trace_Proc'Access);
      Append_Menu_Command
        (new String'("q*uit"),
         new String'("exit simulation"),
         Quit_Proc'Access);
   end Init;
end Simul.Vhdl_Debug;