1130803Smarcel/* Disassembly display.
2130803Smarcel
3130803Smarcel   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
4130803Smarcel   Foundation, Inc.
5130803Smarcel
6130803Smarcel   Contributed by Hewlett-Packard Company.
7130803Smarcel
8130803Smarcel   This file is part of GDB.
9130803Smarcel
10130803Smarcel   This program is free software; you can redistribute it and/or modify
11130803Smarcel   it under the terms of the GNU General Public License as published by
12130803Smarcel   the Free Software Foundation; either version 2 of the License, or
13130803Smarcel   (at your option) any later version.
14130803Smarcel
15130803Smarcel   This program is distributed in the hope that it will be useful,
16130803Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
17130803Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18130803Smarcel   GNU General Public License for more details.
19130803Smarcel
20130803Smarcel   You should have received a copy of the GNU General Public License
21130803Smarcel   along with this program; if not, write to the Free Software
22130803Smarcel   Foundation, Inc., 59 Temple Place - Suite 330,
23130803Smarcel   Boston, MA 02111-1307, USA.  */
24130803Smarcel
25130803Smarcel#include "defs.h"
26130803Smarcel#include "symtab.h"
27130803Smarcel#include "breakpoint.h"
28130803Smarcel#include "frame.h"
29130803Smarcel#include "value.h"
30130803Smarcel#include "source.h"
31130803Smarcel#include "disasm.h"
32130803Smarcel#include "gdb_string.h"
33130803Smarcel#include "tui/tui.h"
34130803Smarcel#include "tui/tui-data.h"
35130803Smarcel#include "tui/tui-win.h"
36130803Smarcel#include "tui/tui-layout.h"
37130803Smarcel#include "tui/tui-winsource.h"
38130803Smarcel#include "tui/tui-stack.h"
39130803Smarcel#include "tui/tui-file.h"
40130803Smarcel
41130803Smarcel#include "gdb_curses.h"
42130803Smarcel
43130803Smarcelstruct tui_asm_line
44130803Smarcel{
45130803Smarcel  CORE_ADDR addr;
46130803Smarcel  char* addr_string;
47130803Smarcel  char* insn;
48130803Smarcel};
49130803Smarcel
50130803Smarcel/* Function to set the disassembly window's content.
51130803Smarcel   Disassemble count lines starting at pc.
52130803Smarcel   Return address of the count'th instruction after pc.  */
53130803Smarcelstatic CORE_ADDR
54130803Smarceltui_disassemble (struct tui_asm_line* asm_lines, CORE_ADDR pc, int count)
55130803Smarcel{
56130803Smarcel  struct ui_file *gdb_dis_out;
57130803Smarcel
58130803Smarcel  /* now init the ui_file structure */
59130803Smarcel  gdb_dis_out = tui_sfileopen (256);
60130803Smarcel
61130803Smarcel  /* Now construct each line */
62130803Smarcel  for (; count > 0; count--, asm_lines++)
63130803Smarcel    {
64130803Smarcel      if (asm_lines->addr_string)
65130803Smarcel        xfree (asm_lines->addr_string);
66130803Smarcel      if (asm_lines->insn)
67130803Smarcel        xfree (asm_lines->insn);
68130803Smarcel
69130803Smarcel      print_address (pc, gdb_dis_out);
70130803Smarcel      asm_lines->addr = pc;
71130803Smarcel      asm_lines->addr_string = xstrdup (tui_file_get_strbuf (gdb_dis_out));
72130803Smarcel
73130803Smarcel      ui_file_rewind (gdb_dis_out);
74130803Smarcel
75130803Smarcel      pc = pc + gdb_print_insn (pc, gdb_dis_out);
76130803Smarcel
77130803Smarcel      asm_lines->insn = xstrdup (tui_file_get_strbuf (gdb_dis_out));
78130803Smarcel
79130803Smarcel      /* reset the buffer to empty */
80130803Smarcel      ui_file_rewind (gdb_dis_out);
81130803Smarcel    }
82130803Smarcel  ui_file_delete (gdb_dis_out);
83130803Smarcel  return pc;
84130803Smarcel}
85130803Smarcel
86130803Smarcel/* Find the disassembly address that corresponds to FROM lines
87130803Smarcel   above or below the PC.  Variable sized instructions are taken
88130803Smarcel   into account by the algorithm.  */
89130803Smarcelstatic CORE_ADDR
90130803Smarceltui_find_disassembly_address (CORE_ADDR pc, int from)
91130803Smarcel{
92130803Smarcel  CORE_ADDR new_low;
93130803Smarcel  int max_lines;
94130803Smarcel  int i;
95130803Smarcel  struct tui_asm_line* asm_lines;
96130803Smarcel
97130803Smarcel  max_lines = (from > 0) ? from : - from;
98130803Smarcel  if (max_lines <= 1)
99130803Smarcel     return pc;
100130803Smarcel
101130803Smarcel  asm_lines = (struct tui_asm_line*) alloca (sizeof (struct tui_asm_line)
102130803Smarcel                                         * max_lines);
103130803Smarcel  memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
104130803Smarcel
105130803Smarcel  new_low = pc;
106130803Smarcel  if (from > 0)
107130803Smarcel    {
108130803Smarcel      tui_disassemble (asm_lines, pc, max_lines);
109130803Smarcel      new_low = asm_lines[max_lines - 1].addr;
110130803Smarcel    }
111130803Smarcel  else
112130803Smarcel    {
113130803Smarcel      CORE_ADDR last_addr;
114130803Smarcel      int pos;
115130803Smarcel      struct minimal_symbol* msymbol;
116130803Smarcel
117130803Smarcel      /* Find backward an address which is a symbol
118130803Smarcel         and for which disassembling from that address will fill
119130803Smarcel         completely the window.  */
120130803Smarcel      pos = max_lines - 1;
121130803Smarcel      do {
122130803Smarcel         new_low -= 1 * max_lines;
123130803Smarcel         msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
124130803Smarcel
125130803Smarcel         if (msymbol)
126130803Smarcel            new_low = SYMBOL_VALUE_ADDRESS (msymbol);
127130803Smarcel         else
128130803Smarcel            new_low += 1 * max_lines;
129130803Smarcel
130130803Smarcel         tui_disassemble (asm_lines, new_low, max_lines);
131130803Smarcel         last_addr = asm_lines[pos].addr;
132130803Smarcel      } while (last_addr > pc && msymbol);
133130803Smarcel
134130803Smarcel      /* Scan forward disassembling one instruction at a time
135130803Smarcel         until the last visible instruction of the window
136130803Smarcel         matches the pc.  We keep the disassembled instructions
137130803Smarcel         in the 'lines' window and shift it downward (increasing
138130803Smarcel         its addresses).  */
139130803Smarcel      if (last_addr < pc)
140130803Smarcel        do
141130803Smarcel          {
142130803Smarcel            CORE_ADDR next_addr;
143130803Smarcel
144130803Smarcel            pos++;
145130803Smarcel            if (pos >= max_lines)
146130803Smarcel              pos = 0;
147130803Smarcel
148130803Smarcel            next_addr = tui_disassemble (&asm_lines[pos], last_addr, 1);
149130803Smarcel
150130803Smarcel            /* If there are some problems while disassembling exit.  */
151130803Smarcel            if (next_addr <= last_addr)
152130803Smarcel              break;
153130803Smarcel            last_addr = next_addr;
154130803Smarcel          } while (last_addr <= pc);
155130803Smarcel      pos++;
156130803Smarcel      if (pos >= max_lines)
157130803Smarcel         pos = 0;
158130803Smarcel      new_low = asm_lines[pos].addr;
159130803Smarcel    }
160130803Smarcel  for (i = 0; i < max_lines; i++)
161130803Smarcel    {
162130803Smarcel      xfree (asm_lines[i].addr_string);
163130803Smarcel      xfree (asm_lines[i].insn);
164130803Smarcel    }
165130803Smarcel  return new_low;
166130803Smarcel}
167130803Smarcel
168130803Smarcel/* Function to set the disassembly window's content.  */
169130803Smarcelenum tui_status
170130803Smarceltui_set_disassem_content (CORE_ADDR pc)
171130803Smarcel{
172130803Smarcel  enum tui_status ret = TUI_FAILURE;
173130803Smarcel  int i;
174130803Smarcel  int offset = TUI_DISASM_WIN->detail.source_info.horizontal_offset;
175130803Smarcel  int line_width, max_lines;
176130803Smarcel  CORE_ADDR cur_pc;
177130803Smarcel  struct tui_gen_win_info * locator = tui_locator_win_info_ptr ();
178130803Smarcel  int tab_len = tui_default_tab_len ();
179130803Smarcel  struct tui_asm_line* asm_lines;
180130803Smarcel  int insn_pos;
181130803Smarcel  int addr_size, max_size;
182130803Smarcel  char* line;
183130803Smarcel
184130803Smarcel  if (pc == 0)
185130803Smarcel    return TUI_FAILURE;
186130803Smarcel
187130803Smarcel  ret = tui_alloc_source_buffer (TUI_DISASM_WIN);
188130803Smarcel  if (ret != TUI_SUCCESS)
189130803Smarcel    return ret;
190130803Smarcel
191130803Smarcel  TUI_DISASM_WIN->detail.source_info.start_line_or_addr.addr = pc;
192130803Smarcel  cur_pc = (CORE_ADDR)
193130803Smarcel    (((struct tui_win_element *) locator->content[0])->which_element.locator.addr);
194130803Smarcel
195130803Smarcel  max_lines = TUI_DISASM_WIN->generic.height - 2;	/* account for hilite */
196130803Smarcel
197130803Smarcel  /* Get temporary table that will hold all strings (addr & insn).  */
198130803Smarcel  asm_lines = (struct tui_asm_line*) alloca (sizeof (struct tui_asm_line)
199130803Smarcel                                         * max_lines);
200130803Smarcel  memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
201130803Smarcel
202130803Smarcel  line_width = TUI_DISASM_WIN->generic.width - 1;
203130803Smarcel
204130803Smarcel  tui_disassemble (asm_lines, pc, max_lines);
205130803Smarcel
206130803Smarcel  /* See what is the maximum length of an address and of a line.  */
207130803Smarcel  addr_size = 0;
208130803Smarcel  max_size = 0;
209130803Smarcel  for (i = 0; i < max_lines; i++)
210130803Smarcel    {
211130803Smarcel      size_t len = strlen (asm_lines[i].addr_string);
212130803Smarcel      if (len > addr_size)
213130803Smarcel        addr_size = len;
214130803Smarcel
215130803Smarcel      len = strlen (asm_lines[i].insn) + tab_len;
216130803Smarcel      if (len > max_size)
217130803Smarcel        max_size = len;
218130803Smarcel    }
219130803Smarcel  max_size += addr_size + tab_len;
220130803Smarcel
221130803Smarcel  /* Allocate memory to create each line.  */
222130803Smarcel  line = (char*) alloca (max_size);
223130803Smarcel  insn_pos = (1 + (addr_size / tab_len)) * tab_len;
224130803Smarcel
225130803Smarcel  /* Now construct each line */
226130803Smarcel  for (i = 0; i < max_lines; i++)
227130803Smarcel    {
228130803Smarcel      struct tui_win_element * element;
229130803Smarcel      struct tui_source_element* src;
230130803Smarcel      int cur_len;
231130803Smarcel
232130803Smarcel      element = (struct tui_win_element *) TUI_DISASM_WIN->generic.content[i];
233130803Smarcel      src = &element->which_element.source;
234130803Smarcel      strcpy (line, asm_lines[i].addr_string);
235130803Smarcel      cur_len = strlen (line);
236130803Smarcel
237130803Smarcel      /* Add spaces to make the instructions start on the same column */
238130803Smarcel      while (cur_len < insn_pos)
239130803Smarcel        {
240130803Smarcel          strcat (line, " ");
241130803Smarcel          cur_len++;
242130803Smarcel        }
243130803Smarcel
244130803Smarcel      strcat (line, asm_lines[i].insn);
245130803Smarcel
246130803Smarcel      /* Now copy the line taking the offset into account */
247130803Smarcel      if (strlen (line) > offset)
248130803Smarcel        strcpy (src->line, &line[offset]);
249130803Smarcel      else
250130803Smarcel        src->line[0] = '\0';
251130803Smarcel
252130803Smarcel      src->line_or_addr.addr = asm_lines[i].addr;
253130803Smarcel      src->is_exec_point = asm_lines[i].addr == cur_pc;
254130803Smarcel
255130803Smarcel      /* See whether there is a breakpoint installed.  */
256130803Smarcel      src->has_break = (!src->is_exec_point
257130803Smarcel                       && breakpoint_here_p (pc) != no_breakpoint_here);
258130803Smarcel
259130803Smarcel      xfree (asm_lines[i].addr_string);
260130803Smarcel      xfree (asm_lines[i].insn);
261130803Smarcel    }
262130803Smarcel  TUI_DISASM_WIN->generic.content_size = i;
263130803Smarcel  return TUI_SUCCESS;
264130803Smarcel}
265130803Smarcel
266130803Smarcel
267130803Smarcel/* Function to display the disassembly window with disassembled code.   */
268130803Smarcelvoid
269130803Smarceltui_show_disassem (CORE_ADDR start_addr)
270130803Smarcel{
271130803Smarcel  struct symtab *s = find_pc_symtab (start_addr);
272130803Smarcel  struct tui_win_info * win_with_focus = tui_win_with_focus ();
273130803Smarcel  union tui_line_or_address val;
274130803Smarcel
275130803Smarcel  val.addr = start_addr;
276130803Smarcel  tui_add_win_to_layout (DISASSEM_WIN);
277130803Smarcel  tui_update_source_window (TUI_DISASM_WIN, s, val, FALSE);
278130803Smarcel  /*
279130803Smarcel     ** if the focus was in the src win, put it in the asm win, if the
280130803Smarcel     ** source view isn't split
281130803Smarcel   */
282130803Smarcel  if (tui_current_layout () != SRC_DISASSEM_COMMAND && win_with_focus == TUI_SRC_WIN)
283130803Smarcel    tui_set_win_focus_to (TUI_DISASM_WIN);
284130803Smarcel
285130803Smarcel  return;
286130803Smarcel}
287130803Smarcel
288130803Smarcel
289130803Smarcel/* Function to display the disassembly window.   */
290130803Smarcelvoid
291130803Smarceltui_show_disassem_and_update_source (CORE_ADDR start_addr)
292130803Smarcel{
293130803Smarcel  struct symtab_and_line sal;
294130803Smarcel
295130803Smarcel  tui_show_disassem (start_addr);
296130803Smarcel  if (tui_current_layout () == SRC_DISASSEM_COMMAND)
297130803Smarcel    {
298130803Smarcel      union tui_line_or_address val;
299130803Smarcel
300130803Smarcel      /*
301130803Smarcel         ** Update what is in the source window if it is displayed too,
302130803Smarcel         ** note that it follows what is in the disassembly window and visa-versa
303130803Smarcel       */
304130803Smarcel      sal = find_pc_line (start_addr, 0);
305130803Smarcel      val.line_no = sal.line;
306130803Smarcel      tui_update_source_window (TUI_SRC_WIN, sal.symtab, val, TRUE);
307130803Smarcel      if (sal.symtab)
308130803Smarcel	{
309130803Smarcel	  set_current_source_symtab_and_line (&sal);
310130803Smarcel	  tui_update_locator_filename (sal.symtab->filename);
311130803Smarcel	}
312130803Smarcel      else
313130803Smarcel	tui_update_locator_filename ("?");
314130803Smarcel    }
315130803Smarcel
316130803Smarcel  return;
317130803Smarcel}
318130803Smarcel
319130803SmarcelCORE_ADDR
320130803Smarceltui_get_begin_asm_address (void)
321130803Smarcel{
322130803Smarcel  struct tui_gen_win_info * locator;
323130803Smarcel  struct tui_locator_element * element;
324130803Smarcel  CORE_ADDR addr;
325130803Smarcel
326130803Smarcel  locator = tui_locator_win_info_ptr ();
327130803Smarcel  element = &((struct tui_win_element *) locator->content[0])->which_element.locator;
328130803Smarcel
329130803Smarcel  if (element->addr == 0)
330130803Smarcel    {
331130803Smarcel      struct minimal_symbol *main_symbol;
332130803Smarcel
333130803Smarcel      /* Find address of the start of program.
334130803Smarcel         Note: this should be language specific.  */
335130803Smarcel      main_symbol = lookup_minimal_symbol ("main", NULL, NULL);
336130803Smarcel      if (main_symbol == 0)
337130803Smarcel        main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL);
338130803Smarcel      if (main_symbol == 0)
339130803Smarcel        main_symbol = lookup_minimal_symbol ("_start", NULL, NULL);
340130803Smarcel      if (main_symbol)
341130803Smarcel        addr = SYMBOL_VALUE_ADDRESS (main_symbol);
342130803Smarcel      else
343130803Smarcel        addr = 0;
344130803Smarcel    }
345130803Smarcel  else				/* the target is executing */
346130803Smarcel    addr = element->addr;
347130803Smarcel
348130803Smarcel  return addr;
349130803Smarcel}
350130803Smarcel
351130803Smarcel/* Determine what the low address will be to display in the TUI's
352130803Smarcel   disassembly window.  This may or may not be the same as the
353130803Smarcel   low address input.  */
354130803SmarcelCORE_ADDR
355130803Smarceltui_get_low_disassembly_address (CORE_ADDR low, CORE_ADDR pc)
356130803Smarcel{
357130803Smarcel  int pos;
358130803Smarcel
359130803Smarcel  /* Determine where to start the disassembly so that the pc is about in the
360130803Smarcel     middle of the viewport.  */
361130803Smarcel  pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2;
362130803Smarcel  pc = tui_find_disassembly_address (pc, -pos);
363130803Smarcel
364130803Smarcel  if (pc < low)
365130803Smarcel    pc = low;
366130803Smarcel  return pc;
367130803Smarcel}
368130803Smarcel
369130803Smarcel/* Scroll the disassembly forward or backward vertically.  */
370130803Smarcelvoid
371130803Smarceltui_vertical_disassem_scroll (enum tui_scroll_direction scroll_direction,
372130803Smarcel			      int num_to_scroll)
373130803Smarcel{
374130803Smarcel  if (TUI_DISASM_WIN->generic.content != NULL)
375130803Smarcel    {
376130803Smarcel      CORE_ADDR pc;
377130803Smarcel      tui_win_content content;
378130803Smarcel      struct symtab *s;
379130803Smarcel      union tui_line_or_address val;
380130803Smarcel      int max_lines, dir;
381130803Smarcel      struct symtab_and_line cursal = get_current_source_symtab_and_line ();
382130803Smarcel
383130803Smarcel      content = (tui_win_content) TUI_DISASM_WIN->generic.content;
384130803Smarcel      if (cursal.symtab == (struct symtab *) NULL)
385130803Smarcel	s = find_pc_symtab (get_frame_pc (deprecated_selected_frame));
386130803Smarcel      else
387130803Smarcel	s = cursal.symtab;
388130803Smarcel
389130803Smarcel      /* account for hilite */
390130803Smarcel      max_lines = TUI_DISASM_WIN->generic.height - 2;
391130803Smarcel      pc = content[0]->which_element.source.line_or_addr.addr;
392130803Smarcel      dir = (scroll_direction == FORWARD_SCROLL) ? max_lines : - max_lines;
393130803Smarcel
394130803Smarcel      val.addr = tui_find_disassembly_address (pc, dir);
395130803Smarcel      tui_update_source_window_as_is (TUI_DISASM_WIN, s, val, FALSE);
396130803Smarcel    }
397130803Smarcel}
398