/////////////////////////////////////////////////////////////////////////
// $Id: dbg_main.cc 12300 2014-04-29 18:49:38Z sshwarts $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2001-2013  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
/////////////////////////////////////////////////////////////////////////

extern "C" {
#include <signal.h>
}

#include "bochs.h"
#include "param_names.h"
#include "cpu/cpu.h"
#include "iodev/iodev.h"

#if BX_DEBUGGER

#include "disasm/disasm.h"

#define LOG_THIS genlog->

#if HAVE_LIBREADLINE
extern "C" {
#include <stdio.h>
#include <readline/readline.h>
#if HAVE_READLINE_HISTORY_H
#include <readline/history.h>
#endif
}
#endif

// default CPU in the debugger.  For commands like "dump_cpu" it will
// use the default instead of always dumping all cpus.
unsigned dbg_cpu = 0;
bx_list_c *dbg_cpu_list = 0;

extern const char* cpu_mode_string(unsigned cpu_mode);
extern void bx_sr_after_restore_state(void);

void bx_dbg_print_descriptor(Bit32u lo, Bit32u hi);
void bx_dbg_print_descriptor64(Bit32u lo1, Bit32u hi1, Bit32u lo2, Bit32u hi2);

static bx_param_bool_c *sim_running = NULL;
static bx_bool bx_dbg_exit_called;

static char tmp_buf[512];
static char tmp_buf_prev[512];
static char *tmp_buf_ptr;
static char *argv0 = NULL;

static FILE *debugger_log = NULL;

static struct {
  // some fields used for single CPU debugger
  bx_bool  auto_disassemble;
  unsigned disassemble_size;
  char     default_display_format;
  char     default_unit_size;
  bx_address default_addr;
  unsigned next_bpoint_id;
  unsigned next_wpoint_id;
} bx_debugger;

typedef struct _debug_info_t {
  const char *name;
  bx_devmodel_c *device;
  struct _debug_info_t *next;
} debug_info_t;

static debug_info_t *bx_debug_info_list = NULL;

typedef struct {
  FILE    *fp;
  char     fname[BX_MAX_PATH];
  unsigned lineno;
} bx_infile_stack_entry_t;

bx_infile_stack_entry_t bx_infile_stack[BX_INFILE_DEPTH];
int                     bx_infile_stack_index = 0;

static int  bx_nest_infile(char *path);

void CDECL bx_debug_ctrlc_handler(int signum);

static void bx_unnest_infile(void);
static void bx_get_command(void);
static void bx_dbg_print_guard_results();
static void bx_dbg_breakpoint_changed(void);
static void bx_dbg_set_icount_guard(int which_cpu, Bit32u n);

bx_guard_t bx_guard;

// DMA stuff
void bx_dbg_post_dma_reports(void);
#define BX_BATCH_DMA_BUFSIZE 512

static struct {
  unsigned this_many; // batch this many max before posting events
  unsigned Qsize;     // this many have been batched
  struct {
    bx_phy_address addr; // address of DMA op
    unsigned len;        // number of bytes in op
    unsigned what;       // BX_READ or BX_WRITE
    Bit32u   val;        // value of DMA op
    Bit64u   time;       // system time at this dma op
  } Q[BX_BATCH_DMA_BUFSIZE];
} bx_dbg_batch_dma;

// some buffers for disassembly
static disassembler bx_disassemble;
static Bit8u bx_disasm_ibuf[32];
static char  bx_disasm_tbuf[512];

static bx_bool watchpoint_continue = 0;
unsigned num_write_watchpoints = 0;
unsigned num_read_watchpoints = 0;
bx_watchpoint write_watchpoint[BX_DBG_MAX_WATCHPONTS];
bx_watchpoint read_watchpoint[BX_DBG_MAX_WATCHPONTS];

#define DBG_PRINTF_BUFFER_LEN 1024

void dbg_printf(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  char buf[DBG_PRINTF_BUFFER_LEN+1];
  vsnprintf(buf, DBG_PRINTF_BUFFER_LEN, fmt, ap);
  va_end(ap);
  if (debugger_log != NULL) {
    fprintf(debugger_log,"%s", buf);
    fflush(debugger_log);
  }
  SIM->debug_puts(buf); // send to debugger, which will free buf when done.
}

void bx_dbg_init_infile(void)
{
  bx_infile_stack_index = 0;
  bx_infile_stack[0].fp = stdin;
  bx_infile_stack[0].lineno = 0;
}

int bx_dbg_set_rcfile(const char *rcfile)
{
  strncpy(bx_infile_stack[0].fname, rcfile, BX_MAX_PATH);
  bx_infile_stack[0].fname[BX_MAX_PATH-1] = 0;
  BX_INFO(("debugger using rc file '%s'.", rcfile));
  return bx_nest_infile((char*)rcfile);
}

bx_bool bx_dbg_register_debug_info(const char *devname, void *dev)
{
  debug_info_t *debug_info;

  debug_info = (debug_info_t *)malloc(sizeof(debug_info_t));
  if (debug_info == NULL) {
    BX_PANIC(("can't allocate debug_info_t"));
    return 0;
  }

  debug_info->name = devname;
  debug_info->device = (bx_devmodel_c*)dev;
  debug_info->next = NULL;

  if (bx_debug_info_list == NULL) {
    bx_debug_info_list = debug_info;
  } else {
    debug_info_t *temp = bx_debug_info_list;

    while (temp->next) {
      if (!strcmp(temp->name, devname)) {
        free(debug_info);
        return 0;
      }
      temp = temp->next;
    }
    temp->next = debug_info;
  }
  return 1;
}

void bx_dbg_info_cleanup(void)
{
  debug_info_t *temp = bx_debug_info_list, *next;
  while (temp != NULL) {
    next = temp->next;
    free(temp);
    temp = next;
  }
  bx_debug_info_list = NULL;
}

bx_bool bx_dbg_info_find_device(const char *devname, debug_info_t **found_debug_info)
{
  debug_info_t *debug_info;

  for (debug_info = bx_debug_info_list; debug_info; debug_info = debug_info->next) {
    if (!strcmp(debug_info->name, devname)) {
      *found_debug_info = debug_info;
      return 1;
    }
  }
  return 0;
}

int bx_dbg_main(void)
{
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);

  bx_dbg_exit_called = 0;
  bx_dbg_batch_dma.this_many = 1;
  bx_dbg_batch_dma.Qsize     = 0;

  memset(&bx_guard, 0, sizeof(bx_guard));
  bx_guard.async.irq = 1;
  bx_guard.async.dma = 1;

  memset(&bx_debugger, 0, sizeof(bx_debugger));
  bx_debugger.auto_disassemble = 1;
  bx_debugger.disassemble_size = 0;
  bx_debugger.default_display_format = 'x';
  bx_debugger.default_unit_size      = 'w';
  bx_debugger.default_addr = 0;
  bx_debugger.next_bpoint_id = 1;
  bx_debugger.next_wpoint_id = 1;

  dbg_cpu_list = (bx_list_c*) SIM->get_param("cpu0", SIM->get_bochs_root());
  const char *debugger_log_filename = SIM->get_param_string(BXPN_DEBUGGER_LOG_FILENAME)->getptr();

  // Open debugger log file if needed
  if (strlen(debugger_log_filename) > 0 && (strcmp(debugger_log_filename, "-") != 0))
  {
    debugger_log = fopen(debugger_log_filename, "w");
    if (!debugger_log) {
      BX_PANIC(("Can not open debugger log file '%s'", debugger_log_filename));
    }
    else {
      BX_INFO(("Using debugger log file %s", debugger_log_filename));
    }
  }

  memset(bx_disasm_ibuf, 0, sizeof(bx_disasm_ibuf));

  // create a boolean parameter that will tell if the simulation is
  // running (continue command) or waiting for user response.  This affects
  // some parts of the GUI.
  if (sim_running == NULL) {
    bx_list_c *base = (bx_list_c*) SIM->get_param("general");
    sim_running = new bx_param_bool_c(base,
        "debug_running",
        "Simulation is running", "", 0);
  } else {
    sim_running->set(0);
  }
  // setup Ctrl-C handler
  if (!SIM->has_debug_gui()) {
    signal(SIGINT, bx_debug_ctrlc_handler);
    BX_INFO(("set SIGINT handler to bx_debug_ctrlc_handler"));
  }

  // Print disassembly of the first instruction...  you wouldn't think it
  // would have to be so hard.  First initialize guard_found, since it is used
  // in the disassembly code to decide what instruction to print.
  for (int i=0; i<BX_SMP_PROCESSORS; i++) {
    BX_CPU(i)->guard_found.cs = BX_CPU(i)->sregs[BX_SEG_REG_CS].selector.value;
    BX_CPU(i)->guard_found.eip = BX_CPU(i)->get_instruction_pointer();
    BX_CPU(i)->guard_found.laddr =
      BX_CPU(i)->get_laddr(BX_SEG_REG_CS, BX_CPU(i)->guard_found.eip);
    BX_CPU(i)->guard_found.code_32_64 = 0;
    // 00 - 16 bit, 01 - 32 bit, 10 - 64-bit, 11 - illegal
    if (BX_CPU(i)->sregs[BX_SEG_REG_CS].cache.u.segment.d_b)
      BX_CPU(i)->guard_found.code_32_64 |= 0x1;
    if (BX_CPU(i)->get_cpu_mode() == BX_MODE_LONG_64)
      BX_CPU(i)->guard_found.code_32_64 |= 0x2;
  }
  // finally, call the usual function to print the disassembly
  dbg_printf("Next at t=" FMT_LL "d\n", bx_pc_system.time_ticks());
  bx_dbg_disassemble_current(-1, 0);  // all cpus, don't print time

  bx_dbg_user_input_loop();

  if(debugger_log != NULL)
    fclose(debugger_log);

  bx_dbg_exit(0);
  return(0); // keep compiler happy
}

void bx_dbg_interpret_line(char *cmd)
{
  bx_add_lex_input(cmd);
  bxparse();
}

void bx_dbg_user_input_loop(void)
{
  int reti;
  unsigned include_cmd_len = strlen(BX_INCLUDE_CMD);

  while(1) {
    SIM->refresh_ci();
    SIM->set_display_mode(DISP_MODE_CONFIG);
    SIM->get_param_bool(BXPN_MOUSE_ENABLED)->set(0);
    bx_get_command();
reparse:
    if ((*tmp_buf_ptr == '\n') || (*tmp_buf_ptr == 0))
    {
        if ((*tmp_buf_prev != '\n') && (*tmp_buf_prev != 0)) {
          strncpy(tmp_buf, tmp_buf_prev, sizeof(tmp_buf_prev));
          goto reparse;
        }
    }
    else if ((strncmp(tmp_buf_ptr, BX_INCLUDE_CMD, include_cmd_len) == 0) &&
              (tmp_buf_ptr[include_cmd_len] == ' ' ||
               tmp_buf_ptr[include_cmd_len] == '\t'))
    {
      char *ptr = tmp_buf_ptr + include_cmd_len + 1;
      while(*ptr==' ' || *ptr=='\t')
        ptr++;

      int len = strlen(ptr);
      if (len == 0) {
        dbg_printf("%s: no filename given to 'source' command.\n", argv0);
        if (bx_infile_stack_index > 0) {
          dbg_printf("%s: ERROR in source file causes exit.\n", argv0);
          bx_dbg_exit(1);
        }
        continue;
      }
      ptr[len-1] = 0; // get rid of newline
      reti = bx_nest_infile(ptr);
      if (reti==0 && bx_infile_stack_index > 0) {
        dbg_printf("%s: ERROR in source file causes exit.\n", argv0);
        bx_dbg_exit(1);
      }
    }
    else {
      // Give a chance to the command line extensions, to
      // consume the command.  If they return 0, then
      // we need to process the command.  A return of 1
      // means, the extensions have handled the command
      if (bx_dbg_extensions(tmp_buf_ptr)==0) {
        // process command here
        bx_add_lex_input(tmp_buf_ptr);
        bxparse();
      }
    }
  }
}

void bx_get_command(void)
{
  char *charptr_ret;

  bx_infile_stack[bx_infile_stack_index].lineno++;

  char prompt[256];
  if (bx_infile_stack_index == 0) {
    sprintf(prompt, "<bochs:%d> ", bx_infile_stack[bx_infile_stack_index].lineno);
  }
  if (SIM->has_debug_gui() && bx_infile_stack_index == 0) {
    // wait for gui debugger to send another debugger command
    charptr_ret = SIM->debug_get_next_command();
    if (charptr_ret) {
      strncpy(tmp_buf, charptr_ret, sizeof(tmp_buf));
      strcat(tmp_buf, "\n");
      // The returned string was allocated in wxmain.cc by "new char[]".
      // Free it with delete[].
      delete [] charptr_ret;
      charptr_ret = &tmp_buf[0];
    } else {
      // if debug_get_next_command returned NULL, probably the GUI is
      // shutting down
    }
  }
#if HAVE_LIBREADLINE
  else if (bx_infile_stack_index == 0) {
    charptr_ret = readline(prompt);
    // beware, returns NULL on end of file
    if (charptr_ret && strlen(charptr_ret) > 0) {
      add_history(charptr_ret);
      strcpy(tmp_buf, charptr_ret);
      strcat(tmp_buf, "\n");
      free(charptr_ret);
      charptr_ret = &tmp_buf[0];
    }
  } else {
    charptr_ret = fgets(tmp_buf, sizeof(tmp_buf), bx_infile_stack[bx_infile_stack_index].fp);
  }
#else /* !HAVE_LIBREADLINE */
  else {
    if (bx_infile_stack_index == 0)
      dbg_printf("%s", prompt);
    strncpy(tmp_buf_prev, tmp_buf, sizeof(tmp_buf));
    charptr_ret = fgets(tmp_buf, sizeof(tmp_buf), bx_infile_stack[bx_infile_stack_index].fp);
  }
#endif
  if (charptr_ret == NULL) {
    // see if error was due to EOF condition
    if (feof(bx_infile_stack[bx_infile_stack_index].fp)) {
      if (bx_infile_stack_index > 0) {
        // nested level of include files, pop back to previous one
        bx_unnest_infile();
      }
      else {
        // not nested, sitting at stdin prompt, user wants out
        bx_dbg_quit_command();
        BX_PANIC(("bx_dbg_quit_command should not return, but it did"));
      }

      // call recursively
      bx_get_command();
      return;
    }

    // error was not EOF, see if it was from a Ctrl-C
    if (bx_guard.interrupt_requested) {
      tmp_buf[0] = '\n';
      tmp_buf[1] = 0;
      tmp_buf_ptr = &tmp_buf[0];
      bx_guard.interrupt_requested = 0;
      return;
    }

    dbg_printf("fgets() returned ERROR.\n");
    dbg_printf("debugger interrupt request was %u\n", bx_guard.interrupt_requested);
    bx_dbg_exit(1);
  }
  tmp_buf_ptr = &tmp_buf[0];

  if (debugger_log != NULL) {
    fprintf(debugger_log, "%s", tmp_buf);
    fflush(debugger_log);
  }

  // look for first non-whitespace character
  while (((*tmp_buf_ptr == ' ')  || (*tmp_buf_ptr == '\t')) &&
          (*tmp_buf_ptr != '\n') && (*tmp_buf_ptr != 0))
  {
    tmp_buf_ptr++;
  }
}

int bx_nest_infile(char *path)
{
  FILE *tmp_fp;

  tmp_fp = fopen(path, "r");
  if (!tmp_fp) {
    dbg_printf("%s: can not open file '%s' for reading.\n", argv0, path);
    return(0);
  }

  if ((bx_infile_stack_index+1) >= BX_INFILE_DEPTH) {
    dbg_printf("%s: source files nested too deeply\n", argv0);
    return(0);
  }

  bx_infile_stack_index++;
  bx_infile_stack[bx_infile_stack_index].fp = tmp_fp;
  strncpy(bx_infile_stack[bx_infile_stack_index].fname, path, BX_MAX_PATH);
  bx_infile_stack[bx_infile_stack_index].fname[BX_MAX_PATH-1] = 0;
  bx_infile_stack[bx_infile_stack_index].lineno = 0;
  return(1);
}

void bx_unnest_infile(void)
{
  if (bx_infile_stack_index <= 0) {
    dbg_printf("%s: ERROR: unnest_infile(): nesting level = 0\n", argv0);
    bx_dbg_exit(1);
  }

  fclose(bx_infile_stack[bx_infile_stack_index].fp);
  bx_infile_stack_index--;
}

int bxwrap(void)
{
  dbg_printf("%s: ERROR: bxwrap() called\n", argv0);
  bx_dbg_exit(1);
  return(0); // keep compiler quiet
}

#ifdef WIN32
char* bxtext;
#endif

void bxerror(char *s)
{
  dbg_printf("%s:%d: %s at '%s'\n", bx_infile_stack[bx_infile_stack_index].fname,
    bx_infile_stack[bx_infile_stack_index].lineno, s, bxtext);

  if (bx_infile_stack_index > 0) {
    dbg_printf("%s: ERROR in source file causes exit\n", argv0);
    bx_dbg_exit(1);
  }
}

void CDECL bx_debug_ctrlc_handler(int signum)
{
  UNUSED(signum);
  if (SIM->has_debug_gui()) {
    // in a multithreaded environment, a signal such as SIGINT can be sent to all
    // threads.  This function is only intended to handle signals in the
    // simulator thread.  It will simply return if called from any other thread.
    // Otherwise the BX_PANIC() below can be called in multiple threads at
    // once, leading to multiple threads trying to display a dialog box,
    // leading to GUI deadlock.
    if (!SIM->is_sim_thread()) {
      BX_INFO(("bx_signal_handler: ignored sig %d because it wasn't called from the simulator thread", signum));
      return;
    }
  }
  BX_INFO(("Ctrl-C detected in signal handler."));

  signal(SIGINT, bx_debug_ctrlc_handler);
  bx_debug_break();
}

void bx_debug_break()
{
  bx_guard.interrupt_requested = 1;
}

void bx_dbg_exception(unsigned cpu, Bit8u vector, Bit16u error_code)
{
  static const char *exception[] = {
     "(#DE) divide by zero",
     "(#DB) debug break",
     "(#NMI)",
     "(#BP) breakpoint match",
     "(#OF) overflow",
     "(#BR) boundary check",
     "(#UD) undefined opcode",
     "(#NM) device not available",
     "(#DF) double fault",
     "(#CO) coprocessor overrun",
     "(#TS) invalid TSS",
     "(#NP) segment not present",
     "(#SS) stack fault",
     "(#GP) general protection fault",
     "(#PF) page fault",
     "(#RESERVED)",
     "(#MF) floating point error",
     "(#AC) alignment check",
     "(#MC) machine check",
     "(#XF) SIMD floating point exception",
  };

  if (BX_CPU(dbg_cpu)->trace || bx_dbg.exceptions)
  {
    if (vector <= BX_XM_EXCEPTION) {
      dbg_printf("CPU %d: Exception 0x%02x - %s occured (error_code=0x%04x)\n",
        cpu, vector, exception[vector], error_code);
    }
    else {
      dbg_printf("CPU %d: Exception 0x%02x occured (error_code=0x%04x)\n",
        cpu, vector, error_code);
    }
  }
}

void bx_dbg_interrupt(unsigned cpu, Bit8u vector, Bit16u error_code)
{
  if (BX_CPU(dbg_cpu)->trace || bx_dbg.interrupts)
  {
    dbg_printf("CPU %d: Interrupt 0x%02x occured (error_code=0x%04x)\n",
      cpu, vector, error_code);
  }
}

void bx_dbg_halt(unsigned cpu)
{
  if (BX_CPU(dbg_cpu)->trace)
  {
    dbg_printf("CPU %d: HALTED\n", cpu);
  }
}

void bx_dbg_watchpoint_continue(bx_bool watch_continue)
{
  watchpoint_continue = watch_continue;
  if (watchpoint_continue) {
     dbg_printf("Will stop on watch points\n");
  }
  else {
     dbg_printf("Will not stop on watch points (they will still be logged)\n");
  }
}

void bx_dbg_check_memory_watchpoints(unsigned cpu, bx_phy_address phy, unsigned len, unsigned rw)
{
  bx_phy_address phy_end = phy + len - 1;

  if (rw & 1) {
    // Check for physical write watch points
    for (unsigned i = 0; i < num_write_watchpoints; i++) {
      bx_phy_address watch_end = write_watchpoint[i].addr + write_watchpoint[i].len - 1;
      if (watch_end < phy || phy_end < write_watchpoint[i].addr) continue;
      BX_CPU(cpu)->watchpoint  = phy;
      BX_CPU(cpu)->break_point = BREAK_POINT_WRITE;
      break;
    }
  }
  else {
    // Check for physical read watch points
    for (unsigned i = 0; i < num_read_watchpoints; i++) {
      bx_phy_address watch_end = read_watchpoint[i].addr + read_watchpoint[i].len - 1;
      if (watch_end < phy || phy_end < read_watchpoint[i].addr) continue;
      BX_CPU(cpu)->watchpoint  = phy;
      BX_CPU(cpu)->break_point = BREAK_POINT_READ;
      break;
    }
  }
}

void bx_dbg_lin_memory_access(unsigned cpu, bx_address lin, bx_phy_address phy, unsigned len, unsigned pl, unsigned rw, Bit8u *data)
{
  bx_dbg_check_memory_watchpoints(cpu, phy, len, rw);

  if (! BX_CPU(cpu)->trace_mem)
    return;

  dbg_printf("[CPU%d %s]: LIN 0x" FMT_ADDRX " PHY 0x" FMT_PHY_ADDRX " (len=%d, pl=%d)",
     cpu, 
     (rw == BX_WRITE) ? "WR" : "RD",
     lin, phy,
     len, pl);

  if (len == 1) {
     Bit8u val8 = *data;
     dbg_printf(": 0x%02X", (unsigned) val8);
  }
  else if (len == 2) {
     Bit16u val16 = *((Bit16u*) data);
     dbg_printf(": 0x%04X", (unsigned) val16);
  }
  else if (len == 4) {
     Bit32u val32 = *((Bit32u*) data);
     dbg_printf(": 0x%08X", (unsigned) val32);
  }
  else if (len == 8) {
     Bit64u data64 = * (Bit64u*)(data);
     dbg_printf(": 0x%08X 0x%08X", GET32H(data64), GET32L(data64));
  }
#if BX_CPU_LEVEL >= 6
  else if (len == 16) {
     const BxPackedXmmRegister *xmmdata = (const BxPackedXmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X",
         xmmdata->xmm32u(3), xmmdata->xmm32u(2), xmmdata->xmm32u(1), xmmdata->xmm32u(0));
  }
#if BX_SUPPORT_AVX
  else if (len == 32) {
     const BxPackedYmmRegister *ymmdata = (const BxPackedYmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X",
        ymmdata->ymm32u(7), ymmdata->ymm32u(6), ymmdata->ymm32u(5), ymmdata->ymm32u(4),
        ymmdata->ymm32u(3), ymmdata->ymm32u(2), ymmdata->ymm32u(1), ymmdata->ymm32u(0));
  }
#endif
#if BX_SUPPORT_EVEX
  else if (len == 64) {
     const BxPackedZmmRegister *zmmdata = (const BxPackedZmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X",
        zmmdata->zmm32u(15), zmmdata->zmm32u(14), zmmdata->zmm32u(13), zmmdata->zmm32u(12),
        zmmdata->zmm32u(11), zmmdata->zmm32u(10), zmmdata->zmm32u(9),  zmmdata->zmm32u(8),
        zmmdata->zmm32u(7),  zmmdata->zmm32u(6),  zmmdata->zmm32u(5),  zmmdata->zmm32u(4),
        zmmdata->zmm32u(3),  zmmdata->zmm32u(2),  zmmdata->zmm32u(1),  zmmdata->zmm32u(0));
  }
#endif
#endif
  else {
     for (int i=len-1;i >= 0;i--) {
        dbg_printf(" %02x", data[i]);
     }
  }

  dbg_printf("\n");
}

void bx_dbg_phy_memory_access(unsigned cpu, bx_phy_address phy, unsigned len, unsigned rw, unsigned access, Bit8u *data)
{
  bx_dbg_check_memory_watchpoints(cpu, phy, len, rw);

  if (! BX_CPU(cpu)->trace_mem)
    return;

  static const char *access_string[] = {
    "",
    "PDPTR0",
    "PDPTR1",
    "PDPTR2",
    "PDPTR3",
    "PTE",
    "PDE",
    "PDPTE",
    "PML4E",
    "EPT PTE",
    "EPT PDE",
    "EPT PDPTE",
    "EPT PML4E",
    "VMCS",
    "SHADOW_VMCS",
    "MSR BITMAP",
    "I/O BITMAP",
    "VMREAD BITMAP",
    "VMWRITE BITMAP",
    "VMX LDMSR",
    "VMX STMSR",
    "VAPIC",
    "SMRAM"
  };

  dbg_printf("[CPU%d %s]: PHY 0x" FMT_PHY_ADDRX " (len=%d)",
     cpu, 
     (rw == BX_WRITE) ? "WR" : "RD",
     phy,
     len);

  if (len == 1) {
     Bit8u val8 = *data;
     dbg_printf(": 0x%02X", (unsigned) val8);
  }
  else if (len == 2) {
     Bit16u val16 = *((Bit16u*) data);
     dbg_printf(": 0x%04X", (unsigned) val16);
  }
  else if (len == 4) {
     Bit32u val32 = *((Bit32u*) data);
     dbg_printf(": 0x%08X", (unsigned) val32);
  }
  else if (len == 8) {
     Bit64u data64 = * (Bit64u*)(data);
     dbg_printf(": 0x%08X 0x%08X", GET32H(data64), GET32L(data64));
  }
#if BX_CPU_LEVEL >= 6
  else if (len == 16) {
     const BxPackedXmmRegister *xmmdata = (const BxPackedXmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X",
         xmmdata->xmm32u(3), xmmdata->xmm32u(2), xmmdata->xmm32u(1), xmmdata->xmm32u(0));
  }
#if BX_SUPPORT_AVX
  else if (len == 32) {
     const BxPackedYmmRegister *ymmdata = (const BxPackedYmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X",
        ymmdata->ymm32u(7), ymmdata->ymm32u(6), ymmdata->ymm32u(5), ymmdata->ymm32u(4),
        ymmdata->ymm32u(3), ymmdata->ymm32u(2), ymmdata->ymm32u(1), ymmdata->ymm32u(0));
  }
#endif
#if BX_SUPPORT_EVEX
  else if (len == 64) {
     const BxPackedZmmRegister *zmmdata = (const BxPackedZmmRegister*)(data);
     dbg_printf(": 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X",
        zmmdata->zmm32u(15), zmmdata->zmm32u(14), zmmdata->zmm32u(13), zmmdata->zmm32u(12),
        zmmdata->zmm32u(11), zmmdata->zmm32u(10), zmmdata->zmm32u(9),  zmmdata->zmm32u(8),
        zmmdata->zmm32u(7),  zmmdata->zmm32u(6),  zmmdata->zmm32u(5),  zmmdata->zmm32u(4),
        zmmdata->zmm32u(3),  zmmdata->zmm32u(2),  zmmdata->zmm32u(1),  zmmdata->zmm32u(0));
  }
#endif
#endif
  else {
     for (int i=len-1;i >= 0;i--) {
        dbg_printf(" %02x", data[i]);
     }
  }

  if (access != 0)
    dbg_printf("\t; %s\n", access_string[access]);
  else
    dbg_printf("\n");
}

void bx_dbg_exit(int code)
{
  if (bx_dbg_exit_called) return;
  bx_dbg_exit_called = 1;
  BX_DEBUG(("dbg: before exit"));
  for (int cpu=0; cpu < BX_SMP_PROCESSORS; cpu++) {
    if (BX_CPU(cpu)) BX_CPU(cpu)->atexit();
  }

  bx_atexit();
  BX_EXIT(code);
}

//
// functions for browsing of cpu state
//

void bx_dbg_print_mxcsr_state(void)
{
#if BX_CPU_LEVEL >= 6
  Bit32u mxcsr = SIM->get_param_num("SSE.mxcsr", dbg_cpu_list)->get();

  static const char* round_control[] = {
    "Nearest", "Down", "Up", "Chop"
  };

  dbg_printf("MXCSR: 0x%05x: %s %s RC:%s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", mxcsr,
     (mxcsr & (1<<17)) ? "ULE" : "ule",
     (mxcsr & (1<<15)) ? "FUZ" : "fuz",
     round_control[(mxcsr >> 13) & 3],
     (mxcsr & (1<<12)) ? "PM" : "pm",
     (mxcsr & (1<<11)) ? "UM" : "um",
     (mxcsr & (1<<10)) ? "OM" : "om",
     (mxcsr & (1<<9)) ? "ZM" : "zm",
     (mxcsr & (1<<8)) ? "DM" : "dm",
     (mxcsr & (1<<7)) ? "IM" : "im",
     (mxcsr & (1<<6)) ? "DAZ" : "daz",
     (mxcsr & (1<<5)) ? "PE" : "pe",
     (mxcsr & (1<<4)) ? "UE" : "ue",
     (mxcsr & (1<<3)) ? "OE" : "oe",
     (mxcsr & (1<<2)) ? "ZE" : "ze",
     (mxcsr & (1<<1)) ? "DE" : "de",
     (mxcsr & (1<<0)) ? "IE" : "ie");
#endif
}

void bx_dbg_print_sse_state(void)
{
#if BX_CPU_LEVEL >= 6
  Bit64u isa_extensions_bitmask = SIM->get_param_num("isa_extensions_bitmask", dbg_cpu_list)->get64();

  if ((isa_extensions_bitmask & BX_ISA_SSE) != 0) {
    bx_dbg_print_mxcsr_state();

    char param_name[20];
    for(unsigned i=0;i</*BX_XMM_REGISTERS*/16;i++) {
      sprintf(param_name, "SSE.xmm%02d_1", i);
      Bit64u hi = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
      sprintf(param_name, "SSE.xmm%02d_0", i);
      Bit64u lo = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
      dbg_printf("XMM[%02u]: %08x_%08x_%08x_%08x\n", i,
         GET32H(hi), GET32L(hi), GET32H(lo), GET32L(lo));
    }
  }
  else
#endif
  {
    dbg_printf("The CPU doesn't support SSE state !\n");
  }
}

void bx_dbg_print_avx_state(unsigned vlen)
{
#if BX_SUPPORT_AVX
  Bit64u isa_extensions_bitmask = SIM->get_param_num("isa_extensions_bitmask", dbg_cpu_list)->get64();
  char param_name[20];

  if ((isa_extensions_bitmask & BX_ISA_AVX) != 0) {
    bx_dbg_print_mxcsr_state();

    unsigned num_regs = 16;
#if BX_SUPPORT_EVEX
    if ((isa_extensions_bitmask & BX_ISA_AVX512) != 0)
      num_regs = BX_XMM_REGISTERS;
    else
      vlen = BX_VL256;
#endif

    for(unsigned i=0;i<num_regs;i++) {
      dbg_printf("VMM[%02u]: ", i);
      for (int j=vlen-1;j >= 0; j--) {
        sprintf(param_name, "SSE.xmm%02d_%d", i, j*2+1);
        Bit64u hi = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
        sprintf(param_name, "SSE.xmm%02d_%d", i, j*2);
        Bit64u lo = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
        dbg_printf("%08x_%08x_%08x_%08x", GET32H(hi), GET32L(hi), GET32H(lo), GET32L(lo));
        if (j!=0) dbg_printf("_");
      }
      dbg_printf("\n");
    }
  }
  else
#endif
  {
    dbg_printf("The CPU doesn't support AVX state !\n");
  }

#if BX_SUPPORT_EVEX
  if ((isa_extensions_bitmask & BX_ISA_AVX512) != 0) {
    for(unsigned i=0;i<8;i++) {
      sprintf(param_name, "OPMASK.k%d", i);
      Bit64u opmask = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
      dbg_printf("K%d: %08x_%08x\n", i, GET32H(opmask), GET32L(opmask));
    }
  }
#endif
}

void bx_dbg_print_mmx_state(void)
{
#if BX_CPU_LEVEL >= 5
  Bit64u isa_extensions_bitmask = SIM->get_param_num("isa_extensions_bitmask", dbg_cpu_list)->get64();

  if ((isa_extensions_bitmask & BX_ISA_MMX) != 0) {
    char param_name[20];
    for(unsigned i=0;i<8;i++) {
      sprintf(param_name, "FPU.st%d.fraction", i);
      Bit64u mmreg = SIM->get_param_num(param_name, dbg_cpu_list)->get64();
      dbg_printf("MM[%d]: %08x_%08x\n", i, GET32H(mmreg), GET32L(mmreg));
    }
  }
  else
#endif
  {
    dbg_printf("The CPU doesn't support MMX state !\n");
  }
}

void bx_dbg_print_fpu_state(void)
{
#if BX_SUPPORT_FPU
  BX_CPU(dbg_cpu)->print_state_FPU();
#else
  dbg_printf("The CPU doesn't support FPU state !\n");
#endif
}

void bx_dbg_info_flags(void)
{
  dbg_printf("%s %s %s %s %s %s %s IOPL=%1u %s %s %s %s %s %s %s %s %s\n",
    BX_CPU(dbg_cpu)->get_ID() ? "ID" : "id",
    BX_CPU(dbg_cpu)->get_VIP() ? "VIP" : "vip",
    BX_CPU(dbg_cpu)->get_VIF() ? "VIF" : "vif",
    BX_CPU(dbg_cpu)->get_AC() ? "AC" : "ac",
    BX_CPU(dbg_cpu)->get_VM() ? "VM" : "vm",
    BX_CPU(dbg_cpu)->get_RF() ? "RF" : "rf",
    BX_CPU(dbg_cpu)->get_NT() ? "NT" : "nt",
    BX_CPU(dbg_cpu)->get_IOPL(),
    BX_CPU(dbg_cpu)->get_OF() ? "OF" : "of",
    BX_CPU(dbg_cpu)->get_DF() ? "DF" : "df",
    BX_CPU(dbg_cpu)->get_IF() ? "IF" : "if",
    BX_CPU(dbg_cpu)->get_TF() ? "TF" : "tf",
    BX_CPU(dbg_cpu)->get_SF() ? "SF" : "sf",
    BX_CPU(dbg_cpu)->get_ZF() ? "ZF" : "zf",
    BX_CPU(dbg_cpu)->get_AF() ? "AF" : "af",
    BX_CPU(dbg_cpu)->get_PF() ? "PF" : "pf",
    BX_CPU(dbg_cpu)->get_CF() ? "CF" : "cf");
}

void bx_dbg_info_debug_regs_command(void)
{
  bx_address dr0 = SIM->get_param_num("DR0", dbg_cpu_list)->get64();
  bx_address dr1 = SIM->get_param_num("DR1", dbg_cpu_list)->get64();
  bx_address dr2 = SIM->get_param_num("DR2", dbg_cpu_list)->get64();
  bx_address dr3 = SIM->get_param_num("DR3", dbg_cpu_list)->get64();
  Bit32u dr6 = SIM->get_param_num("DR6", dbg_cpu_list)->get();
  Bit32u dr7 = SIM->get_param_num("DR7", dbg_cpu_list)->get();

  dbg_printf("DR0=0x" FMT_ADDRX "\n", dr0);
  dbg_printf("DR1=0x" FMT_ADDRX "\n", dr1);
  dbg_printf("DR2=0x" FMT_ADDRX "\n", dr2);
  dbg_printf("DR3=0x" FMT_ADDRX "\n", dr3);

  static const char *dr_ln[4] = { "Byte", "Word", "QWord", "Dword" };
  static const char *dr_rw[4] = { "Code", "DataW", "I/O", "DataRW" };

  dbg_printf("DR6=0x%08x: %s %s %s %s %s %s %s\n", dr6,
      (dr6 & (1<<15)) ? "BT" : "bt",
      (dr6 & (1<<14)) ? "BS" : "bs",
      (dr6 & (1<<13)) ? "BD" : "bd",
      (dr6 & (1<<3)) ? "B3" : "b3",
      (dr6 & (1<<2)) ? "B2" : "b2",
      (dr6 & (1<<1)) ? "B1" : "b1",
      (dr6 & (1<<0)) ? "B0" : "b0");

  dbg_printf("DR7=0x%08x: DR3=%s-%s DR2=%s-%s DR1=%s-%s DR0=%s-%s %s | %s %s | %s %s %s %s %s %s %s %s\n", dr7,
      dr_rw[(dr7 >> 28) & 3], dr_ln[(dr7 >> 30) & 3],
      dr_rw[(dr7 >> 24) & 3], dr_ln[(dr7 >> 26) & 3],
      dr_rw[(dr7 >> 20) & 3], dr_ln[(dr7 >> 22) & 3],
      dr_rw[(dr7 >> 16) & 3], dr_ln[(dr7 >> 18) & 3],
      (dr7 & (1<<13)) ? "GD" : "gd",
      (dr7 & (1<<9)) ? "GE" : "ge",
      (dr7 & (1<<8)) ? "LE" : "le",
      (dr7 & (1<<7)) ? "G3" : "g3",
      (dr7 & (1<<6)) ? "L3" : "l3",
      (dr7 & (1<<5)) ? "G2" : "g2",
      (dr7 & (1<<4)) ? "L2" : "l2",
      (dr7 & (1<<3)) ? "G1" : "g1",
      (dr7 & (1<<2)) ? "L1" : "l1",
      (dr7 & (1<<1)) ? "G0" : "g0",
      (dr7 & (1<<0)) ? "L0" : "l0");
}

void bx_dbg_info_control_regs_command(void)
{
  Bit32u cr0 = SIM->get_param_num("CR0", dbg_cpu_list)->get();
  bx_address cr2 = (bx_address) SIM->get_param_num("CR2", dbg_cpu_list)->get64();
  bx_phy_address cr3 = (bx_phy_address) SIM->get_param_num("CR3", dbg_cpu_list)->get64();
  dbg_printf("CR0=0x%08x: %s %s %s %s %s %s %s %s %s %s %s\n", cr0,
    (cr0 & (1<<31)) ? "PG" : "pg",
    (cr0 & (1<<30)) ? "CD" : "cd",
    (cr0 & (1<<29)) ? "NW" : "nw",
    (cr0 & (1<<18)) ? "AC" : "ac",
    (cr0 & (1<<16)) ? "WP" : "wp",
    (cr0 & (1<<5))  ? "NE" : "ne",
    (cr0 & (1<<4))  ? "ET" : "et",
    (cr0 & (1<<3))  ? "TS" : "ts",
    (cr0 & (1<<2))  ? "EM" : "em",
    (cr0 & (1<<1))  ? "MP" : "mp",
    (cr0 & (1<<0))  ? "PE" : "pe");
  dbg_printf("CR2=page fault laddr=0x" FMT_ADDRX "\n", cr2);
  dbg_printf("CR3=0x" FMT_PHY_ADDRX "\n", cr3);
  dbg_printf("    PCD=page-level cache disable=%d\n", (cr3>>4) & 1);
  dbg_printf("    PWT=page-level write-through=%d\n", (cr3>>3) & 1);
#if BX_CPU_LEVEL >= 5
  Bit32u cr4 = SIM->get_param_num("CR4", dbg_cpu_list)->get();
  dbg_printf("CR4=0x%08x: %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", cr4,
    (cr4 & (1<<21)) ? "SMAP" : "smap",
    (cr4 & (1<<20)) ? "SMEP" : "smep",
    (cr4 & (1<<18)) ? "OSXSAVE" : "osxsave",
    (cr4 & (1<<17)) ? "PCID" : "pcid",
    (cr4 & (1<<16)) ? "FSGSBASE" : "fsgsbase",
    (cr4 & (1<<14)) ? "SMX" : "smx",
    (cr4 & (1<<13)) ? "VMX" : "vmx",
    (cr4 & (1<<10)) ? "OSXMMEXCPT" : "osxmmexcpt",
    (cr4 & (1<<9))  ? "OSFXSR" : "osfxsr",
    (cr4 & (1<<8))  ? "PCE" : "pce",
    (cr4 & (1<<7))  ? "PGE" : "pge",
    (cr4 & (1<<6))  ? "MCE" : "mce",
    (cr4 & (1<<5))  ? "PAE" : "pae",
    (cr4 & (1<<4))  ? "PSE" : "pse",
    (cr4 & (1<<3))  ? "DE" : "de",
    (cr4 & (1<<2))  ? "TSD" : "tsd",
    (cr4 & (1<<1))  ? "PVI" : "pvi",
    (cr4 & (1<<0))  ? "VME" : "vme");
#if BX_SUPPORT_X86_64
  Bit64u cpu_extensions_bitmask = SIM->get_param_num("cpu_extensions_bitmask", dbg_cpu_list)->get();
  if ((cpu_extensions_bitmask & BX_CPU_LONG_MODE) != 0) {
    dbg_printf("CR8: 0x%x\n", BX_CPU(dbg_cpu)->get_cr8());
  }
#endif
  Bit32u efer = SIM->get_param_num("MSR.EFER", dbg_cpu_list)->get();
  dbg_printf("EFER=0x%08x: %s %s %s %s %s\n", efer,
    (efer & (1<<14)) ? "FFXSR" : "ffxsr",
    (efer & (1<<11)) ? "NXE" : "nxe",
    (efer & (1<<10)) ? "LMA" : "lma",
    (efer & (1<<8))  ? "LME" : "lme",
    (efer & (1<<0))  ? "SCE" : "sce");
#endif
#if BX_CPU_LEVEL >= 6
  Bit64u isa_extensions_bitmask = SIM->get_param_num("isa_extensions_bitmask", dbg_cpu_list)->get64();
  if ((isa_extensions_bitmask & BX_ISA_XSAVE) != 0) {
    Bit32u xcr0 = SIM->get_param_num("XCR0", dbg_cpu_list)->get();
    dbg_printf("XCR0=0x%08x: %s %s %s %s %s %s %s %s\n", xcr0,
      (xcr0 & (1<<7)) ? "HI_ZMM" : "hi_zmm",
      (xcr0 & (1<<6)) ? "ZMM_HI256" : "zmm_hi256",
      (xcr0 & (1<<5)) ? "OPMASK" : "opmask",
      (xcr0 & (1<<4)) ? "BNDCFG" : "bndcfg",
      (xcr0 & (1<<3)) ? "BNDREGS" : "bndregs",
      (xcr0 & (1<<2)) ? "YMM" : "ymm",
      (xcr0 & (1<<1)) ? "SSE" : "sse",
      (xcr0 & (1<<0)) ? "FPU" : "fpu");
  }
#endif
}

void bx_dbg_info_segment_regs_command(void)
{
  static const char *segname[] = { "es", "cs", "ss", "ds", "fs", "gs" };

  bx_dbg_sreg_t sreg;
  bx_dbg_global_sreg_t global_sreg;

  for(int s=0;s<6;s++) {
    BX_CPU(dbg_cpu)->dbg_get_sreg(&sreg, s);
    dbg_printf("%s:0x%04x, dh=0x%08x, dl=0x%08x, valid=%u\n", segname[s],
       (unsigned) sreg.sel, (unsigned) sreg.des_h,
       (unsigned) sreg.des_l, (unsigned) sreg.valid);
    if (sreg.valid) {
       dbg_printf("\t");
       bx_dbg_print_descriptor(sreg.des_l, sreg.des_h);
    }
  }

  BX_CPU(dbg_cpu)->dbg_get_ldtr(&sreg);
  dbg_printf("ldtr:0x%04x, dh=0x%08x, dl=0x%08x, valid=%u\n",
      (unsigned) sreg.sel, (unsigned) sreg.des_h,
      (unsigned) sreg.des_l, (unsigned) sreg.valid);

  BX_CPU(dbg_cpu)->dbg_get_tr(&sreg);
  dbg_printf("tr:0x%04x, dh=0x%08x, dl=0x%08x, valid=%u\n",
      (unsigned) sreg.sel, (unsigned) sreg.des_h,
      (unsigned) sreg.des_l, (unsigned) sreg.valid);

  BX_CPU(dbg_cpu)->dbg_get_gdtr(&global_sreg);
  dbg_printf("gdtr:base=0x"FMT_ADDRX", limit=0x%x\n",
      global_sreg.base, (unsigned) global_sreg.limit);

  BX_CPU(dbg_cpu)->dbg_get_idtr(&global_sreg);
  dbg_printf("idtr:base=0x"FMT_ADDRX", limit=0x%x\n",
      global_sreg.base, (unsigned) global_sreg.limit);
}

void bx_dbg_info_registers_command(int which_regs_mask)
{
  bx_address reg;

  if (which_regs_mask & BX_INFO_GENERAL_PURPOSE_REGS) {
#if BX_SUPPORT_SMP
    dbg_printf("CPU%d:\n", BX_CPU(dbg_cpu)->bx_cpuid);
#endif
#if BX_SUPPORT_X86_64 == 0
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EAX);
    dbg_printf("eax: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_ECX);
    dbg_printf("ecx: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EDX);
    dbg_printf("edx: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EBX);
    dbg_printf("ebx: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_ESP);
    dbg_printf("esp: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EBP);
    dbg_printf("ebp: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_ESI);
    dbg_printf("esi: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EDI);
    dbg_printf("edi: 0x%08x %d\n", (unsigned) reg, (int) reg);
    reg = bx_dbg_get_eip();
    dbg_printf("eip: 0x%08x\n", (unsigned) reg);
#else
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RAX);
    dbg_printf("rax: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RCX);
    dbg_printf("rcx: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RDX);
    dbg_printf("rdx: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RBX);
    dbg_printf("rbx: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RSP);
    dbg_printf("rsp: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RBP);
    dbg_printf("rbp: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RSI);
    dbg_printf("rsi: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RDI);
    dbg_printf("rdi: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R8);
    dbg_printf("r8 : %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R9);
    dbg_printf("r9 : %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R10);
    dbg_printf("r10: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R11);
    dbg_printf("r11: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R12);
    dbg_printf("r12: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R13);
    dbg_printf("r13: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R14);
    dbg_printf("r14: %08x_%08x ", GET32H(reg), GET32L(reg));
    reg = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_R15);
    dbg_printf("r15: %08x_%08x\n", GET32H(reg), GET32L(reg));
    reg = bx_dbg_get_instruction_pointer();
    dbg_printf("rip: %08x_%08x\n", GET32H(reg), GET32L(reg));
#endif
    reg = BX_CPU(dbg_cpu)->read_eflags();
    dbg_printf("eflags 0x%08x: ", (unsigned) reg);
    bx_dbg_info_flags();
  }

#if BX_SUPPORT_FPU
  if (which_regs_mask & BX_INFO_FPU_REGS) {
    bx_dbg_print_fpu_state();
  }
#endif

  if (which_regs_mask & BX_INFO_MMX_REGS) {
    bx_dbg_print_mmx_state();
  }

#if BX_SUPPORT_EVEX
  if (which_regs_mask & BX_INFO_ZMM_REGS) {
    bx_dbg_print_avx_state(BX_VL512);
  }
  else
#endif
  {
#if BX_SUPPORT_AVX
    if (which_regs_mask & BX_INFO_YMM_REGS) {
      bx_dbg_print_avx_state(BX_VL256);
    }
    else
#endif
    {
      if (which_regs_mask & BX_INFO_SSE_REGS) {
        bx_dbg_print_sse_state();
      }
    }
  }
}

//
// commands invoked from parser
//

void bx_dbg_quit_command(void)
{
  BX_INFO(("dbg: Quit"));
  bx_dbg_exit(0);
}

void bx_dbg_trace_command(bx_bool enable)
{
  BX_CPU(dbg_cpu)->trace = enable;
  dbg_printf("Tracing %s for CPU%d\n", enable ? "enabled" : "disabled",
     BX_CPU(dbg_cpu)->which_cpu());
}

void bx_dbg_trace_reg_command(bx_bool enable)
{
  BX_CPU(dbg_cpu)->trace_reg = enable;
  dbg_printf("Register-Tracing %s for CPU%d\n", enable ? "enabled" : "disabled",
     BX_CPU(dbg_cpu)->which_cpu());
}

void bx_dbg_trace_mem_command(bx_bool enable)
{
  BX_CPU(dbg_cpu)->trace_mem = enable;
  dbg_printf("Memory-Tracing %s for CPU%d\n", enable ? "enabled" : "disabled",
     BX_CPU(dbg_cpu)->which_cpu());
}

void bx_dbg_ptime_command(void)
{
  dbg_printf("ptime: " FMT_LL "d\n", bx_pc_system.time_ticks());
}

int timebp_timer = -1;
Bit64u timebp_queue[MAX_CONCURRENT_BPS];
int timebp_queue_size = 0;

void bx_dbg_timebp_command(bx_bool absolute, Bit64u time)
{
  Bit64u abs_time = (absolute) ? time : time + bx_pc_system.time_ticks();

  if (abs_time <= bx_pc_system.time_ticks()) {
    dbg_printf("Request for time break point in the past. I can't let you do that.\n");
    return;
  }

  if (timebp_queue_size == MAX_CONCURRENT_BPS) {
    dbg_printf("Too many time break points\n");
    return;
  }

  Bit64u diff = (absolute) ? time - bx_pc_system.time_ticks() : time;

  if (timebp_timer >= 0) {
    if (timebp_queue_size == 0 || abs_time < timebp_queue[0]) {
      /* first in queue */
      for (int i = timebp_queue_size; i >= 0; i--)
        timebp_queue[i+1] = timebp_queue[i];
      timebp_queue[0] = abs_time;
      timebp_queue_size++;
      bx_pc_system.activate_timer_ticks(timebp_timer, diff, 1);
    } else {
      /* not first, insert at suitable place */
      for (int i = 1; i < timebp_queue_size; i++) {
        if (timebp_queue[i] == abs_time) {
          dbg_printf("Time breakpoint not inserted (duplicate)\n");
          return;
        } else if (abs_time < timebp_queue[i]) {
          for (int j = timebp_queue_size; j >= i; j--)
            timebp_queue[j+1] = timebp_queue[j];
          timebp_queue[i] = abs_time;
          goto inserted;
        }
      }
      /* last */
      timebp_queue[timebp_queue_size] = abs_time;
inserted:
      timebp_queue_size++;
    }
  } else {
    timebp_queue_size = 1;
    timebp_queue[0] = abs_time;
    timebp_timer = bx_pc_system.register_timer_ticks(&bx_pc_system, bx_pc_system_c::timebp_handler, diff, 0, 1, "debug.timebp");
  }

  dbg_printf("Time breakpoint inserted. Delta = " FMT_LL "u\n", diff);
}

Bit32u conv_4xBit8u_to_Bit32u(const Bit8u* buf)
{
  Bit32u ret = 0;
  for (int i = 0; i < 4; i++) {
    ret |= (buf[i] << (8 * i));
  }
  return ret;
}

Bit16u conv_2xBit8u_to_Bit16u(const Bit8u* buf)
{
  Bit16u ret = 0;
  for (int i = 0; i < 2; i++) {
    ret |= (buf[i] << (8 * i));
  }
  return ret;
}

// toggles mode switch breakpoint
void bx_dbg_modebp_command()
{
  BX_CPU(dbg_cpu)->mode_break = !BX_CPU(dbg_cpu)->mode_break;
  dbg_printf("mode switch break %s\n",
    BX_CPU(dbg_cpu)->mode_break ? "enabled" : "disabled");
}

// toggles vmexit switch breakpoint
void bx_dbg_vmexitbp_command()
{
#if BX_SUPPORT_VMX
  BX_CPU(dbg_cpu)->vmexit_break = !BX_CPU(dbg_cpu)->vmexit_break;
  dbg_printf("vmexit switch break %s\n",
    BX_CPU(dbg_cpu)->vmexit_break ? "enabled" : "disabled");
#else
  dbg_printf("VMX is not compiled in, cannot set vmexit breakpoint !");
#endif
}

bx_bool bx_dbg_read_linear(unsigned which_cpu, bx_address laddr, unsigned len, Bit8u *buf)
{
  unsigned remainsInPage;
  bx_phy_address paddr;
  unsigned read_len;
  bx_bool paddr_valid;

next_page:
  remainsInPage = 0x1000 - PAGE_OFFSET(laddr);
  read_len = (remainsInPage < len) ? remainsInPage : len;

  paddr_valid = BX_CPU(which_cpu)->dbg_xlate_linear2phy(laddr, &paddr);
  if (paddr_valid) {
    if (! BX_MEM(0)->dbg_fetch_mem(BX_CPU(which_cpu), paddr, read_len, buf)) {
      dbg_printf("bx_dbg_read_linear: physical memory read error (phy=0x" FMT_PHY_ADDRX ", lin=0x" FMT_ADDRX ")\n", paddr, laddr);
      return 0;
    }
  }
  else {
    dbg_printf("bx_dbg_read_linear: physical address not available for linear 0x" FMT_ADDRX "\n", laddr);
    return 0;
  }

  /* check for access across multiple pages */
  if (remainsInPage < len)
  {
    laddr += read_len;
    len -= read_len;
    buf += read_len;
    goto next_page;
  }

  return 1;
}

// where
// stack trace: ebp -> old ebp
// return eip at ebp + 4
void bx_dbg_where_command()
{
  if (!BX_CPU(dbg_cpu)->protected_mode()) {
    dbg_printf("'where' only supported in protected mode\n");
    return;
  }
  if (BX_CPU(dbg_cpu)->get_segment_base(BX_SEG_REG_SS) != 0) {
    dbg_printf("non-zero stack base\n");
    return;
  }
  Bit32u bp = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_EBP);
  bx_address ip = BX_CPU(dbg_cpu)->get_instruction_pointer();
  dbg_printf("(%d) 0x%08x\n", dbg_cpu, ip);
  for (int i = 1; i < 50; i++) {
    // Up
    bx_phy_address paddr;
    Bit8u buf[4];

    // bp = [bp];
    bx_bool paddr_valid = BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(bp, &paddr);
    if (paddr_valid) {
      if (BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), paddr, 4, buf)) {
        bp = conv_4xBit8u_to_Bit32u(buf);
      } else {
        dbg_printf("(%d) Physical memory read error (BP)\n", i);
        break;
      }
    } else {
      dbg_printf("(%d) Could not translate linear address (BP)\n", i);
      break;
    }

    // ip = [bp + 4];
    paddr_valid = BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(bp + 4, &paddr);
    if (paddr_valid) {
      if (BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), paddr, 4, buf)) {
        ip = conv_4xBit8u_to_Bit32u(buf);
      } else {
        dbg_printf("(%d) Physical memory read error (IP)\n", i);
        break;
      }
    } else {
      dbg_printf("(%d) Could not translate linear address (IP)\n", i);
      break;
    }

    // Print
    dbg_printf("(%d) 0x%08x\n", i, ip);
  }
}

void bx_dbg_print_string_command(bx_address start_addr)
{
  dbg_printf("0x%08x: ", start_addr);
  for (int i = 0; ; i++) {
    Bit8u buf = 0;
    if (! bx_dbg_read_linear(dbg_cpu, start_addr+i, 1, &buf)) break;
    if (buf == 0) break;
    if (isgraph(buf) || buf == 0x20)
      dbg_printf("%c", buf);
    else
      dbg_printf("\\%d", buf);
  }
  dbg_printf("\n");
}

static void dbg_print_guard_found(unsigned cpu_mode, Bit32u cs, bx_address eip, bx_address laddr)
{
#if BX_SUPPORT_X86_64
  if (cpu_mode == BX_MODE_LONG_64) {
    dbg_printf("0x%04x:" FMT_ADDRX " (0x" FMT_ADDRX ")", cs, eip, laddr);
    return;
  }
#endif

  if (cpu_mode >= BX_MODE_IA32_PROTECTED)
    dbg_printf("%04x:%08x (0x%08x)", cs, (unsigned) eip, (unsigned) laddr);
  else // real or v8086 mode
    dbg_printf("%04x:%04x (0x%08x)", cs, (unsigned) eip, (unsigned) laddr);
}

void bx_dbg_xlate_address(bx_lin_address laddr)
{
  bx_phy_address paddr;
  laddr &= BX_CONST64(0xfffffffffffff000);

  bx_bool paddr_valid = BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(laddr, &paddr, 1);
  if (paddr_valid) {
    dbg_printf("linear page 0x" FMT_ADDRX " maps to physical page 0x" FMT_PHY_ADDRX "\n", laddr, paddr);
  }
  else {
    dbg_printf("physical address not available for linear 0x" FMT_ADDRX "\n", laddr);
  }
}

void bx_dbg_tlb_lookup(bx_lin_address laddr)
{
  Bit32u index = BX_TLB_INDEX_OF(laddr, 0);
  char cpu_param_name[16];
  sprintf(cpu_param_name, "TLB.entry%d", index);
  bx_dbg_show_param_command(cpu_param_name);
}

unsigned dbg_show_mask = 0;

#define BX_DBG_SHOW_CALLRET  (Flag_call|Flag_ret)
#define BX_DBG_SHOW_SOFTINT  (Flag_softint)
#define BX_DBG_SHOW_EXTINT   (Flag_intsig)
#define BX_DBG_SHOW_IRET     (Flag_iret)
#define BX_DBG_SHOW_INT      (Flag_softint|Flag_iret|Flag_intsig)
#define BX_DBG_SHOW_MODE     (Flag_mode)

void bx_dbg_show_command(const char* arg)
{
  if(arg) {
    if (!strcmp(arg, "mode")) {
      if (dbg_show_mask & BX_DBG_SHOW_MODE) {
        dbg_show_mask &= ~BX_DBG_SHOW_MODE;
        dbg_printf("show mode switch: OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_MODE;
        dbg_printf("show mode switch: ON\n");
      }
    } else if (!strcmp(arg, "int")) {
      if (dbg_show_mask & BX_DBG_SHOW_INT) {
        dbg_show_mask &= ~BX_DBG_SHOW_INT;
        dbg_printf("show interrupts tracing (extint/softint/iret): OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_INT;
        dbg_printf("show interrupts tracing (extint/softint/iret): ON\n");
      }
    } else if (!strcmp(arg, "extint")) {
      if (dbg_show_mask & BX_DBG_SHOW_EXTINT) {
        dbg_show_mask &= ~BX_DBG_SHOW_EXTINT;
        dbg_printf("show external interrupts: OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_EXTINT;
        dbg_printf("show external interrupts: ON\n");
      }
    } else if (!strcmp(arg, "softint")) {
      if (dbg_show_mask & BX_DBG_SHOW_SOFTINT) {
        dbg_show_mask &= ~BX_DBG_SHOW_SOFTINT;
        dbg_printf("show software interrupts: OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_SOFTINT;
        dbg_printf("show software interrupts: ON\n");
      }
    } else if (!strcmp(arg, "iret")) {
      if (dbg_show_mask & BX_DBG_SHOW_IRET) {
        dbg_show_mask &= ~BX_DBG_SHOW_IRET;
        dbg_printf("show iret: OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_IRET;
        dbg_printf("show iret: ON\n");
      }
    } else if(!strcmp(arg,"call")) {
      if (dbg_show_mask & BX_DBG_SHOW_CALLRET) {
        dbg_show_mask &= ~BX_DBG_SHOW_CALLRET;
        dbg_printf("show calls/returns: OFF\n");
      } else {
        dbg_show_mask |= BX_DBG_SHOW_CALLRET;
        dbg_printf("show calls/returns: ON\n");
      }
    } else if(!strcmp(arg,"off")) {
      dbg_show_mask = 0x0;
      dbg_printf("Disable all show flags\n");
    } else if(!strcmp(arg,"dbg-all")) {
      bx_dbg.interrupts = 1;
      bx_dbg.exceptions = 1;
      dbg_printf("Turned ON all bx_dbg flags\n");
      return;
    } else if(!strcmp(arg,"dbg-none")) {
      bx_dbg.interrupts = 0;
      bx_dbg.exceptions = 0;
      dbg_printf("Turned OFF all bx_dbg flags\n");
      return;
    } else if(!strcmp(arg,"vga")){
      SIM->refresh_vga();
      return;
    } else {
      dbg_printf("Unrecognized arg: %s (only 'mode', 'int', 'softint', 'extint', 'iret', 'call', 'off', 'dbg-all' and 'dbg-none' are valid)\n", arg);
      return;
    }
  }

  if (dbg_show_mask) {
    dbg_printf("show mask is:");
    if (dbg_show_mask & BX_DBG_SHOW_CALLRET)
      dbg_printf(" call");
    if (dbg_show_mask & BX_DBG_SHOW_SOFTINT)
      dbg_printf(" softint");
    if (dbg_show_mask & BX_DBG_SHOW_EXTINT)
      dbg_printf(" extint");
    if (dbg_show_mask & BX_DBG_SHOW_IRET)
      dbg_printf(" iret");
    if (dbg_show_mask & BX_DBG_SHOW_MODE)
      dbg_printf(" mode");
    dbg_printf("\n");
  }
  else {
    dbg_printf("show mask is: 0\n");
  }
}

void bx_dbg_show_param_command(const char *param)
{
  dbg_printf("show param name: <%s>\n", param);
  bx_param_c *node = SIM->get_param(param, SIM->get_bochs_root());
  if (node) {
    print_tree(node, 0);
  }
  else {
    node = SIM->get_param(param, dbg_cpu_list);
    if (node)
      print_tree(node, 0);
    else
      dbg_printf("can't find param <%s> in global or default CPU tree\n", param);
  }
}

// return non zero to cause a stop
int bx_dbg_show_symbolic(void)
{
  static unsigned last_cpu_mode = 0;
  static bx_address last_cr3 = 0;

  /* modes & address spaces */
  if (dbg_show_mask & BX_DBG_SHOW_MODE) {
    if(BX_CPU(dbg_cpu)->get_cpu_mode() != last_cpu_mode) {
      dbg_printf (FMT_TICK ": switched from '%s' to '%s'\n",
        bx_pc_system.time_ticks(),
        cpu_mode_string(last_cpu_mode),
        cpu_mode_string(BX_CPU(dbg_cpu)->get_cpu_mode()));
    }

    if(last_cr3 != BX_CPU(dbg_cpu)->cr3)
      dbg_printf(FMT_TICK ": address space switched. CR3: 0x" FMT_PHY_ADDRX "\n",
        bx_pc_system.time_ticks(), BX_CPU(dbg_cpu)->cr3);
  }

  /* interrupts */
  if (dbg_show_mask & BX_DBG_SHOW_SOFTINT) {
    if(BX_CPU(dbg_cpu)->show_flag & Flag_softint) {
      dbg_printf(FMT_TICK ": softint ", bx_pc_system.time_ticks());
      dbg_print_guard_found(BX_CPU(dbg_cpu)->get_cpu_mode(),
        BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip,
        BX_CPU(dbg_cpu)->guard_found.laddr);
      dbg_printf("\n");
    }
  }

  if (dbg_show_mask & BX_DBG_SHOW_EXTINT) {
    if((BX_CPU(dbg_cpu)->show_flag & Flag_intsig) && !(BX_CPU(dbg_cpu)->show_flag & Flag_softint)) {
      dbg_printf(FMT_TICK ": exception (not softint) ", bx_pc_system.time_ticks());
      dbg_print_guard_found(BX_CPU(dbg_cpu)->get_cpu_mode(),
        BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip,
        BX_CPU(dbg_cpu)->guard_found.laddr);
      dbg_printf("\n");
    }
  }

  if (dbg_show_mask & BX_DBG_SHOW_IRET) {
    if(BX_CPU(dbg_cpu)->show_flag & Flag_iret) {
      dbg_printf(FMT_TICK ": iret ", bx_pc_system.time_ticks());
      dbg_print_guard_found(BX_CPU(dbg_cpu)->get_cpu_mode(),
        BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip,
        BX_CPU(dbg_cpu)->guard_found.laddr);
      dbg_printf("\n");
    }
  }

  /* calls */
  if (dbg_show_mask & BX_DBG_SHOW_CALLRET)
  {
    if(BX_CPU(dbg_cpu)->show_flag & Flag_call) {
      bx_phy_address phy = 0;
      bx_bool valid = BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(BX_CPU(dbg_cpu)->guard_found.laddr, &phy);
      dbg_printf(FMT_TICK ": call ", bx_pc_system.time_ticks());
      dbg_print_guard_found(BX_CPU(dbg_cpu)->get_cpu_mode(),
        BX_CPU(dbg_cpu)->guard_found.cs, BX_CPU(dbg_cpu)->guard_found.eip,
        BX_CPU(dbg_cpu)->guard_found.laddr);
      if (!valid) dbg_printf(" phys not valid");
      else {
        dbg_printf(" (phy: 0x" FMT_PHY_ADDRX ") %s", phy,
          bx_dbg_symbolic_address(BX_CPU(dbg_cpu)->cr3 >> 12,
              BX_CPU(dbg_cpu)->guard_found.eip,
              BX_CPU(dbg_cpu)->guard_found.laddr - BX_CPU(dbg_cpu)->guard_found.eip));
      }
      dbg_printf("\n");
    }
  }

  last_cr3 = BX_CPU(dbg_cpu)->cr3;
  last_cpu_mode = BX_CPU(dbg_cpu)->get_cpu_mode();
  BX_CPU(dbg_cpu)->show_flag = 0;

  return 0;
}

void bx_dbg_print_stack_command(unsigned nwords)
{
  bx_address linear_sp;
  unsigned len;

#if BX_SUPPORT_X86_64
  if (BX_CPU(dbg_cpu)->get_cpu_mode() == BX_MODE_LONG_64) {
    linear_sp = BX_CPU(dbg_cpu)->get_reg64(BX_64BIT_REG_RSP);
    len = 8;
  }
  else
#endif
  {
    if (BX_CPU(dbg_cpu)->sregs[BX_SEG_REG_SS].cache.u.segment.d_b) {
      linear_sp = BX_CPU(dbg_cpu)->get_reg32(BX_32BIT_REG_ESP);
      len = 4;
    }
    else {
      linear_sp = BX_CPU(dbg_cpu)->get_reg16(BX_16BIT_REG_SP);
      len = 2;
    }

    linear_sp = BX_CPU(dbg_cpu)->get_laddr(BX_SEG_REG_SS, linear_sp);
  }

  Bit8u buf[8];

  dbg_printf("Stack address size %d\n", len);

  for (unsigned i = 0; i < nwords; i++) {
    if (! bx_dbg_read_linear(dbg_cpu, linear_sp, len, buf)) break;
#if BX_SUPPORT_X86_64
    if (len == 8) {
      dbg_printf(" | STACK 0x%08x%08x [0x%08x:0x%08x]\n",
        GET32H(linear_sp), GET32L(linear_sp),
        (unsigned) conv_4xBit8u_to_Bit32u(buf+4),
        (unsigned) conv_4xBit8u_to_Bit32u(buf));
    }
    else
#endif
    {
      if (len == 4) {
        dbg_printf(" | STACK 0x%08x [0x%08x]\n",
          (unsigned) linear_sp, (unsigned) conv_4xBit8u_to_Bit32u(buf));
      }
      else {
        dbg_printf(" | STACK 0x%04x [0x%04x]\n",
          (unsigned) linear_sp, (unsigned) conv_2xBit8u_to_Bit16u(buf));
      }
    }

    linear_sp += len;
  }
}

void bx_dbg_print_watchpoints(void)
{ unsigned i;
  Bit8u buf[2];

  // print watch point info
  for (i = 0; i < num_read_watchpoints; i++) {
    if (BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), read_watchpoint[i].addr, 2, buf))
      dbg_printf("rd 0x"FMT_PHY_ADDRX" len=%d\t\t(%04x)\n",
          read_watchpoint[i].addr, read_watchpoint[i].len, (int)buf[0] | ((int)buf[1] << 8));
    else
      dbg_printf("rd 0x"FMT_PHY_ADDRX" len=%d\t\t(read error)\n",
          read_watchpoint[i].addr, read_watchpoint[i].len);
  }
  for (i = 0; i < num_write_watchpoints; i++) {
    if (BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), write_watchpoint[i].addr, 2, buf))
      dbg_printf("wr 0x"FMT_PHY_ADDRX" len=%d\t\t(%04x)\n",
          write_watchpoint[i].addr, write_watchpoint[i].len, (int)buf[0] | ((int)buf[1] << 8));
    else
      dbg_printf("rd 0x"FMT_PHY_ADDRX" len=%d\t\t(read error)\n",
          write_watchpoint[i].addr, write_watchpoint[i].len);
  }
}

void bx_dbg_watch(int type, bx_phy_address address, Bit32u len)
{
  if (type == BX_READ) {
    if (num_read_watchpoints == BX_DBG_MAX_WATCHPONTS) {
      dbg_printf("Too many read watchpoints (%d)\n", BX_DBG_MAX_WATCHPONTS);
      return;
    }
    read_watchpoint[num_read_watchpoints].addr = address;
    read_watchpoint[num_read_watchpoints].len = len;
    num_read_watchpoints++;
    dbg_printf("read watchpoint at 0x" FMT_PHY_ADDRX " len=%d inserted\n", address, len);
  }
  else if (type == BX_WRITE) {
    if (num_write_watchpoints == BX_DBG_MAX_WATCHPONTS) {
      dbg_printf("Too many write watchpoints (%d)\n", BX_DBG_MAX_WATCHPONTS);
      return;
    }
    write_watchpoint[num_write_watchpoints].addr = address;
    write_watchpoint[num_write_watchpoints].len = len;
    num_write_watchpoints++;
    dbg_printf("write watchpoint at 0x" FMT_PHY_ADDRX " len=%d inserted\n", address, len);
  }
  else {
    dbg_printf("bx_dbg_watch: broken watchpoint type");
  }
}

void bx_dbg_unwatch_all()
{
  num_read_watchpoints = num_write_watchpoints = 0;
  dbg_printf("All watchpoints removed\n");
}

void bx_dbg_unwatch(bx_phy_address address)
{ unsigned i;
  for (i=0; i<num_read_watchpoints; i++) {
    if (read_watchpoint[i].addr == address) {
      dbg_printf("read watchpoint at 0x" FMT_PHY_ADDRX " removed\n", address);
      // found watchpoint, delete it by shifting remaining entries left
      for (int j=i; j<(int)(num_read_watchpoints-1); j++) {
        read_watchpoint[j] = read_watchpoint[j+1];
      }
      num_read_watchpoints--;
      break;
    }
  }

  for (i=0; i<num_write_watchpoints; i++) {
    if (write_watchpoint[i].addr == address) {
      dbg_printf("write watchpoint at 0x" FMT_PHY_ADDRX " removed\n", address);
      // found watchpoint, delete it by shifting remaining entries left
      for (int j=i; j<(int)(num_write_watchpoints-1); j++) {
        write_watchpoint[j] = write_watchpoint[j+1];
      }
      num_write_watchpoints--;
      break;
    }
  }
}

void bx_dbg_continue_command(void)
{
  // continue executing, until a guard found

one_more:

  // update gui (disable continue command, enable stop command, etc.)
  sim_running->set(1);
  SIM->refresh_ci();

  // use simulation mode while executing instructions.  When the prompt
  // is printed, we will return to config mode.
  SIM->set_display_mode(DISP_MODE_SIM);

  bx_guard.interrupt_requested = 0;
  int stop = 0;
  int which = -1;
  while (!stop && !bx_guard.interrupt_requested) {
    // the quantum is an arbitrary number of cycles to run in each
    // processor.  In SMP mode, when this limit is reached, the
    // cpu_loop exits so that another processor can be simulated
    // for a few cycles.  With a single processor, the quantum
    // setting should have no effect, although a low setting does
    // lead to poor performance because cpu_loop is returning and
    // getting called again, over and over.

#define BX_DBG_DEFAULT_ICOUNT_QUANTUM 5

    if (BX_SMP_PROCESSORS == 1) {
      bx_dbg_set_icount_guard(0, 0); // run to next breakpoint
      BX_CPU(0)->cpu_loop();
      // set stop flag if a guard found other than icount or halted
      unsigned found = BX_CPU(0)->guard_found.guard_found;
      stop_reason_t reason = (stop_reason_t) BX_CPU(0)->stop_reason;
      if (found || (reason != STOP_NO_REASON && reason != STOP_CPU_HALTED)) {
        stop = 1;
        which = 0;
      }
    }
#if BX_SUPPORT_SMP
    else {
      Bit32u max_executed = 0;
      for (int cpu=0; cpu < BX_SMP_PROCESSORS; cpu++) {
        Bit64u cpu_icount = BX_CPU(cpu)->get_icount();
        bx_dbg_set_icount_guard(cpu, BX_DBG_DEFAULT_ICOUNT_QUANTUM);
        BX_CPU(cpu)->cpu_loop();
        Bit32u executed = BX_CPU(cpu)->get_icount() - cpu_icount;
        if (executed > max_executed) max_executed = executed;
        // set stop flag if a guard found other than icount or halted
        unsigned found = BX_CPU(cpu)->guard_found.guard_found;
        stop_reason_t reason = (stop_reason_t) BX_CPU(cpu)->stop_reason;
        if (found || (reason != STOP_NO_REASON && reason != STOP_CPU_HALTED)) {
          stop = 1;
          which = cpu;
        }
        // even if stop==1, finish cycling through all processors.
        // "which" remembers which cpu set the stop flag.  If multiple
        // cpus set stop, too bad.
      }

      // Potential deadlock if all processors are halted.  Then
      // max_executed will be 0, tick will be incremented by zero, and
      // there will never be a timed event to wake them up.
      // To avoid this, always tick by a minimum of 1.
      if (max_executed < 1) max_executed=1;

      // increment time tick only after all processors have had their chance.
      BX_TICKN(max_executed);
    }
#endif
  }

  sim_running->set(0);
  SIM->refresh_ci();

  // (mch) hack
  if (!bx_user_quit) {
    SIM->refresh_vga();
  }

  BX_INSTR_DEBUG_PROMPT();
  bx_dbg_print_guard_results();

  if (watchpoint_continue && (BX_CPU(which)->stop_reason == STOP_READ_WATCH_POINT ||
            BX_CPU(which)->stop_reason == STOP_WRITE_WATCH_POINT))
  goto one_more;
}

void bx_dbg_stepN_command(int cpu, Bit32u count)
{
  if (cpu != -1 && cpu >= BX_SMP_PROCESSORS) {
    dbg_printf("Error: stepN: unknown cpu=%d\n", cpu);
    return;
  }

  if (count == 0) {
    dbg_printf("Error: stepN: count=0\n");
    return;
  }

  // use simulation mode while executing instructions.  When the prompt
  // is printed, we will return to config mode.
  SIM->set_display_mode(DISP_MODE_SIM);

  if (cpu >= 0 || BX_SMP_PROCESSORS == 1) {
    bx_guard.interrupt_requested = 0;
    bx_dbg_set_icount_guard(cpu, count);
    BX_CPU(cpu)->cpu_loop();
  }
#if BX_SUPPORT_SMP
  else {
    int stop = 0;
    // for now, step each CPU one instruction at a time
    for (unsigned cycle=0; !stop && cycle < count; cycle++) {
      for (unsigned ncpu=0; ncpu < BX_SMP_PROCESSORS; ncpu++) {
        bx_guard.interrupt_requested = 0;
        bx_dbg_set_icount_guard(ncpu, 1);
        BX_CPU(ncpu)->cpu_loop();
        // set stop flag if a guard found other than icount or halted
        unsigned found = BX_CPU(ncpu)->guard_found.guard_found;
        stop_reason_t reason = (stop_reason_t) BX_CPU(ncpu)->stop_reason;
        if (found || (reason != STOP_NO_REASON && reason != STOP_CPU_HALTED))
          stop = 1;
      }

      // when (BX_SMP_PROCESSORS == 1) ticks are handled inside the cpu loop
      BX_TICK1();
    }
  }
#endif

  BX_INSTR_DEBUG_PROMPT();
  bx_dbg_print_guard_results();
}

void bx_dbg_disassemble_current(int which_cpu, int print_time)
{
  bx_phy_address phy;

  if (which_cpu < 0) {
    // iterate over all of them.
    for (int i=0; i<BX_SMP_PROCESSORS; i++)
      bx_dbg_disassemble_current(i, print_time);
    return;
  }

  bx_bool phy_valid = BX_CPU(which_cpu)->dbg_xlate_linear2phy(BX_CPU(which_cpu)->guard_found.laddr, &phy);
  if (! phy_valid) {
    dbg_printf("(%u).[" FMT_LL "d] ??? (physical address not available)\n", which_cpu, bx_pc_system.time_ticks());
    return;
  }

  if (bx_dbg_read_linear(which_cpu, BX_CPU(which_cpu)->guard_found.laddr, 16, bx_disasm_ibuf))
  {
#if 1
    unsigned ilen = bx_disassemble.disasm(IS_CODE_32(BX_CPU(which_cpu)->guard_found.code_32_64),
      IS_CODE_64(BX_CPU(which_cpu)->guard_found.code_32_64),
      BX_CPU(which_cpu)->get_segment_base(BX_SEG_REG_CS),
      BX_CPU(which_cpu)->guard_found.eip, bx_disasm_ibuf, bx_disasm_tbuf);
#else
//  extern char* disasm(const Bit8u *opcode, bool is_32, bool is_64, char *disbufptr, bxInstruction_c *i, bx_address cs_base, bx_address rip);

    bxInstruction_c i;
    BX_CPU(which_cpu)->disasm(bx_disasm_ibuf, IS_CODE_32(BX_CPU(which_cpu)->guard_found.code_32_64),
        IS_CODE_64(BX_CPU(which_cpu)->guard_found.code_32_64), 
        bx_disasm_tbuf, &i,
        BX_CPU(which_cpu)->get_segment_base(BX_SEG_REG_CS), BX_CPU(which_cpu)->guard_found.eip);

    unsigned ilen = i.ilen();
#endif

    // Note: it would be nice to display only the modified registers here, the easy
    // way out I have thought of would be to keep a prev_eax, prev_ebx, etc copies
    // in each cpu description (see cpu/cpu.h) and update/compare those "prev" values
    // from here. (eks)
    if(BX_CPU(which_cpu)->trace_reg)
      bx_dbg_info_registers_command(BX_INFO_GENERAL_PURPOSE_REGS);

    if (print_time)
      dbg_printf("(%u).[" FMT_LL "d] ", which_cpu, bx_pc_system.time_ticks());
    else
      dbg_printf("(%u) ", which_cpu);

    if (BX_CPU(which_cpu)->protected_mode()) {
      dbg_printf("[0x"FMT_PHY_ADDRX"] %04x:" FMT_ADDRX " (%s): ",
        phy, BX_CPU(which_cpu)->guard_found.cs,
        BX_CPU(which_cpu)->guard_found.eip,
        bx_dbg_symbolic_address(BX_CPU(which_cpu)->cr3 >> 12,
           BX_CPU(which_cpu)->guard_found.eip,
           BX_CPU(which_cpu)->get_segment_base(BX_SEG_REG_CS)));
    }
    else { // Real & V86 mode
      dbg_printf("[0x"FMT_PHY_ADDRX"] %04x:%04x (%s): ",
        phy, BX_CPU(which_cpu)->guard_found.cs,
        (unsigned) BX_CPU(which_cpu)->guard_found.eip,
        bx_dbg_symbolic_address(0,
           BX_CPU(which_cpu)->guard_found.eip & 0xffff,
           BX_CPU(which_cpu)->get_segment_base(BX_SEG_REG_CS)));
    }
    dbg_printf("%-25s ; ", bx_disasm_tbuf);
    for (unsigned j=0; j<ilen; j++) {
      dbg_printf("%02x", (unsigned) bx_disasm_ibuf[j]);
    }
    dbg_printf("\n");
  }
}

void bx_dbg_print_guard_results(void)
{
  unsigned cpu, i;

  for (cpu=0; cpu<BX_SMP_PROCESSORS; cpu++) {
    unsigned found = BX_CPU(cpu)->guard_found.guard_found;
    if (! found) { /* ... */ }
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
    else if (found & BX_DBG_GUARD_IADDR_VIR) {
      i = BX_CPU(cpu)->guard_found.iaddr_index;
      dbg_printf("(%u) Breakpoint %u, in ", cpu,
            bx_guard.iaddr.vir[i].bpoint_id);
      dbg_print_guard_found(BX_CPU(dbg_cpu)->get_cpu_mode(),
            BX_CPU(cpu)->guard_found.cs, BX_CPU(cpu)->guard_found.eip,
            BX_CPU(cpu)->guard_found.laddr);
      dbg_printf("\n");
    }
#endif
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
    else if (found & BX_DBG_GUARD_IADDR_LIN) {
      i = BX_CPU(cpu)->guard_found.iaddr_index;
      if (bx_guard.iaddr.lin[i].bpoint_id != 0)
        dbg_printf("(%u) Breakpoint %u, 0x" FMT_ADDRX " in ?? ()\n",
            cpu,
            bx_guard.iaddr.lin[i].bpoint_id,
            BX_CPU(cpu)->guard_found.laddr);
    }
#endif
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
    else if (found & BX_DBG_GUARD_IADDR_PHY) {
      i = BX_CPU(cpu)->guard_found.iaddr_index;
      dbg_printf("(%u) Breakpoint %u, 0x" FMT_ADDRX " in ?? ()\n",
            cpu,
            bx_guard.iaddr.phy[i].bpoint_id,
            BX_CPU(cpu)->guard_found.laddr);
    }
#endif
    switch(BX_CPU(cpu)->stop_reason) {
    case STOP_NO_REASON:
    case STOP_CPU_HALTED:
        break;
    case STOP_TIME_BREAK_POINT:
        dbg_printf("(%u) Caught time breakpoint\n", cpu);
        break;
    case STOP_READ_WATCH_POINT:
        dbg_printf("(%u) Caught read watch point at 0x" FMT_PHY_ADDRX "\n", cpu, BX_CPU(cpu)->watchpoint);
        break;
    case STOP_WRITE_WATCH_POINT:
        dbg_printf("(%u) Caught write watch point at 0x" FMT_PHY_ADDRX "\n", cpu, BX_CPU(cpu)->watchpoint);
        break;
    case STOP_MAGIC_BREAK_POINT:
        dbg_printf("(%u) Magic breakpoint\n", cpu);
        break;
    case STOP_MODE_BREAK_POINT:
        dbg_printf("(%u) Caught mode switch breakpoint switching to '%s'\n",
          cpu, cpu_mode_string(BX_CPU(cpu)->get_cpu_mode()));
        break;
    case STOP_VMEXIT_BREAK_POINT:
        dbg_printf("(%u) Caught VMEXIT breakpoint\n", cpu);
        break;
    default:
        dbg_printf("Error: (%u) print_guard_results: guard_found ? (stop reason %u)\n",
          cpu, BX_CPU(cpu)->stop_reason);
    }

    if (bx_debugger.auto_disassemble) {
      if (cpu==0) {
        // print this only once
        dbg_printf("Next at t=" FMT_LL "d\n", bx_pc_system.time_ticks());
      }
      bx_dbg_disassemble_current(cpu, 0);  // one cpu, don't print time
    }
  }
}

void bx_dbg_set_icount_guard(int which_cpu, Bit32u n)
{
  if (n == 0) {
    bx_guard.guard_for &= ~BX_DBG_GUARD_ICOUNT;
  }
  else {
    bx_guard.guard_for |= BX_DBG_GUARD_ICOUNT;
    BX_CPU(which_cpu)->guard_found.icount_max = BX_CPU(which_cpu)->get_icount() + n;
  }

  BX_CPU(which_cpu)->guard_found.guard_found = 0;
}

void bx_dbg_breakpoint_changed(void)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  if (bx_guard.iaddr.num_virtual)
    bx_guard.guard_for |= BX_DBG_GUARD_IADDR_VIR;
  else
    bx_guard.guard_for &= ~BX_DBG_GUARD_IADDR_VIR;
#endif

#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  if (bx_guard.iaddr.num_linear)
    bx_guard.guard_for |= BX_DBG_GUARD_IADDR_LIN;
  else
    bx_guard.guard_for &= ~BX_DBG_GUARD_IADDR_LIN;
#endif

#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  if (bx_guard.iaddr.num_physical)
    bx_guard.guard_for |= BX_DBG_GUARD_IADDR_PHY;
  else
    bx_guard.guard_for &= ~BX_DBG_GUARD_IADDR_PHY;
#endif
}

void bx_dbg_en_dis_breakpoint_command(unsigned handle, bx_bool enable)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  if (bx_dbg_en_dis_vbreak(handle, enable))
    goto done;
#endif

#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  if (bx_dbg_en_dis_lbreak(handle, enable))
    goto done;
#endif

#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  if (bx_dbg_en_dis_pbreak(handle, enable))
    goto done;
#endif

  dbg_printf("Error: breakpoint %u not found.\n", handle);
  return;

done:
  bx_dbg_breakpoint_changed();
}

bx_bool bx_dbg_en_dis_pbreak(unsigned handle, bx_bool enable)
{
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  // see if breakpoint is a physical breakpoint
  for (unsigned i=0; i<bx_guard.iaddr.num_physical; i++) {
    if (bx_guard.iaddr.phy[i].bpoint_id == handle) {
      bx_guard.iaddr.phy[i].enabled=enable;
      return 1;
    }
  }
#endif
  return 0;
}

bx_bool bx_dbg_en_dis_lbreak(unsigned handle, bx_bool enable)
{
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  // see if breakpoint is a linear breakpoint
  for (unsigned i=0; i<bx_guard.iaddr.num_linear; i++) {
    if (bx_guard.iaddr.lin[i].bpoint_id == handle) {
      bx_guard.iaddr.lin[i].enabled=enable;
      return 1;
    }
  }
#endif
  return 0;
}

bx_bool bx_dbg_en_dis_vbreak(unsigned handle, bx_bool enable)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  // see if breakpoint is a virtual breakpoint
  for (unsigned i=0; i<bx_guard.iaddr.num_virtual; i++) {
    if (bx_guard.iaddr.vir[i].bpoint_id == handle) {
      bx_guard.iaddr.vir[i].enabled=enable;
      return 1;
    }
  }
#endif
  return 0;
}

void bx_dbg_del_breakpoint_command(unsigned handle)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  if (bx_dbg_del_vbreak(handle))
   goto done;
#endif

#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  if (bx_dbg_del_lbreak(handle))
   goto done;
#endif

#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  if (bx_dbg_del_pbreak(handle))
   goto done;
#endif

  dbg_printf("Error: breakpoint %u not found.\n", handle);
  return;

done:
  bx_dbg_breakpoint_changed();
}

bx_bool bx_dbg_del_pbreak(unsigned handle)
{
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  // see if breakpoint is a physical breakpoint
  for (int i=0; i<(int)bx_guard.iaddr.num_physical; i++) {
    if (bx_guard.iaddr.phy[i].bpoint_id == handle) {
      // found breakpoint, delete it by shifting remaining entries left
      for (int j=i; j<(int)(bx_guard.iaddr.num_physical-1); j++) {
        bx_guard.iaddr.phy[j] = bx_guard.iaddr.phy[j+1];
      }
      bx_guard.iaddr.num_physical--;
      return 1;
    }
  }
#endif
  return 0;
}

bx_bool bx_dbg_del_lbreak(unsigned handle)
{
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  // see if breakpoint is a linear breakpoint
  for (int i=0; i<(int)bx_guard.iaddr.num_linear; i++) {
    if (bx_guard.iaddr.lin[i].bpoint_id == handle) {
      // found breakpoint, delete it by shifting remaining entries left
      for (int j=i; j<(int)(bx_guard.iaddr.num_linear-1); j++) {
        bx_guard.iaddr.lin[j] = bx_guard.iaddr.lin[j+1];
      }
      bx_guard.iaddr.num_linear--;
      return 1;
    }
  }
#endif
  return 0;
}

bx_bool bx_dbg_del_vbreak(unsigned handle)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  // see if breakpoint is a virtual breakpoint
  for (int i=0; i<(int)bx_guard.iaddr.num_virtual; i++) {
    if (bx_guard.iaddr.vir[i].bpoint_id == handle) {
      // found breakpoint, delete it by shifting remaining entries left
      for (int j=i; j<(int)(bx_guard.iaddr.num_virtual-1); j++) {
        bx_guard.iaddr.vir[j] = bx_guard.iaddr.vir[j+1];
      }
      bx_guard.iaddr.num_virtual--;
      return 1;
    }
  }
#endif
  return 0;
}

int bx_dbg_vbreakpoint_command(BreakpointKind bk, Bit32u cs, bx_address eip)
{
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  if (bk != bkRegular) {
    dbg_printf("Error: vbreak of this kind not implemented yet.\n");
    return -1;
  }

  if (bx_guard.iaddr.num_virtual >= BX_DBG_MAX_VIR_BPOINTS) {
    dbg_printf("Error: no more virtual breakpoint slots left.\n");
    dbg_printf("Error: see BX_DBG_MAX_VIR_BPOINTS.\n");
    return -1;
  }

  bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].cs  = cs;
  bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].eip = eip;
  bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].bpoint_id = bx_debugger.next_bpoint_id++;
  int BpId = (int)bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].bpoint_id;
  bx_guard.iaddr.vir[bx_guard.iaddr.num_virtual].enabled=1;
  bx_guard.iaddr.num_virtual++;
  bx_guard.guard_for |= BX_DBG_GUARD_IADDR_VIR;
  return BpId;

#else
  dbg_printf("Error: virtual breakpoint support not compiled in.\n");
  dbg_printf("Error: make sure BX_DBG_MAX_VIR_BPOINTS > 0\n");
  return -1;
#endif
}

int bx_dbg_lbreakpoint_command(BreakpointKind bk, bx_address laddress)
{
#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  if (bk == bkAtIP) {
    dbg_printf("Error: lbreak of this kind not implemented yet.\n");
    return -1;
  }

  if (bx_guard.iaddr.num_linear >= BX_DBG_MAX_LIN_BPOINTS) {
    dbg_printf("Error: no more linear breakpoint slots left.\n");
    dbg_printf("Error: see BX_DBG_MAX_LIN_BPOINTS.\n");
    return -1;
  }

  bx_guard.iaddr.lin[bx_guard.iaddr.num_linear].addr = laddress;
  int BpId = (bk == bkStepOver) ? 0 : bx_debugger.next_bpoint_id++;
  bx_guard.iaddr.lin[bx_guard.iaddr.num_linear].bpoint_id = BpId;
  bx_guard.iaddr.lin[bx_guard.iaddr.num_linear].enabled=1;
  bx_guard.iaddr.num_linear++;
  bx_guard.guard_for |= BX_DBG_GUARD_IADDR_LIN;
  return BpId;

#else
  dbg_printf("Error: linear breakpoint support not compiled in.\n");
  dbg_printf("Error: make sure BX_DBG_MAX_LIN_BPOINTS > 0\n");
  return -1;
#endif
}

int bx_dbg_pbreakpoint_command(BreakpointKind bk, bx_phy_address paddress)
{
#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  if (bk != bkRegular) {
    dbg_printf("Error: pbreak of this kind not implemented yet.\n");
    return -1;
  }

  if (bx_guard.iaddr.num_physical >= BX_DBG_MAX_PHY_BPOINTS) {
    dbg_printf("Error: no more physical breakpoint slots left.\n");
    dbg_printf("Error: see BX_DBG_MAX_PHY_BPOINTS.\n");
    return -1;
  }

  bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].addr = paddress;
  bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].bpoint_id = bx_debugger.next_bpoint_id++;
  int BpId = (int)bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].bpoint_id;
  bx_guard.iaddr.phy[bx_guard.iaddr.num_physical].enabled=1;
  bx_guard.iaddr.num_physical++;
  bx_guard.guard_for |= BX_DBG_GUARD_IADDR_PHY;
  return BpId;
#else
  dbg_printf("Error: physical breakpoint support not compiled in.\n");
  dbg_printf("Error: make sure BX_DBG_MAX_PHY_BPOINTS > 0\n");
  return -1;
#endif
}

void bx_dbg_info_bpoints_command(void)
{
  unsigned i;
// Num Type           Disp Enb Address    What
// 1   breakpoint     keep y   0x00010664 in main at temp.c:7

  dbg_printf("Num Type           Disp Enb Address\n");
#if (BX_DBG_MAX_VIR_BPOINTS > 0)
  for (i=0; i<bx_guard.iaddr.num_virtual; i++) {
    dbg_printf("%3u ", bx_guard.iaddr.vir[i].bpoint_id);
    dbg_printf("vbreakpoint    ");
    dbg_printf("keep ");
    dbg_printf(bx_guard.iaddr.vir[i].enabled?"y   ":"n   ");
    dbg_printf("0x%04x:" FMT_ADDRX "\n",
                  bx_guard.iaddr.vir[i].cs,
                  bx_guard.iaddr.vir[i].eip);
  }
#endif

#if (BX_DBG_MAX_LIN_BPOINTS > 0)
  for (i=0; i<bx_guard.iaddr.num_linear; i++) {
    dbg_printf("%3u ", bx_guard.iaddr.lin[i].bpoint_id);
    dbg_printf("lbreakpoint    ");
    dbg_printf("keep ");
    dbg_printf(bx_guard.iaddr.lin[i].enabled?"y   ":"n   ");
    dbg_printf("0x" FMT_ADDRX "\n", bx_guard.iaddr.lin[i].addr);
  }
#endif

#if (BX_DBG_MAX_PHY_BPOINTS > 0)
  for (i=0; i<bx_guard.iaddr.num_physical; i++) {
    dbg_printf("%3u ", bx_guard.iaddr.phy[i].bpoint_id);
    dbg_printf("pbreakpoint    ");
    dbg_printf("keep ");
    dbg_printf(bx_guard.iaddr.phy[i].enabled?"y   ":"n   ");
    dbg_printf("0x"FMT_PHY_ADDRX"\n", bx_guard.iaddr.phy[i].addr);
  }
#endif
}

void bx_dbg_set_auto_disassemble(bx_bool enable)
{
  bx_debugger.auto_disassemble = enable;
}

void bx_dbg_set_disassemble_size(unsigned size)
{
  if ((size!=16) && (size!=32) && (size!=64) && (size!=0))
  {
    dbg_printf("Error: disassemble size must be 16/32 or 64.\n");
    return;
  }
  bx_debugger.disassemble_size = size;
}

void bx_dbg_disassemble_switch_mode()
{
  bx_disassemble.toggle_syntax_mode();
}

void bx_dbg_disassemble_hex_mode_switch(int mode)
{
  bx_disassemble.set_offset_mode_hex(mode);
}

void bx_dbg_take_command(const char *what, unsigned n)
{
  if (! strcmp(what, "dma")) {
    if (n == 0) {
      dbg_printf("Error: take what n=0.\n");
      return;
    }
    bx_dbg_post_dma_reports(); // in case there's some pending reports
    bx_dbg_batch_dma.this_many = n;

    for (unsigned i=0; i<n; i++) {
      BX_CPU(0)->dbg_take_dma();
    }

    bx_dbg_batch_dma.this_many = 1;  // reset to normal
    bx_dbg_post_dma_reports(); // print reports and flush
    if (bx_guard.report.dma)
      dbg_printf("done\n");
  }
  else {
    dbg_printf("Error: Take '%s' not understood.\n", what);
  }
}

static void bx_print_char(Bit8u ch)
{
  if (ch < 10)
    dbg_printf(" \\%d  ", ch);
  else if (isprint(ch))
    dbg_printf("  %c  ", ch);
  else
    dbg_printf(" \\x%02X", ch);
}

void dbg_printf_binary(const char *format, Bit32u data, int bits)
{
  int len = 0;
  char num[33];

  for (unsigned b = 1 << (bits - 1); b; b >>= 1)
    num [len++] = (data & b) ? '1' : '0';
  num[len] = 0;
  dbg_printf(format, num);
}

void bx_dbg_examine_command(const char *command, const char *format, bx_bool format_passed,
               bx_address addr, bx_bool addr_passed)
{
  unsigned repeat_count, i;
  char ch, display_format, unit_size;
  bx_bool iteration, memory_dump = false;
  unsigned data_size;
  Bit8u   data8;
  Bit16u  data16;
  Bit32u  data32;
  unsigned columns, per_line, offset;
  bx_bool is_linear;
  Bit8u databuf[8];

  dbg_printf("[bochs]:\n");

  // If command was the extended "xp" command, meaning eXamine Physical memory,
  // then flag memory address as physical, rather than linear.
  if (strcmp(command, "xp") == 0) {
    is_linear = 0;
  }
  else {
    is_linear = 1;
  }

  if (addr_passed==0)
    addr = bx_debugger.default_addr;

  if (format_passed==0) {
    display_format = bx_debugger.default_display_format;
    unit_size      = bx_debugger.default_unit_size;
    repeat_count   = 1;
  }
  else {
    if (format==NULL) {
      dbg_printf("dbg_examine: format NULL\n");
      bx_dbg_exit(1);
    }

    if (strlen(format) < 2) {
      dbg_printf("dbg_examine: invalid format passed.\n");
      bx_dbg_exit(1);
    }

    if (format[0] != '/') {
      dbg_printf("dbg_examine: '/' is not first char of format.\n");
      bx_dbg_exit(1);
    }

    format++;
    repeat_count = 0;
    ch = *format;
    iteration = 0;

    while (ch>='0' && ch<='9') {
      iteration = 1;
      repeat_count = 10*repeat_count + (ch-'0');
      format++;
      ch = *format;
    }

    if (iteration==0) {
      // if no count given, use default
      repeat_count = 1;
    }
    else if (repeat_count==0) {
      // count give, but zero is an error
      dbg_printf("dbg_examine: repeat count given but is zero.\n");
      return;
    }

    // set up the default display format and unit size parameters
    display_format = bx_debugger.default_display_format;
    unit_size      = bx_debugger.default_unit_size;

    for (i = 0; format[i]; i++) {
      switch (ch = format[i]) {
        case 'x': // hex
        case 'd': // signed decimal
        case 'u': // unsigned decimal
        case 'o': // octal
        case 't': // binary
        case 'c': // chars
        case 's': // null terminated string
        case 'i': // machine instruction
          display_format = ch;
          break;

        case 'b': // bytes
        case 'h': // halfwords (two bytes)
        case 'w': // words (4 bytes)
        case 'g': // giant words (8 bytes)
          unit_size = ch;
          break;

	case 'm': // memory dump
	  memory_dump = true;
          break;

        default:
          dbg_printf("dbg_examine: invalid format passed. \'%c\'\n", ch);
          bx_dbg_exit(1);
          break;
      }
    }
  }

  if ((display_format == 'i') || (display_format == 's')) {
    dbg_printf("error: dbg_examine: 'i' and 's' formats not supported.\n");
    return;
  }

  if (unit_size == 'g') {
    dbg_printf("error: dbg_examine: 'g' (8-byte) unit size not supported.\n");
    return;
  }

  if (format_passed) {
    // store current options as default
    bx_debugger.default_display_format = display_format;
    bx_debugger.default_unit_size      = unit_size;
  }

  data_size = 0;
  per_line  = 0;
  offset = 0;

  if (memory_dump) {
    if (display_format == 'c') {
      // Display character dump in lines of 64 characters
      unit_size = 'b';
      data_size = 1;
      per_line = 64;
    }
    else
      switch (unit_size) {
        case 'b': data_size = 1; per_line = 16; break;
        case 'h': data_size = 2; per_line = 8; break;
        case 'w': data_size = 4; per_line = 4; break;
        //case 'g': data_size = 8; per_line = 2; break;
      }
      // binary format is quite large
      if (display_format == 't')
        per_line /= 4;
  }
  else {
    switch (unit_size) {
      case 'b': data_size = 1; per_line = 8; break;
      case 'h': data_size = 2; per_line = 8; break;
      case 'w': data_size = 4; per_line = 4; break;
    //case 'g': data_size = 8; per_line = 2; break;
    }
  }

  columns = per_line + 1; // set current number columns past limit

  for (i=1; i<=repeat_count; i++) {
    if (columns > per_line) {
      // if not 1st run, need a newline from last line
      if (i!=1)
        dbg_printf("\n");
      if (memory_dump)
        dbg_printf("0x" FMT_ADDRX ":", addr);
      else
        dbg_printf("0x" FMT_ADDRX " <bogus+%8u>:", addr, offset);
      columns = 1;
    }

    /* Put a space in the middle of dump, for readability */
    if ((columns - 1) == per_line / 2
     && memory_dump && display_format != 'c')
      dbg_printf(" ");

    if (is_linear) {
      if (! bx_dbg_read_linear(dbg_cpu, addr, data_size, databuf)) return;
    }
    else {
      // address is already physical address
      BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), (bx_phy_address) addr, data_size, databuf);
    }

    //FIXME HanishKVC The char display for data to be properly integrated
    //      so that repeat_count, columns, etc. can be set or used properly.
    //      Also for data_size of 2 and 4 how to display the individual
    //      characters i.e in which order to be decided.
    switch (data_size) {
      case 1:
        data8 = databuf[0];
        if (memory_dump)
          switch (display_format) {
	    case 'd': dbg_printf("%03d ", data8); break;
	    case 'u': dbg_printf("%03u ", data8); break;
	    case 'o': dbg_printf("%03o ", data8); break;
	    case 't': dbg_printf_binary("%s ", data8, 8); break;
            case 'c': dbg_printf("%c", isprint(data8) ? data8 : '.'); break;
	    default : dbg_printf("%02X ", data8); break;
        }
	else
        switch (display_format) {
          case 'x': dbg_printf("\t0x%02x", (unsigned) data8); break;
          case 'd': dbg_printf("\t%d", (int) (Bit8s) data8); break;
          case 'u': dbg_printf("\t%u", (unsigned) data8); break;
          case 'o': dbg_printf("\t%o", (unsigned) data8); break;
            case 't': dbg_printf_binary("\t%s", data8, 8); break;
            case 'c': bx_print_char(data8); break;
        }
        break;

      case 2:
        ReadHostWordFromLittleEndian(databuf, data16);

        if (memory_dump)
          switch (display_format) {
	    case 'd': dbg_printf("%05d ", data16); break;
	    case 'u': dbg_printf("%05u ", data16); break;
	    case 'o': dbg_printf("%06o ", data16); break;
	    case 't': dbg_printf_binary("%s ", data16, 16); break;
	    default : dbg_printf("%04X ", data16); break;
        }
	else
        switch (display_format) {
          case 'x': dbg_printf("\t0x%04x", (unsigned) data16); break;
          case 'd': dbg_printf("\t%d", (int) (Bit16s) data16); break;
          case 'u': dbg_printf("\t%u", (unsigned) data16); break;
          case 'o': dbg_printf("\t%o", (unsigned) data16); break;
            case 't': dbg_printf_binary("\t%s", data16, 16); break;
          case 'c':
            bx_print_char(data16>>8);
            bx_print_char(data16 & 0xff);
            break;
        }
        break;

      case 4:
        ReadHostDWordFromLittleEndian(databuf, data32);

        if (memory_dump)
          switch (display_format) {
	    case 'd': dbg_printf("%10d ", data32); break;
	    case 'u': dbg_printf("%10u ", data32); break;
	    case 'o': dbg_printf("%12o ", data32); break;
	    case 't': dbg_printf_binary("%s ", data32, 32); break;
	    default : dbg_printf("%08X ", data32); break;
        }
	else
        switch (display_format) {
          case 'x': dbg_printf("\t0x%08x", (unsigned) data32); break;
          case 'd': dbg_printf("\t%d", (int) (Bit32s) data32); break;
          case 'u': dbg_printf("\t%u", (unsigned) data32); break;
          case 'o': dbg_printf("\t%o", (unsigned) data32); break;
          case 't': dbg_printf_binary("\t%s", data32, 32); break;
          case 'c':
            bx_print_char(0xff & (data32>>24));
            bx_print_char(0xff & (data32>>16));
            bx_print_char(0xff & (data32>> 8));
            bx_print_char(0xff & (data32>> 0));
            break;
        }
        break;
    }

    addr += data_size;
    bx_debugger.default_addr = addr;
    columns++;
    offset += data_size;
  }
  dbg_printf("\n");
}

Bit32u bx_dbg_lin_indirect(bx_address addr)
{
  Bit8u  databuf[4];
  Bit32u result;

  if (! bx_dbg_read_linear(dbg_cpu, addr, 4, databuf)) {
    /* bx_dbg_read_linear already printed an error message if failed */
    return 0;
  }

  ReadHostDWordFromLittleEndian(databuf, result);
  return result;
}

Bit32u bx_dbg_phy_indirect(bx_phy_address paddr)
{
  Bit8u  databuf[4];
  Bit32u result;

  if (! BX_MEM(0)->dbg_fetch_mem(BX_CPU(dbg_cpu), paddr, 4, databuf)) {
    /* dbg_fetch_mem already printed an error message if failed */
    return 0;
  }

  ReadHostDWordFromLittleEndian(databuf, result);
  return result;
}

void bx_dbg_writemem_command(const char *filename, bx_address laddr, unsigned len)
{
  if (len == 0) {
    dbg_printf("writemem: required length in bytes\n");
    return;
  }

  FILE *f = fopen(filename, "wb");
  if (!f) {
    dbg_printf("Can not open file '%s' for writemem log!\n", filename);
    return;
  }

  Bit8u databuf[4096];

  while(len > 0) {
    unsigned bytes = len;
    if (len > 4096) bytes = 4096;

    // I hope laddr is 4KB aligned so read_linear will be done efficiently
    if (! bx_dbg_read_linear(dbg_cpu, laddr, bytes, databuf)) {
      /* bx_dbg_read_linear already printed an error message if failed */
      len = 0;
      break;
    }

    if (fwrite(databuf, 1, bytes, f) < bytes) {
      dbg_printf("Write error to file '%s'\n", filename);
      len = 0;
      break;
    }

    len -= bytes;
    laddr += bytes;
  }

  fclose(f);
}

void bx_dbg_setpmem_command(bx_phy_address paddr, unsigned len, Bit32u val)
{
  Bit8u buf[4];

  switch (len) {
    case 1:
      buf[0] = (Bit8u) val;
      break;
    case 2:
      buf[0] = val & 0xff; val >>= 8;
      buf[1] = val & 0xff;
      break;
    case 4:
      buf[0] = val & 0xff; val >>= 8;
      buf[1] = val & 0xff; val >>= 8;
      buf[2] = val & 0xff; val >>= 8;
      buf[3] = val & 0xff;
      break;
    default:
      dbg_printf("Error: setpmem: bad length value = %u\n", len);
      return;
    }

  if (! BX_MEM(0)->dbg_set_mem(paddr, len, buf)) {
    dbg_printf("Error: setpmem: could not set memory, out of physical bounds?\n");
  }
}

void bx_dbg_set_symbol_command(const char *symbol, bx_address val)
{
  bx_bool is_OK = false;
  symbol++; // get past '$'

  if (!strcmp(symbol, "eip") || !strcmp(symbol, "rip")) {
    bx_dbg_set_rip_value(val);
    return;
  }
  else if (!strcmp(symbol, "eflags")) {
    is_OK = BX_CPU(dbg_cpu)->dbg_set_eflags(val);
  }
  else if (!strcmp(symbol, "cpu")) {
    if (val >= BX_SMP_PROCESSORS) {
      dbg_printf("invalid cpu id number %d\n", val);
      return;
    }
    char cpu_param_name[10];
    sprintf(cpu_param_name, "cpu%d", val);
    dbg_cpu_list = (bx_list_c*) SIM->get_param(cpu_param_name, SIM->get_bochs_root());
    dbg_cpu = val;
    return;
  }
  else if (!strcmp(symbol, "synchronous_dma")) {
    bx_guard.async.dma = !val;
    return;
  }
  else if (!strcmp(symbol, "synchronous_irq")) {
    bx_guard.async.irq = !val;
    return;
  }
  else if (!strcmp(symbol, "event_reports")) {
    bx_guard.report.irq   = val;
    bx_guard.report.a20   = val;
    bx_guard.report.io    = val;
    bx_guard.report.dma   = val;
    return;
  }
  else if (!strcmp(symbol, "auto_disassemble")) {
    bx_dbg_set_auto_disassemble(val != 0);
    return;
  }
  else {
    dbg_printf("Error: set: unrecognized symbol.\n");
    return;
  }

  if (!is_OK) {
    dbg_printf("Error: could not set value for '%s'\n", symbol);
  }
}

void bx_dbg_query_command(const char *what)
{
  unsigned pending;

  if (! strcmp(what, "pending")) {
    pending = BX_CPU(0)->dbg_query_pending();

    if (pending & BX_DBG_PENDING_DMA)
      dbg_printf("pending DMA\n");

    if (pending & BX_DBG_PENDING_IRQ)
      dbg_printf("pending IRQ\n");

    if (!pending)
      dbg_printf("pending none\n");

    dbg_printf("done\n");
  }
  else {
    dbg_printf("Error: Query '%s' not understood.\n", what);
  }
}

void bx_dbg_restore_command(const char *param_name, const char *restore_path)
{
  const char *path = (restore_path == NULL) ? "." : restore_path;
  dbg_printf("restoring param (%s) state from file (%s/%s)\n",
      param_name, path, param_name);
  if (! SIM->restore_bochs_param(SIM->get_bochs_root(), path, param_name)) {
    dbg_printf("Error: error occured during restore\n");
  }
  else {
    bx_sr_after_restore_state();
  }
}

void bx_dbg_disassemble_current(const char *format)
{
  Bit64u addr = bx_dbg_get_laddr(bx_dbg_get_selector_value(BX_DBG_SREG_CS), 
     BX_CPU(dbg_cpu)->get_instruction_pointer());
  bx_dbg_disassemble_command(format, addr, addr);
}

void bx_dbg_disassemble_command(const char *format, Bit64u from, Bit64u to)
{
  int numlines = INT_MAX;

  if (from > to) {
     Bit64u temp = from;
     from = to;
     to = temp;
  }

  if (format) {
    // format always begins with '/' (checked in lexer)
    // so we won't bother checking it here second time.
    numlines = atoi(format + 1);
    if (to == from)
      to = BX_MAX_BIT64U; // Disassemble just X lines
  }

  unsigned dis_size = bx_debugger.disassemble_size;
  if (dis_size == 0) {
    dis_size = 16; 		// until otherwise proven
    if (BX_CPU(dbg_cpu)->sregs[BX_SEG_REG_CS].cache.u.segment.d_b)
      dis_size = 32;
    if (BX_CPU(dbg_cpu)->get_cpu_mode() == BX_MODE_LONG_64)
      dis_size = 64;
  }

  do {
    numlines--;

    if (! bx_dbg_read_linear(dbg_cpu, from, 16, bx_disasm_ibuf)) break;

    unsigned ilen = bx_disassemble.disasm(dis_size==32, dis_size==64,
       (bx_address)(-1), (bx_address)(-1), bx_disasm_ibuf, bx_disasm_tbuf);

    const char *Sym=bx_dbg_disasm_symbolic_address((Bit32u)from, 0);

    dbg_printf("%08x: ", (unsigned) from);
    dbg_printf("(%20s): ", Sym?Sym:"");
    dbg_printf("%-25s ; ", bx_disasm_tbuf);

    for (unsigned j=0; j<ilen; j++)
      dbg_printf("%02x", (unsigned) bx_disasm_ibuf[j]);
    dbg_printf("\n");

    from += ilen;
  } while ((from < to) && numlines > 0);
}

void bx_dbg_instrument_command(const char *comm)
{
#if BX_INSTRUMENTATION
  dbg_printf("Command '%s' passed to instrumentation module\n", comm);
  BX_INSTR_DEBUG_CMD(comm);
#else
  UNUSED(comm);
  dbg_printf("Error: instrumentation not enabled.\n");
#endif
}

void bx_dbg_doit_command(unsigned n)
{
  // generic command to add temporary hacks to
  // for debugging purposes
  bx_dbg.interrupts = n;
  bx_dbg.exceptions = n;
}

void bx_dbg_crc_command(bx_phy_address addr1, bx_phy_address addr2)
{
  Bit32u crc1;

  if (addr1 >= addr2) {
    dbg_printf("Error: crc32: invalid range\n");
    return;
  }

  if (!BX_MEM(0)->dbg_crc32(addr1, addr2, &crc1)) {
    dbg_printf("Error: could not crc32 memory\n");
    return;
  }
  dbg_printf("0x%lx\n", crc1);
}

void bx_dbg_print_descriptor(Bit32u lo, Bit32u hi)
{
  Bit32u base = ((lo >> 16) & 0xffff)
             | ((hi << 16) & 0xff0000)
             | (hi & 0xff000000);
  Bit32u limit = (hi & 0x000f0000) | (lo & 0xffff);
  Bit32u segment = (lo >> 16) & 0xffff;
  Bit32u offset = (lo & 0xffff) | (hi & 0xffff0000);
  unsigned type = (hi >> 8) & 0xf;
  unsigned dpl = (hi >> 13) & 0x3;
  unsigned s = (hi >> 12) & 0x1;
  unsigned d_b = (hi >> 22) & 0x1;
  unsigned g = (hi >> 23) & 0x1;
#if BX_SUPPORT_X86_64
  unsigned l = (hi >> 21) & 0x1;
#endif

  // 32-bit trap gate, target=0010:c0108ec4, DPL=0, present=1
  // code segment, base=0000:00cfffff, length=0xffff
  if (s) {
    // either a code or a data segment. bit 11 (type file MSB) then says
    // 0=data segment, 1=code seg
    if (IS_CODE_SEGMENT(type)) {
      dbg_printf("Code segment, base=0x%08x, limit=0x%08x, %s, %s%s",
        base, g ? (limit * 4096 + 4095) : limit,
        IS_CODE_SEGMENT_READABLE(type) ? "Execute/Read" : "Execute-Only",
        IS_CODE_SEGMENT_CONFORMING(type)? "Conforming" : "Non-Conforming",
        IS_SEGMENT_ACCESSED(type)? ", Accessed" : "");
#if BX_SUPPORT_X86_64
      if (l && !d_b)
        dbg_printf(", 64-bit\n");
      else
#endif
        dbg_printf(", %d-bit\n", d_b ? 32 : 16);
    } else {
      dbg_printf("Data segment, base=0x%08x, limit=0x%08x, %s%s%s\n",
        base, g ? (limit * 4096 + 4095) : limit,
        IS_DATA_SEGMENT_WRITEABLE(type)? "Read/Write" : "Read-Only",
        IS_DATA_SEGMENT_EXPAND_DOWN(type)? ", Expand-down" : "",
        IS_SEGMENT_ACCESSED(type)? ", Accessed" : "");
    }
  } else {
    // types from IA32-devel-guide-3, page 3-15.
    static const char *undef = "???";
    static const char *type_names[16] = { 
        undef,
        "16-Bit TSS (available)",
        "LDT",
        "16-Bit TSS (Busy)",
        "16-Bit Call Gate",
        "Task Gate",
        "16-Bit Interrupt Gate",
        "16-Bit Trap Gate",
        undef,
        "32-Bit TSS (Available)",
        undef,
        "32-Bit TSS (Busy)",
        "32-Bit Call Gate",
        undef,
        "32-Bit Interrupt Gate",
        "32-Bit Trap Gate"
    };
    dbg_printf("%s ", type_names[type]);
    // only print more if type is valid
    if (type_names[type] == undef)  {
      dbg_printf("descriptor hi=0x%08x, lo=0x%08x", hi, lo);
    } else {
      // for call gates, print segment:offset and parameter count p.4-15
      // for task gate, only present,dpl,TSS segment selector exist. p.5-13
      // for interrupt gate, segment:offset,p,dpl
      // for trap gate, segment:offset,p,dpl
      // for TSS, base address and segment limit
      switch (type) {
      case BX_SYS_SEGMENT_AVAIL_286_TSS:
      case BX_SYS_SEGMENT_BUSY_286_TSS:
      case BX_SYS_SEGMENT_AVAIL_386_TSS:
      case BX_SYS_SEGMENT_BUSY_386_TSS:
        dbg_printf("at 0x%08x, length 0x%05x", base, limit);
        break;
      case BX_SYS_SEGMENT_LDT:
        // it's an LDT. not much to print.
        break;
      default:
        // task, int, trap, or call gate.
        dbg_printf("target=0x%04x:0x%08x, DPL=%d", segment, offset, dpl);
        break;
      }
    }
    dbg_printf("\n");
  }
}

#if BX_SUPPORT_X86_64
void bx_dbg_print_descriptor64(Bit32u lo1, Bit32u hi1, Bit32u lo2, Bit32u hi2)
{
  Bit32u segment = (lo1 >> 16) & 0xffff;
  Bit64u offset = (lo1 & 0xffff) | (hi1 & 0xffff0000) | ((Bit64u)(lo2) << 32);
  unsigned type = (hi1 >> 8) & 0xf;
  unsigned dpl = (hi1 >> 13) & 0x3;
  unsigned s = (hi1 >> 12) & 0x1;

  if (s) {
    dbg_printf("bx_dbg_print_descriptor64: only system entries displayed in 64bit mode\n");
  }
  else {
    static const char *undef = "???";
    static const char *type_names[16] = { 
        undef,
        undef,
        "LDT",
        undef,
        undef,
        undef,
        undef,
        undef,
        undef,
        "64-Bit TSS (Available)",
        undef,
        "64-Bit TSS (Busy)",
        "64-Bit Call Gate",
        undef,
        "64-Bit Interrupt Gate",
        "64-Bit Trap Gate"
    };
    dbg_printf("%s ", type_names[type]);
    // only print more if type is valid
    if (type_names[type] == undef)  {
      dbg_printf("\ndescriptor dword2 hi=0x%08x, lo=0x%08x", hi2, lo2);
      dbg_printf("\n           dword1 hi=0x%08x, lo=0x%08x", hi1, lo1);
    } else {
      // for call gates, print segment:offset and parameter count p.4-15
      // for task gate, only present,dpl,TSS segment selector exist. p.5-13
      // for interrupt gate, segment:offset,p,dpl
      // for trap gate, segment:offset,p,dpl
      // for TSS, base address and segment limit
      switch (type) {
      case BX_SYS_SEGMENT_AVAIL_286_TSS:
      case BX_SYS_SEGMENT_BUSY_286_TSS:
      case BX_SYS_SEGMENT_AVAIL_386_TSS:
      case BX_SYS_SEGMENT_BUSY_386_TSS:
        // don't print nothing about 64-bit TSS
        break;
      case BX_SYS_SEGMENT_LDT:
        // it's an LDT. not much to print.
        break;
      default:
        // task, int, trap, or call gate.
        dbg_printf("target=0x%04x:"FMT_ADDRX", DPL=%d", segment, offset, dpl);
        break;
      }
    }
    dbg_printf("\n");
  }
}
#endif

void bx_dbg_info_idt_command(unsigned from, unsigned to)
{
  bx_dbg_global_sreg_t idtr;
  BX_CPU(dbg_cpu)->dbg_get_idtr(&idtr);
  bx_bool all = 0;

  if (to == (unsigned) EMPTY_ARG) {
    to = from;
    if(from == (unsigned) EMPTY_ARG) { from = 0; to = 255; all = 1; }
  }
  if (from > 255 || to > 255) {
    dbg_printf("IDT entry should be [0-255], 'info idt' command malformed\n");
    return;
  }
  if (from > to) {
    unsigned temp = from;
    from = to;
    to = temp;
  }

#if BX_SUPPORT_X86_64
  if (BX_CPU(dbg_cpu)->long_mode()) {
    dbg_printf("Interrupt Descriptor Table (base=0x" FMT_ADDRX ", limit=%d):\n", idtr.base, idtr.limit);
    for (unsigned n = from; n<=to; n++) {
      Bit8u entry[16];
      if (16*n + 15 > idtr.limit) break;
      if (bx_dbg_read_linear(dbg_cpu, idtr.base + 16*n, 16, entry)) {
        dbg_printf("IDT[0x%02x]=", n);

        Bit32u lo1 = (entry[3]  << 24) | (entry[2]  << 16) | (entry[1]  << 8) | (entry[0]);
        Bit32u hi1 = (entry[7]  << 24) | (entry[6]  << 16) | (entry[5]  << 8) | (entry[4]);
        Bit32u lo2 = (entry[11] << 24) | (entry[10] << 16) | (entry[9]  << 8) | (entry[8]);
        Bit32u hi2 = (entry[15] << 24) | (entry[14] << 16) | (entry[13] << 8) | (entry[12]);

        bx_dbg_print_descriptor64(lo1, hi1, lo2, hi2);
      }
      else {
        dbg_printf("error: IDTR+16*%d points to invalid linear address 0x" FMT_ADDRX "\n", n, idtr.base);
      }
    }
  }
  else
#endif
  {
    dbg_printf("Interrupt Descriptor Table (base=0x" FMT_ADDRX ", limit=%d):\n", idtr.base, idtr.limit);
    for (unsigned n = from; n<=to; n++) {
      Bit8u entry[8];
      if (8*n + 7 > idtr.limit) break;
      if (bx_dbg_read_linear(dbg_cpu, idtr.base + 8*n, 8, entry)) {
        dbg_printf("IDT[0x%02x]=", n);

        Bit32u lo = (entry[3]  << 24) | (entry[2]  << 16) | (entry[1]  << 8) | (entry[0]);
        Bit32u hi = (entry[7]  << 24) | (entry[6]  << 16) | (entry[5]  << 8) | (entry[4]);

        bx_dbg_print_descriptor(lo, hi);
      }
      else {
        dbg_printf("error: IDTR+8*%d points to invalid linear address 0x" FMT_ADDRX "\n", n, idtr.base);
      }
    }
  }

  if (all)
    dbg_printf("You can list individual entries with 'info idt [NUM]' or groups with 'info idt [NUM] [NUM]'\n");
}

void bx_dbg_info_gdt_command(unsigned from, unsigned to)
{
  bx_dbg_global_sreg_t gdtr;
  BX_CPU(dbg_cpu)->dbg_get_gdtr(&gdtr);
  bx_bool all = 0;

  if (to == (unsigned) EMPTY_ARG) {
    to = from;
    if(from == (unsigned) EMPTY_ARG) { from = 0; to = 0xffff; all = 1; }
  }
  if (from > 0xffff || to > 0xffff) {
    dbg_printf("GDT entry should be [0-65535], 'info gdt' command malformed\n");
    return;
  }
  if (from > to) {
    unsigned temp = from;
    from = to;
    to = temp;
  }

  dbg_printf("Global Descriptor Table (base=0x" FMT_ADDRX ", limit=%d):\n", gdtr.base, gdtr.limit);
  for (unsigned n = from; n<=to; n++) {
    Bit8u entry[8];
    if (8*n + 7 > gdtr.limit) break;
    if (bx_dbg_read_linear(dbg_cpu, gdtr.base + 8*n, 8, entry)) {
      dbg_printf("GDT[0x%02x]=", n);

      Bit32u lo = (entry[3]  << 24) | (entry[2]  << 16) | (entry[1]  << 8) | (entry[0]);
      Bit32u hi = (entry[7]  << 24) | (entry[6]  << 16) | (entry[5]  << 8) | (entry[4]);

      bx_dbg_print_descriptor(lo, hi);
    }
    else {
      dbg_printf("error: GDTR+8*%d points to invalid linear address 0x" FMT_ADDRX "\n",
        n, gdtr.base);
    }
  }
  if (all)
    dbg_printf("You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'\n");
}

void bx_dbg_info_ldt_command(unsigned from, unsigned to)
{
  bx_address ldtr_base = SIM->get_param_num("LDTR.base", dbg_cpu_list)->get64();
  Bit32u ldtr_limit = SIM->get_param_num("LDTR.limit_scaled", dbg_cpu_list)->get();

  bx_bool all = 0;

  if (to == (unsigned) EMPTY_ARG) {
    to = from;
    if(from == (unsigned) EMPTY_ARG) { from = 0; to = 0xffff; all = 1; }
  }
  if (from > 0xffff || to > 0xffff) {
    dbg_printf("LDT entry should be [0-65535], 'info ldt' command malformed\n");
    return;
  }
  if (from > to) {
    unsigned temp = from;
    from = to;
    to = temp;
  }

  dbg_printf("Local Descriptor Table (base=0x" FMT_ADDRX ", limit=%d):\n", ldtr_base, ldtr_limit);
  for (unsigned n = from; n<=to; n++) {
    Bit8u entry[8];
    if (8*n + 7 > ldtr_limit) break;
    if (bx_dbg_read_linear(dbg_cpu, ldtr_base + 8*n, 8, entry)) {
      dbg_printf("LDT[0x%02x]=", n);

      Bit32u lo = (entry[3]  << 24) | (entry[2]  << 16) | (entry[1]  << 8) | (entry[0]);
      Bit32u hi = (entry[7]  << 24) | (entry[6]  << 16) | (entry[5]  << 8) | (entry[4]);

      bx_dbg_print_descriptor(lo, hi);
    }
    else {
      dbg_printf("error: LDTR+8*%d points to invalid linear address 0x" FMT_ADDRX "\n",
        n, ldtr_base);
    }
  }
  if (all)
    dbg_printf("You can list individual entries with 'info ldt [NUM]' or groups with 'info ldt [NUM] [NUM]'\n");
}

/*form RB list*/
static const char* bx_dbg_ivt_desc(int intnum)
{
  const char* ret;
  switch (intnum) {
    case 0x00: ret = "DIVIDE ERROR"                        ; break;
    case 0x01: ret = "SINGLE STEP"                         ; break;
    case 0x02: ret = "NON-MASKABLE INTERRUPT"              ; break;
    case 0x03: ret = "BREAKPOINT"                          ; break;
    case 0x04: ret = "INT0 DETECTED OVERFLOW"              ; break;
    case 0x05: ret = "BOUND RANGE EXCEED"                  ; break;
    case 0x06: ret = "INVALID OPCODE"                      ; break;
    case 0x07: ret = "PROCESSOR EXTENSION NOT AVAILABLE"   ; break;
    case 0x08: ret = "IRQ0 - SYSTEM TIMER"                 ; break;
    case 0x09: ret = "IRQ1 - KEYBOARD DATA READY"          ; break;
    case 0x0a: ret = "IRQ2 - LPT2"                         ; break;
    case 0x0b: ret = "IRQ3 - COM2"                         ; break;
    case 0x0c: ret = "IRQ4 - COM1"                         ; break;
    case 0x0d: ret = "IRQ5 - FIXED DISK"                   ; break;
    case 0x0e: ret = "IRQ6 - DISKETTE CONTROLLER"          ; break;
    case 0x0f: ret = "IRQ7 - PARALLEL PRINTER"             ; break;
    case 0x10: ret = "VIDEO"                               ; break;
    case 0x11: ret = "GET EQUIPMENT LIST"                  ; break;
    case 0x12: ret = "GET MEMORY SIZE"                     ; break;
    case 0x13: ret = "DISK"                                ; break;
    case 0x14: ret = "SERIAL"                              ; break;
    case 0x15: ret = "SYSTEM"                              ; break;
    case 0x16: ret = "KEYBOARD"                            ; break;
    case 0x17: ret = "PRINTER"                             ; break;
    case 0x18: ret = "CASETTE BASIC"                       ; break;
    case 0x19: ret = "BOOTSTRAP LOADER"                    ; break;
    case 0x1a: ret = "TIME"                                ; break;
    case 0x1b: ret = "KEYBOARD - CONTROL-BREAK HANDLER"    ; break;
    case 0x1c: ret = "TIME - SYSTEM TIMER TICK"            ; break;
    case 0x1d: ret = "SYSTEM DATA - VIDEO PARAMETER TABLES"; break;
    case 0x1e: ret = "SYSTEM DATA - DISKETTE PARAMETERS"   ; break;
    case 0x1f: ret = "SYSTEM DATA - 8x8 GRAPHICS FONT"     ; break;
    case 0x70: ret = "IRQ8 - CMOS REAL-TIME CLOCK"         ; break;
    case 0x71: ret = "IRQ9 - REDIRECTED TO INT 0A BY BIOS" ; break;
    case 0x72: ret = "IRQ10 - RESERVED"                    ; break;
    case 0x73: ret = "IRQ11 - RESERVED"                    ; break;
    case 0x74: ret = "IRQ12 - POINTING DEVICE"             ; break;
    case 0x75: ret = "IRQ13 - MATH COPROCESSOR EXCEPTION"  ; break;
    case 0x76: ret = "IRQ14 - HARD DISK CONTROLLER OPERATION COMPLETE"; break;
    case 0x77: ret = "IRQ15 - SECONDARY IDE CONTROLLER OPERATION COMPLETE"; break;
    default  : ret = ""                                    ; break;
  }
  return ret;
}

void bx_dbg_info_ivt_command(unsigned from, unsigned to)
{
  unsigned char buff[4];
  unsigned seg, off;
  bx_bool all = 0;
  bx_dbg_global_sreg_t idtr;

  BX_CPU(dbg_cpu)->dbg_get_idtr(&idtr);

  if (! BX_CPU(dbg_cpu)->protected_mode())
  {
    if (to == (unsigned) EMPTY_ARG) {
      to = from;
      if(from == (unsigned) EMPTY_ARG) { from = 0; to = 255; all = 1; }
    }
    if (from > 255 || to > 255) {
      dbg_printf("IVT entry should be [0-255], 'info ivt' command malformed\n");
      return;
    }
    if (from > to) {
      unsigned temp = from;
      from = to;
      to = temp;
    }

    for (unsigned i = from; i <= to; i++)
    {
      bx_dbg_read_linear(dbg_cpu, idtr.base + i*4, 4, buff);
      seg = ((Bit32u) buff[3] << 8) | buff[2];
      off = ((Bit32u) buff[1] << 8) | buff[0];
      bx_dbg_read_linear(dbg_cpu, (seg << 4) + off, 1, buff);
      dbg_printf("INT# %02x > %04X:%04X (0x%08x) %s%s\n", i, seg, off,
         (unsigned) ((seg << 4) + off), bx_dbg_ivt_desc(i),
         (buff[0] == 0xcf) ? " ; dummy iret" : "");
    }
    if (all) dbg_printf("You can list individual entries with 'info ivt [NUM]' or groups with 'info ivt [NUM] [NUM]'\n");
  }
  else
    dbg_printf("cpu in protected mode, use info idt\n");
}

static void bx_dbg_print_tss(Bit8u *tss, int len)
{
  if (len<104) {
    dbg_printf("Invalid tss length (limit must be greater then 103)\n");
    return;
  }

  dbg_printf("ss:esp(0): 0x%04x:0x%08x\n",
    *(Bit16u*)(tss+8), *(Bit32u*)(tss+4));
  dbg_printf("ss:esp(1): 0x%04x:0x%08x\n",
    *(Bit16u*)(tss+0x10), *(Bit32u*)(tss+0xc));
  dbg_printf("ss:esp(2): 0x%04x:0x%08x\n",
    *(Bit16u*)(tss+0x18), *(Bit32u*)(tss+0x14));
  dbg_printf("cr3: 0x%08x\n", *(Bit32u*)(tss+0x1c));
  dbg_printf("eip: 0x%08x\n", *(Bit32u*)(tss+0x20));
  dbg_printf("eflags: 0x%08x\n", *(Bit32u*)(tss+0x24));

  dbg_printf("cs: 0x%04x ds: 0x%04x ss: 0x%04x\n",
    *(Bit16u*)(tss+76), *(Bit16u*)(tss+84), *(Bit16u*)(tss+80));
  dbg_printf("es: 0x%04x fs: 0x%04x gs: 0x%04x\n",
    *(Bit16u*)(tss+72), *(Bit16u*)(tss+88), *(Bit16u*)(tss+92));

  dbg_printf("eax: 0x%08x  ebx: 0x%08x  ecx: 0x%08x  edx: 0x%08x\n",
    *(Bit32u*)(tss+0x28), *(Bit32u*)(tss+0x34), *(Bit32u*)(tss+0x2c), *(Bit32u*)(tss+0x30));
  dbg_printf("esi: 0x%08x  edi: 0x%08x  ebp: 0x%08x  esp: 0x%08x\n",
    *(Bit32u*)(tss+0x40), *(Bit32u*)(tss+0x44), *(Bit32u*)(tss+0x3c), *(Bit32u*)(tss+0x38));

  dbg_printf("ldt: 0x%04x\n", *(Bit16u*)(tss+0x60));
  dbg_printf("i/o map: 0x%04x\n", *(Bit16u*)(tss+0x66));
}

void bx_dbg_info_tss_command(void)
{
  bx_dbg_sreg_t tr;
  BX_CPU(dbg_cpu)->dbg_get_tr(&tr);

  bx_address base = (tr.des_l>>16) |
                   ((tr.des_h<<16)&0x00ff0000) | (tr.des_h & 0xff000000);
#if BX_SUPPORT_X86_64
  base |= (Bit64u)(tr.dword3) << 32;
#endif
  Bit32u len = (tr.des_l & 0xffff) + 1;

  dbg_printf("tr:s=0x%x, base=0x" FMT_ADDRX ", valid=%u\n",
      (unsigned) tr.sel, base, (unsigned) tr.valid);

  bx_phy_address paddr = 0;
  if (BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(base, &paddr)) {
    bx_dbg_print_tss(BX_MEM(0)->get_vector(paddr), len);
  }
  else {
    dbg_printf("bx_dbg_info_tss_command: failed to get physical address for TSS.BASE !");
  }
}

/*
 * this implements the info device command in the debugger.
 * info device - list devices supported by this command
 * info device [string] - shows the state of device specified in string
 * info device [string] [string] - shows the state of device with options
 */
void bx_dbg_info_device(const char *dev, const char *args)
{
  debug_info_t *temp = NULL;
  unsigned i, string_i;
  int argc = 0;
  char *argv[16];
  char *ptr;
  char string[512];
  size_t len;

  if (strlen(dev) == 0) {
    if (bx_debug_info_list == NULL) {
      dbg_printf("info device list: no device registered\n");
    } else {
      dbg_printf("devices supported by 'info device':\n\n");
      temp = bx_debug_info_list;
      while (temp) {
        dbg_printf("%s\n", temp->name);
        temp = temp->next;
      }
      dbg_printf("\n");
    }
  } else {
    if (bx_dbg_info_find_device(dev, &temp)) {
      if (temp->device != NULL) {
        len = strlen(args);
        memset(argv, 0, sizeof(argv));
        if (len > 0) {
          char *options = new char[len + 1];
          strcpy(options, args);
          ptr = strtok(options, ",");
          while (ptr) {
            string_i = 0;
            for (i=0; i<strlen(ptr); i++) {
              if (!isspace(ptr[i])) string[string_i++] = ptr[i];
            }
            string[string_i] = '\0';
            if (argc < 16) {
              argv[argc++] = strdup(string);
            } else {
              BX_PANIC (("too many parameters, max is 16\n"));
              break;
            }
            ptr = strtok(NULL, ",");
          }
          delete [] options;
        }
        temp->device->debug_dump(argc, argv);
        for (i = 0; i < (unsigned)argc; i++) {
          if (argv[i] != NULL) {
            free(argv[i]);
            argv[i] = NULL;
          }
        }
      } else {
        BX_PANIC(("info device: device pointer is NULL"));
      }
    } else {
      dbg_printf("info device: '%s' not found\n", dev);
    }
  }
}

//
// Reports from various events
//

void bx_dbg_iac_report(unsigned vector, unsigned irq)
{
  if (bx_guard.report.irq) {
    dbg_printf("event at t=" FMT_LL "d IRQ irq=%u vec=%x\n",
      bx_pc_system.time_ticks(), irq, vector);
  }
}

void bx_dbg_a20_report(unsigned val)
{
  if (bx_guard.report.a20) {
    dbg_printf("event at t=" FMT_LL "d A20 val=%u\n",
      bx_pc_system.time_ticks(), val);
  }
}

void bx_dbg_io_report(Bit32u port, unsigned size, unsigned op, Bit32u val)
{
  if (bx_guard.report.io) {
    dbg_printf("event at t=" FMT_LL "d IO addr=0x%x size=%u op=%s val=0x%x\n",
      bx_pc_system.time_ticks(),
      port,
      size,
      (op==BX_READ) ? "read" : "write",
      (unsigned) val);
  }
}

void bx_dbg_dma_report(bx_phy_address addr, unsigned len, unsigned what, Bit32u val)
{
  if (bx_dbg_batch_dma.this_many == 0) {
    dbg_printf("%s: DMA batch this_many=0.\n", argv0);
    bx_dbg_exit(1);
  }

  // if Q is full, post events (and flush)
  if (bx_dbg_batch_dma.Qsize >= bx_dbg_batch_dma.this_many) {
    dbg_printf("%s: DMA batch Q was not flushed.\n", argv0);
    bx_dbg_exit(1);
  }

  // if Q already has MAX elements in it
  if (bx_dbg_batch_dma.Qsize >= BX_BATCH_DMA_BUFSIZE) {
    dbg_printf("%s: DMA batch buffer overrun.\n", argv0);
    bx_dbg_exit(1);
  }

  bx_dbg_batch_dma.Qsize++;
  bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].addr = addr;
  bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].len  = len;
  bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].what = what;
  bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].val  = val;
  bx_dbg_batch_dma.Q[bx_dbg_batch_dma.Qsize-1].time = bx_pc_system.time_ticks();

  // if Q is full, post events (and flush)
  if (bx_dbg_batch_dma.Qsize >= bx_dbg_batch_dma.this_many)
    bx_dbg_post_dma_reports();
}

void bx_dbg_post_dma_reports(void)
{
  unsigned i;
  unsigned addr, len, what, val;
  unsigned last_addr, last_len, last_what;
  unsigned print_header;
  unsigned first_iteration;

  if (bx_guard.report.dma) {
    if (bx_dbg_batch_dma.Qsize == 0) return; // nothing batched to print

    // compress output so all contiguous DMA ops of the same type and size
    // are printed on the same line
    last_addr = bx_dbg_batch_dma.Q[0].addr;
    last_len  = bx_dbg_batch_dma.Q[0].len;
    last_what = bx_dbg_batch_dma.Q[0].what;
    first_iteration = 1;

    for (i=0; i<bx_dbg_batch_dma.Qsize; i++) {
      addr = bx_dbg_batch_dma.Q[i].addr;
      len  = bx_dbg_batch_dma.Q[i].len;
      what = bx_dbg_batch_dma.Q[i].what;
      val  = bx_dbg_batch_dma.Q[i].val;

      if (len != last_len)
        print_header = 1;
      else if (what != last_what)
        print_header = 1;
      else if (addr != (last_addr + last_len))
        print_header = 1;
      else
        print_header = 0;

      // now store current values for next iteration
      last_addr = addr;
      last_len  = len;
      last_what = what;

      if (print_header) {
        if (!first_iteration) // need return from previous line
          dbg_printf("\n");
        else
          first_iteration = 0;
        // need to output the event header
        dbg_printf("event at t=" FMT_LL "d DMA addr=0x%x size=%u op=%s val=0x%x",
                         bx_pc_system.time_ticks(),
                         addr, len, (what==BX_READ) ? "read" : "write", val);
        print_header = 0;
      }
      else {
        // *no* need to output the event header
        dbg_printf(" 0x%x", val);
      }
    }
    if (bx_dbg_batch_dma.Qsize)
      dbg_printf("\n");
  }

  // empty Q, regardless of whether reports are printed
  bx_dbg_batch_dma.Qsize = 0;
}

void bx_dbg_dump_table(void)
{
  Bit32u lin, start_lin; // show only low 32 bit
  bx_phy_address phy, start_phy; // start of a valid translation interval
  bx_bool valid;

  if (! BX_CPU(dbg_cpu)->cr0.get_PG()) {
    printf("paging off\n");
    return;
  }

  printf("cr3: 0x"FMT_PHY_ADDRX"\n", (bx_phy_address)BX_CPU(dbg_cpu)->cr3);

  lin = 0;
  phy = 0;

  start_lin = 1;
  start_phy = 2;
  while(1) {
    valid = BX_CPU(dbg_cpu)->dbg_xlate_linear2phy(lin, &phy);
    if(valid) {
      if((lin - start_lin) != (phy - start_phy)) {
        if(start_lin != 1)
          dbg_printf("0x%08x-0x%08x -> 0x"FMT_PHY_ADDRX"-0x"FMT_PHY_ADDRX"\n",
            start_lin, lin - 1, start_phy, start_phy + (lin-1-start_lin));
        start_lin = lin;
        start_phy = phy;
      }
    } else {
      if(start_lin != 1)
        dbg_printf("0x%08x-0x%08x -> 0x"FMT_PHY_ADDRX"-0x"FMT_PHY_ADDRX"\n",
          start_lin, lin - 1, start_phy, start_phy + (lin-1-start_lin));
      start_lin = 1;
      start_phy = 2;
    }

    if(lin == 0xfffff000) break;
    lin += 0x1000;
  }
  if(start_lin != 1)
    dbg_printf("0x%08x-0x%08x -> 0x"FMT_PHY_ADDRX"-0x"FMT_PHY_ADDRX"\n",
         start_lin, 0xffffffff, start_phy, start_phy + (0xffffffff-start_lin));
}

void bx_dbg_print_help(void)
{
  dbg_printf("h|help - show list of debugger commands\n");
  dbg_printf("h|help command - show short command description\n");
  dbg_printf("-*- Debugger control -*-\n");
  dbg_printf("    help, q|quit|exit, set, instrument, show, trace, trace-reg,\n");
  dbg_printf("    trace-mem, u|disasm, ldsym, slist\n");
  dbg_printf("-*- Execution control -*-\n");
  dbg_printf("    c|cont|continue, s|step, p|n|next, modebp, vmexitbp\n");
  dbg_printf("-*- Breakpoint management -*-\n");
  dbg_printf("    vb|vbreak, lb|lbreak, pb|pbreak|b|break, sb, sba, blist,\n");
  dbg_printf("    bpe, bpd, d|del|delete, watch, unwatch\n");
  dbg_printf("-*- CPU and memory contents -*-\n");
  dbg_printf("    x, xp, setpmem, writemem, crc, info,\n");
  dbg_printf("    r|reg|regs|registers, fp|fpu, mmx, sse, sreg, dreg, creg,\n");
  dbg_printf("    page, set, ptime, print-stack, ?|calc\n");
  dbg_printf("-*- Working with bochs param tree -*-\n");
  dbg_printf("    show \"param\", restore\n");
}

void bx_dbg_calc_command(Bit64u value)
{
  dbg_printf("0x" FMT_LL "x " FMT_LL "d\n", value, value);
}

Bit8u bx_dbg_get_reg8l_value(unsigned reg)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    return BX_CPU(dbg_cpu)->get_reg8l(reg);

  dbg_printf("Unknown 8BL register [%d] !!!\n", reg);
  return 0;
}

Bit8u bx_dbg_get_reg8h_value(unsigned reg)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    return BX_CPU(dbg_cpu)->get_reg8h(reg);

  dbg_printf("Unknown 8BH register [%d] !!!\n", reg);
  return 0;
}

Bit16u bx_dbg_get_reg16_value(unsigned reg)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    return BX_CPU(dbg_cpu)->get_reg16(reg);

  dbg_printf("Unknown 16B register [%d] !!!\n", reg);
  return 0;
}

Bit32u bx_dbg_get_reg32_value(unsigned reg)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    return BX_CPU(dbg_cpu)->get_reg32(reg);

  dbg_printf("Unknown 32B register [%d] !!!\n", reg);
  return 0;
}

Bit64u bx_dbg_get_reg64_value(unsigned reg)
{
#if BX_SUPPORT_X86_64
  if (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS)
    return BX_CPU(dbg_cpu)->get_reg64(reg);
#endif

  dbg_printf("Unknown 64B register [%d] !!!\n", reg);
  return 0;
}

void bx_dbg_set_reg8l_value(unsigned reg, Bit8u value)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    BX_CPU(dbg_cpu)->set_reg8l(reg, value);
  else
    dbg_printf("Unknown 8BL register [%d] !!!\n", reg);
}

void bx_dbg_set_reg8h_value(unsigned reg, Bit8u value)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    BX_CPU(dbg_cpu)->set_reg8h(reg, value);
  else
    dbg_printf("Unknown 8BH register [%d] !!!\n", reg);
}

void bx_dbg_set_reg16_value(unsigned reg, Bit16u value)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    BX_CPU(dbg_cpu)->set_reg16(reg, value);
  else
    dbg_printf("Unknown 16B register [%d] !!!\n", reg);
}

void bx_dbg_set_reg32_value(unsigned reg, Bit32u value)
{
  if (reg < 8 || (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS))
    BX_CPU(dbg_cpu)->set_reg32(reg, value);
  else
    dbg_printf("Unknown 32B register [%d] !!!\n", reg);
}

void bx_dbg_set_reg64_value(unsigned reg, Bit64u value)
{
#if BX_SUPPORT_X86_64
  if (BX_CPU(dbg_cpu)->long64_mode() && reg < BX_GENERAL_REGISTERS)
    BX_CPU(dbg_cpu)->set_reg64(reg, value);
  else
#endif
    dbg_printf("Unknown 64B register [%d] !!!\n", reg);
}

void bx_dbg_set_rip_value(bx_address value)
{
#if BX_SUPPORT_X86_64
  if ((value >> 32) != 0 && ! BX_CPU(dbg_cpu)->long64_mode()) {
    dbg_printf("Cannot set EIP to 64-bit value when not in long64 mode !\n");
  }
  else
#endif
    BX_CPU(dbg_cpu)->dbg_set_eip(value);
}

Bit64u bx_dbg_get_opmask_value(unsigned reg)
{
#if BX_SUPPORT_EVEX
  if (reg < 8)
    return BX_CPU(dbg_cpu)->get_opmask(reg);
#endif

  dbg_printf("Unknown OPMASK register k%d !!!\n", reg);
  return 0;
}

Bit16u bx_dbg_get_selector_value(unsigned int seg_no)
{
  bx_dbg_sreg_t sreg;

  if (seg_no > 5) {
    dbg_printf("Error: seg_no out of bounds\n");
    return 0;
  }
  BX_CPU(dbg_cpu)->dbg_get_sreg(&sreg, seg_no);
  if (!sreg.valid) {
    dbg_printf("Error: segment valid bit cleared\n");
    return 0;
  }
  return sreg.sel;
}

Bit16u bx_dbg_get_ip(void)
{
  return BX_CPU(dbg_cpu)->get_ip();
}

Bit32u bx_dbg_get_eip(void)
{
  return BX_CPU(dbg_cpu)->get_eip();
}

bx_address bx_dbg_get_instruction_pointer(void)
{
  return BX_CPU(dbg_cpu)->get_instruction_pointer();
}

bx_bool bx_dbg_read_pmode_descriptor(Bit16u sel, bx_descriptor_t *descriptor)
{
  bx_selector_t selector;
  Bit32u dword1, dword2;
  bx_address desc_base;

  /* if selector is NULL, error */
  if ((sel & 0xfffc) == 0) {
    dbg_printf("bx_dbg_read_pmode_descriptor: Dereferencing a NULL selector!\n");
    return 0;
  }

  /* parse fields in selector */
  parse_selector(sel, &selector);

  if (selector.ti) {
    // LDT
    if (((Bit32u)selector.index*8 + 7) > BX_CPU(dbg_cpu)->ldtr.cache.u.segment.limit_scaled) {
      dbg_printf("bx_dbg_read_pmode_descriptor: selector (0x%04x) > LDT size limit\n", selector.index*8);
      return 0;
    }
    desc_base = BX_CPU(dbg_cpu)->ldtr.cache.u.segment.base;
  }
  else {
    // GDT
    if (((Bit32u)selector.index*8 + 7) > BX_CPU(dbg_cpu)->gdtr.limit) {
      dbg_printf("bx_dbg_read_pmode_descriptor: selector (0x%04x) > GDT size limit\n", selector.index*8);
      return 0;
    }
    desc_base = BX_CPU(dbg_cpu)->gdtr.base;
  }

  if (! bx_dbg_read_linear(dbg_cpu, desc_base + selector.index * 8,     4, (Bit8u*) &dword1)) {
    dbg_printf("bx_dbg_read_pmode_descriptor: cannot read selector 0x%04x (index=0x%04x)\n", sel, selector.index);
    return 0;
  }
  if (! bx_dbg_read_linear(dbg_cpu, desc_base + selector.index * 8 + 4, 4, (Bit8u*) &dword2)) {
    dbg_printf("bx_dbg_read_pmode_descriptor: cannot read selector 0x%04x (index=0x%04x)\n", sel, selector.index);
    return 0;
  }

  memset (descriptor, 0, sizeof (bx_descriptor_t));
  parse_descriptor(dword1, dword2, descriptor);

  if (!descriptor->segment) {
    dbg_printf("bx_dbg_read_pmode_descriptor: selector 0x%04x points to a system descriptor and is not supported!\n", sel);
    return 0;
  }

  /* #NP(selector) if descriptor is not present */
  if (descriptor->p==0) {
    dbg_printf("bx_dbg_read_pmode_descriptor: descriptor 0x%04x not present!\n", sel);
    return 0;
  }

  return 1;
}

void bx_dbg_load_segreg(unsigned seg_no, unsigned value)
{
  bx_segment_reg_t sreg;

  if (seg_no > 6) {
    dbg_printf("bx_dbg_load_segreg: unknown segment register !");
    return;
  }

  if (value > 0xffff) {
    dbg_printf("bx_dbg_load_segreg: segment selector out of limits !");
    return;
  }

  unsigned cpu_mode = BX_CPU(dbg_cpu)->get_cpu_mode();
  if (cpu_mode == BX_MODE_LONG_64) {
    dbg_printf("bx_dbg_load_segreg: not supported in long64 mode !");
    return;
  }

  if (! BX_CPU(dbg_cpu)->protected_mode()) {
    parse_selector(value, &sreg.selector);

    sreg.cache.valid   = SegValidCache;
    sreg.cache.p       = 1;
    sreg.cache.dpl     = (cpu_mode == BX_MODE_IA32_V8086);
    sreg.cache.segment = 1;
    sreg.cache.type    = BX_DATA_READ_WRITE_ACCESSED;

    sreg.cache.u.segment.base = sreg.selector.value << 4;
    sreg.cache.u.segment.limit_scaled = 0xffff;
    sreg.cache.u.segment.g            = 0;
    sreg.cache.u.segment.d_b          = 0;
    sreg.cache.u.segment.avl          = 0;
    sreg.selector.rpl                 = (cpu_mode == BX_MODE_IA32_V8086);

    BX_CPU(dbg_cpu)->dbg_set_sreg(seg_no, &sreg);
  }
  else {
    parse_selector(value, &sreg.selector);
    if (bx_dbg_read_pmode_descriptor(value, &sreg.cache)) {
      BX_CPU(dbg_cpu)->dbg_set_sreg(seg_no, &sreg);
    }
  }
}

bx_address bx_dbg_get_laddr(Bit16u sel, bx_address ofs)
{
  bx_address laddr;

  if (BX_CPU(dbg_cpu)->protected_mode()) {
    bx_descriptor_t descriptor;
    Bit32u lowaddr, highaddr;

    if (! bx_dbg_read_pmode_descriptor(sel, &descriptor))
      return 0;

    // expand-down
    if (IS_DATA_SEGMENT(descriptor.type) && IS_DATA_SEGMENT_EXPAND_DOWN(descriptor.type)) {
      lowaddr = descriptor.u.segment.limit_scaled;
      highaddr = descriptor.u.segment.g ? 0xffffffff : 0xffff;
    }
    else {
      lowaddr = 0;
      highaddr = descriptor.u.segment.limit_scaled;
    }

    if (ofs < lowaddr || ofs > highaddr) {
      dbg_printf("WARNING: Offset %08X is out of selector %04x limit (%08x...%08x)!\n",
        ofs, sel, lowaddr, highaddr);
    }

    laddr = descriptor.u.segment.base + ofs;
  }
  else {
    laddr = sel * 16 + ofs;
  }

  return laddr;
}

void bx_dbg_step_over_command()
{
  bx_address laddr = BX_CPU(dbg_cpu)->guard_found.laddr;

  if (! bx_dbg_read_linear(dbg_cpu, laddr, 16, bx_disasm_ibuf))
  {
    return;
  }

  x86_insn insn = bx_disassemble.decode(IS_CODE_32(BX_CPU(dbg_cpu)->guard_found.code_32_64),
      IS_CODE_64(BX_CPU(dbg_cpu)->guard_found.code_32_64),
      BX_CPU(dbg_cpu)->get_segment_base(BX_SEG_REG_CS),
      BX_CPU(dbg_cpu)->guard_found.eip, bx_disasm_ibuf, bx_disasm_tbuf);

  unsigned b1 = insn.b1;

  switch(b1) {
    // Jcc short
    case 0x70:
    case 0x71:
    case 0x72:
    case 0x73:
    case 0x74:
    case 0x75:
    case 0x76:
    case 0x77:
    case 0x78:
    case 0x79:
    case 0x7A:
    case 0x7B:
    case 0x7C:
    case 0x7D:
    case 0x7E:
    case 0x7F:

    // Jcc near
    case 0x180:
    case 0x181:
    case 0x182:
    case 0x183:
    case 0x184:
    case 0x185:
    case 0x186:
    case 0x187:
    case 0x188:
    case 0x189:
    case 0x18A:
    case 0x18B:
    case 0x18C:
    case 0x18D:
    case 0x18E:
    case 0x18F:

    // jcxz
    case 0xE3:

    // retn n
    case 0xC2:
    // retn
    case 0xC3:
    // retf n
    case 0xCA:
    // retf
    case 0xCB:
    // iret
    case 0xCF:

    // jmp near
    case 0xE9:
    // jmp far
    case 0xEA:
    // jmp short
    case 0xEB:
      bx_dbg_stepN_command(dbg_cpu, 1);
      return;
    // jmp absolute indirect
    case 0xFF:
      switch (insn.nnn) {
        // near
        case 4:
        // far
        case 5:
         bx_dbg_stepN_command(dbg_cpu, 1);
         return;
      }
  }

  // calls, ints, loops and so on
  int BpId = bx_dbg_lbreakpoint_command(bkStepOver, laddr + insn.ilen);
  if (BpId == -1) {
    dbg_printf("bx_dbg_step_over_command:: Failed to set lbreakpoint !\n");
    return;
  }

  bx_dbg_continue_command();

  if (bx_dbg_del_lbreak(BpId))
    bx_dbg_breakpoint_changed();
}

#endif /* if BX_DEBUGGER */
