/* MI Command Set - disassemble commands.
   Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
   Contributed by Cygnus Solutions (a Red Hat company).

   This file is part of GDB.

   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, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "defs.h"
#include "target.h"
#include "value.h"
#include "mi-cmds.h"
#include "mi-getopt.h"
#include "gdb_string.h"
#include "ui-out.h"
#include "disasm.h"

/* The arguments to be passed on the command line and parsed here are:

   either:

   START-ADDRESS: address to start the disassembly at.
   END-ADDRESS: address to end the disassembly at.

   Optionally, you can leave out END-ADDRESS, in which case it
   will disassemble the function around START-ADDRESS.  In this
   case we will also accept the HOW_MANY argument to throttle
   the disassembly display at that number of assembly lines.

   or:
   
   FILENAME: The name of the file where we want disassemble from.
   LINE: The line around which we want to disassemble. It will
   disassemble the function that contins that line.
   HOW_MANY: Number of disassembly lines to display. In mixed mode, it
   is the number of disassembly lines only, not counting the source
   lines.  

   always required:

   MODE: 0 or 1 for disassembly only, or mixed source and disassembly,
   respectively. */
enum mi_cmd_result
mi_cmd_disassemble (char *command, char **argv, int argc)
{
/* APPLE LOCAL: hack to work around bug in our find_line_pc() replacement func */
  CORE_ADDR start = 0;

  int mixed_source_and_assembly;
  struct symtab *s;

  /* Which options have we processed ... */
  int file_seen = 0;
  int line_seen = 0;
  int num_seen = 0;
  int start_seen = 0;
  int end_seen = 0;

  /* ... and their corresponding value. */
  char *file_string = NULL;
  int line_num = -1;
  int how_many = -1;
  CORE_ADDR low = 0;
  CORE_ADDR high = 0;

  /* Options processing stuff. */
  int optind = 0;
  char *optarg;
  enum opt
  {
    FILE_OPT, LINE_OPT, NUM_OPT, START_OPT, END_OPT
  };
  static struct mi_opt opts[] = {
    {"f", FILE_OPT, 1},
    {"l", LINE_OPT, 1},
    {"n", NUM_OPT, 1},
    {"s", START_OPT, 1},
    {"e", END_OPT, 1},
    0
  };

  /* Get the options with their arguments. Keep track of what we
     encountered. */
  while (1)
    {
      int opt = mi_getopt ("mi_cmd_disassemble", argc, argv, opts,
			   &optind, &optarg);
      if (opt < 0)
	break;
      switch ((enum opt) opt)
	{
	case FILE_OPT:
	  file_string = xstrdup (optarg);
	  file_seen = 1;
	  break;
	case LINE_OPT:
	  line_num = atoi (optarg);
	  line_seen = 1;
	  break;
	case NUM_OPT:
	  how_many = atoi (optarg);
	  num_seen = 1;
	  break;
	case START_OPT:
	  low = parse_and_eval_address (optarg);
	  start_seen = 1;
	  break;
	case END_OPT:
	  high = parse_and_eval_address (optarg);
	  end_seen = 1;
	  break;
	}
    }
  argv += optind;
  argc -= optind;

  /* Allow only filename + linenum (with how_many which is not
     required) OR start_addr + and_addr */

  /* APPLE LOCAL: Allow only the start addr, and interpret this to mean 
     the same thing as in the disassemble command - disassemble the function
     around the -s address.  */

  if (!((line_seen && file_seen && num_seen && !start_seen && !end_seen)
	|| (line_seen && file_seen && !num_seen && !start_seen && !end_seen)
	|| (!line_seen && !file_seen && !num_seen && start_seen && end_seen)
	|| (!line_seen && !file_seen && start_seen && !end_seen)))
    error
      ("mi_cmd_disassemble: Usage: ( [-f filename -l linenum [-n howmany]] | [-s startaddr -e endaddr] | "
       "[-s startaddr [-n howmany]]) [--] mixed_mode.");

  if (argc != 1)
    error
      ("mi_cmd_disassemble: Usage: [-f filename -l linenum [-n howmany]] [-s startaddr -e endaddr] | "
       "[-s startaddr [-n howmany]] [--] mixed_mode.");

  mixed_source_and_assembly = atoi (argv[0]);
  if ((mixed_source_and_assembly != 0) && (mixed_source_and_assembly != 1))
    error ("mi_cmd_disassemble: Mixed_mode argument must be 0 or 1.");


  /* We must get the function beginning and end where line_num is
     contained. */

  if (line_seen && file_seen)
    {
      s = lookup_symtab (file_string);
      if (s == NULL)
	error ("mi_cmd_disassemble: Invalid filename.");
      if (!find_line_pc (s, line_num, &start))
	error ("mi_cmd_disassemble: Invalid line number");
      if (find_pc_partial_function (start, NULL, &low, &high) == 0)
	error ("mi_cmd_disassemble: No function contains specified address");
    }
  else if (start_seen && !end_seen)
    {
      /* APPLE LOCAL: See above, if only start address, disassemble
	 the function around the start address.  */
      if (find_pc_partial_function (low, NULL, &low, &high) == 0)
	error ("mi_cmd_disassemble: No function contains specified address");
    }

  gdb_disassembly (uiout,
  		   file_string,
		   line_num,
		   mixed_source_and_assembly, how_many, low, high);

  return MI_CMD_DONE;
}
