/*
   ** tuiDisassem.c
   **         This module contains functions for handling disassembly display.
 */


#include "defs.h"
#include "symtab.h"
#include "breakpoint.h"
#include "frame.h"

#include "tui.h"
#include "tui-file.h"
#include "tuiData.h"
#include "tuiLayout.h"
#include "tuiSourceWin.h"
#include "tuiStack.h"


/*****************************************
** STATIC LOCAL FUNCTIONS FORWARD DECLS    **
******************************************/

static struct breakpoint *_hasBreak (CORE_ADDR);


/*****************************************
** PUBLIC FUNCTIONS                        **
******************************************/

/*
   ** tuiSetDisassemContent().
   **        Function to set the disassembly window's content.
 */
TuiStatus
#ifdef __STDC__
tuiSetDisassemContent (
			struct symtab *s,
			Opaque startAddr)
#else
tuiSetDisassemContent (s, startAddr)
     struct symtab *s;
     Opaque startAddr;
#endif
{
  TuiStatus ret = TUI_FAILURE;
  struct ui_file *gdb_dis_out;

  if (startAddr != (Opaque) NULL)
    {
      register int i, desc;

      if ((ret = tuiAllocSourceBuffer (disassemWin)) == TUI_SUCCESS)
	{
	  register int offset = disassemWin->detail.sourceInfo.horizontalOffset;
	  register int threshold, curLine = 0, lineWidth, maxLines;
	  CORE_ADDR newpc, pc;
	  disassemble_info asmInfo;
	  TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
extern void strcat_address (CORE_ADDR, char *, int);
extern void strcat_address_numeric (CORE_ADDR, int, char *, int);
	  int curLen = 0;
	  int tab_len = tuiDefaultTabLen ();

	  maxLines = disassemWin->generic.height - 2;	/* account for hilite */
	  lineWidth = disassemWin->generic.width - 1;
	  threshold = (lineWidth - 1) + offset;

	  /* now init the ui_file structure */
	  gdb_dis_out = tui_sfileopen (threshold);

	  INIT_DISASSEMBLE_INFO_NO_ARCH (asmInfo, gdb_dis_out, (fprintf_ftype) fprintf_filtered);
	  asmInfo.read_memory_func = dis_asm_read_memory;
	  asmInfo.memory_error_func = dis_asm_memory_error;

	  disassemWin->detail.sourceInfo.startLineOrAddr.addr = startAddr;

	  /* Now construct each line */
	  for (curLine = 0, pc = (CORE_ADDR) startAddr; (curLine < maxLines);)
	    {
	      TuiWinElementPtr element = (TuiWinElementPtr) disassemWin->generic.content[curLine];
	      struct breakpoint *bp;

	      print_address (pc, gdb_dis_out);

	      curLen = strlen (tui_file_get_strbuf (gdb_dis_out));
	      i = curLen - ((curLen / tab_len) * tab_len);

	      /* adjust buffer length if necessary */
	      tui_file_adjust_strbuf ((tab_len - i > 0) ? (tab_len - i) : 0, gdb_dis_out);

	      /* Add spaces to make the instructions start onthe same column */
	      while (i < tab_len)
		{
		  tui_file_get_strbuf (gdb_dis_out)[curLen] = ' ';
		  i++;
		  curLen++;
		}
	      tui_file_get_strbuf (gdb_dis_out)[curLen] = '\0';

	      newpc = pc + ((*tm_print_insn) (pc, &asmInfo));

	      /* Now copy the line taking the offset into account */
	      if (strlen (tui_file_get_strbuf (gdb_dis_out)) > offset)
		strcpy (element->whichElement.source.line,
			&(tui_file_get_strbuf (gdb_dis_out)[offset]));
	      else
		element->whichElement.source.line[0] = '\0';
	      element->whichElement.source.lineOrAddr.addr = (Opaque) pc;
	      element->whichElement.source.isExecPoint =
		(pc == (CORE_ADDR) ((TuiWinElementPtr) locator->content[0])->whichElement.locator.addr);
	      bp = _hasBreak (pc);
	      element->whichElement.source.hasBreak =
		(bp != (struct breakpoint *) NULL &&
		 (!element->whichElement.source.isExecPoint ||
		  (bp->disposition != del || bp->hit_count <= 0)));
	      curLine++;
	      pc = newpc;
	      /* reset the buffer to empty */
	      tui_file_get_strbuf (gdb_dis_out)[0] = '\0';
	    }
	  ui_file_delete (gdb_dis_out);
	  gdb_dis_out = NULL;
	  disassemWin->generic.contentSize = curLine;
	  ret = TUI_SUCCESS;
	}
    }

  return ret;
}				/* tuiSetDisassemContent */


/*
   ** tuiShowDisassem().
   **        Function to display the disassembly window with disassembled code.
 */
void
#ifdef __STDC__
tuiShowDisassem (
		  Opaque startAddr)
#else
tuiShowDisassem (startAddr)
     Opaque startAddr;
#endif
{
  struct symtab *s = find_pc_symtab ((CORE_ADDR) startAddr);
  TuiWinInfoPtr winWithFocus = tuiWinWithFocus ();

  tuiAddWinToLayout (DISASSEM_WIN);
  tuiUpdateSourceWindow (disassemWin, s, startAddr, FALSE);
  /*
     ** if the focus was in the src win, put it in the asm win, if the
     ** source view isn't split
   */
  if (currentLayout () != SRC_DISASSEM_COMMAND && winWithFocus == srcWin)
    tuiSetWinFocusTo (disassemWin);

  return;
}				/* tuiShowDisassem */


/*
   ** tuiShowDisassemAndUpdateSource().
   **        Function to display the disassembly window.
 */
void
#ifdef __STDC__
tuiShowDisassemAndUpdateSource (
				 Opaque startAddr)
#else
tuiShowDisassemAndUpdateSource (startAddr)
     Opaque startAddr;
#endif
{
  struct symtab_and_line sal;

  tuiShowDisassem (startAddr);
  if (currentLayout () == SRC_DISASSEM_COMMAND)
    {
      TuiGenWinInfoPtr locator = locatorWinInfoPtr ();
      /*
         ** Update what is in the source window if it is displayed too,
         ** note that it follows what is in the disassembly window and visa-versa
       */
      sal = find_pc_line ((CORE_ADDR) startAddr, 0);
      current_source_symtab = sal.symtab;
      tuiUpdateSourceWindow (srcWin, sal.symtab, (Opaque) sal.line, TRUE);
      tuiUpdateLocatorFilename (sal.symtab->filename);
    }

  return;
}				/* tuiShowDisassemAndUpdateSource */


/*
   ** tuiShowDisassemAsIs().
   **        Function to display the disassembly window.  This function shows
   **        the disassembly as specified by the horizontal offset.
 */
void
#ifdef __STDC__
tuiShowDisassemAsIs (
		      Opaque addr)
#else
tuiShowDisassemAsIs (addr)
     Opaque addr;
#endif
{
  tuiAddWinToLayout (DISASSEM_WIN);
  tuiUpdateSourceWindowAsIs (disassemWin, (struct symtab *) NULL, addr, FALSE);
  /*
     ** Update what is in the source window if it is displayed too, not that it
     ** follows what is in the disassembly window and visa-versa
   */
  if (currentLayout () == SRC_DISASSEM_COMMAND)
    tuiShowSourceContent (srcWin);	/*????  Need to do more? */

  return;
}				/* tuiShowDisassem */


/*
   ** tuiGetBeginAsmAddress().
 */
Opaque
#ifdef __STDC__
tuiGetBeginAsmAddress (void)
#else
tuiGetBeginAsmAddress ()
#endif
{
  TuiGenWinInfoPtr locator;
  TuiLocatorElementPtr element;
  Opaque addr;

  locator = locatorWinInfoPtr ();
  element = &((TuiWinElementPtr) locator->content[0])->whichElement.locator;

  if (element->addr == (Opaque) 0)
    {
      /*the target is not executing, because the pc is 0 */

      addr = (Opaque) parse_and_eval_address ("main");

      if (addr == (Opaque) 0)
	addr = (Opaque) parse_and_eval_address ("MAIN");

    }
  else				/* the target is executing */
    addr = element->addr;

  return addr;
}				/* tuiGetBeginAsmAddress */


/*
   ** tuiVerticalDisassemScroll().
   **      Scroll the disassembly forward or backward vertically
 */
void
#ifdef __STDC__
tuiVerticalDisassemScroll (
			    TuiScrollDirection scrollDirection,
			    int numToScroll)
#else
tuiVerticalDisassemScroll (scrollDirection, numToScroll)
     TuiScrollDirection scrollDirection;
     int numToScroll;
#endif
{
  if (disassemWin->generic.content != (OpaquePtr) NULL)
    {
      Opaque pc, lowAddr;
      TuiWinContent content;
      struct symtab *s;

      content = (TuiWinContent) disassemWin->generic.content;
      if (current_source_symtab == (struct symtab *) NULL)
	s = find_pc_symtab (selected_frame->pc);
      else
	s = current_source_symtab;

      pc = content[0]->whichElement.source.lineOrAddr.addr;
      if (find_pc_partial_function ((CORE_ADDR) pc,
				    (char **) NULL,
				    (CORE_ADDR *) & lowAddr,
				    (CORE_ADDR) NULL) == 0)
	error ("No function contains prgram counter for selected frame.\n");
      else
	{
	  register int line = 0;
	  register Opaque newLow;
	  bfd_byte buffer[4];

	  newLow = pc;
	  if (scrollDirection == FORWARD_SCROLL)
	    {
	      for (; line < numToScroll; line++)
		newLow += sizeof (bfd_getb32 (buffer));
	    }
	  else
	    {
	      for (; newLow >= (Opaque) 0 && line < numToScroll; line++)
		newLow -= sizeof (bfd_getb32 (buffer));
	    }
	  tuiUpdateSourceWindowAsIs (disassemWin, s, newLow, FALSE);
	}
    }

  return;
}				/* tuiVerticalDisassemScroll */



/*****************************************
** STATIC LOCAL FUNCTIONS                 **
******************************************/
/*
   ** _hasBreak().
   **      Answer whether there is a break point at the input line in the
   **      source file indicated
 */
static struct breakpoint *
#ifdef __STDC__
_hasBreak (
	    CORE_ADDR addr)
#else
_hasBreak (addr)
     CORE_ADDR addr;
#endif
{
  struct breakpoint *bpWithBreak = (struct breakpoint *) NULL;
  struct breakpoint *bp;
  extern struct breakpoint *breakpoint_chain;


  for (bp = breakpoint_chain;
       (bp != (struct breakpoint *) NULL &&
	bpWithBreak == (struct breakpoint *) NULL);
       bp = bp->next)
    if (addr == bp->address)
      bpWithBreak = bp;

  return bpWithBreak;
}				/* _hasBreak */
