/////////////////////////////////////////////////////////////////////////
// $Id: iodebug.cc,v 1.15 2002/11/19 05:47:45 bdenney Exp $
/////////////////////////////////////////////////////////////////////////
//
#include "bochs.h"
#if BX_IODEBUG_SUPPORT



bx_iodebug_c bx_iodebug;
bx_iodebug_c *bx_iodebug_ptr;

  struct bx_iodebug_s_type {
    bx_bool enabled;
    unsigned int register_select;
    Bit32u registers[2];
    Bit32u monitored_mem_areas_start[BX_IODEBUG_MAX_AREAS];
    Bit32u monitored_mem_areas_end[BX_IODEBUG_MAX_AREAS];
  } bx_iodebug_s;




// Constructor
bx_iodebug_c::bx_iodebug_c( void )
{
  put("IODEBUG");
  settype(IODEBUGLOG);

}





// Destructor
bx_iodebug_c::~bx_iodebug_c( void )
{
}





void bx_iodebug_c::init(void)
{
  int i;

  DEV_register_ioread_handler(this, read_handler, 0x8A00,"BOCHS IODEBUG", 7);
  DEV_register_iowrite_handler(this, write_handler, 0x8A00,"BOCHS IODEBUG", 7);
  DEV_register_iowrite_handler(this, write_handler, 0x8A01,"BOCHS IODEBUG", 7);
//  fprintf( stderr, "IODEBUG initialized\n");

  bx_iodebug_s.enabled = 0;
  bx_iodebug_s.register_select = 0;
  for(i=0;i<BX_IODEBUG_MAX_AREAS;i++) {
    bx_iodebug_s.monitored_mem_areas_start[i] = 0;
    bx_iodebug_s.monitored_mem_areas_end[i] = 0;
  }
}

void bx_iodebug_c::reset(unsigned type)
{
}


Bit32u bx_iodebug_c::read_handler(void *this_ptr, Bit32u addr, unsigned io_len)
{
  bx_iodebug_ptr = (bx_iodebug_c *) this_ptr;
  return( bx_iodebug_ptr->read(addr, io_len) );
}






Bit32u bx_iodebug_c::read( Bit32u addr, unsigned io_len )
{

  if(bx_iodebug_s.enabled) return(0x8A00);
  return(0);
}










void bx_iodebug_c::write_handler(void *this_ptr, Bit32u addr, Bit32u dvalue, unsigned io_len)
{
  bx_iodebug_c *class_ptr = (bx_iodebug_c *) this_ptr;
  class_ptr->write( addr, dvalue, io_len );
}






void bx_iodebug_c::write( Bit32u addr, Bit32u dvalue, unsigned int io_len )
{


//  fprintf(stderr, "IODEBUG addr: %4x\tdvalue: %8x\tio_len: %8x\n", (unsigned int)addr, (unsigned int)dvalue, io_len);

  if( addr == 0x8A01 && io_len == 2 )
  {
      bx_iodebug_s.registers[bx_iodebug_s.register_select] =
        (bx_iodebug_s.registers[bx_iodebug_s.register_select] << 16) +
	(dvalue & 0x0000FFFF );
  }

  if( (addr != 0x8A00) || (io_len != 2) ) return;

  if( !bx_iodebug_s.enabled )
  {
    if( dvalue == 0x8A00 )
    {
      bx_iodebug_s.enabled = 1;
//      fprintf(stderr, "IODEBUG enabled\n");
      bx_iodebug_s.registers[0] = 0;
      bx_iodebug_s.registers[1] = 0;
    }
    return;
  }

  switch( dvalue )
  {
    case( 0x8A01 ):
      bx_iodebug_s.register_select = 0;
//      fprintf( stderr, "IODEBUG register 0 selected\n");
      break;

    case( 0x8A02 ):
      bx_iodebug_s.register_select = 1;
//      fprintf( stderr, "IODEBUG register 1 selected\n");
      break;

    case( 0x8A80 ):
      bx_iodebug_s.register_select = 0;
      bx_iodebug_c::add_range(
          bx_iodebug_s.registers[0],
	  bx_iodebug_s.registers[1]);
      bx_iodebug_s.registers[0] = 0;
      bx_iodebug_s.registers[1] = 0;
      break;

#if BX_DEBUGGER
    case( 0x8AE0 ):
      fprintf( stderr, "request return to dbg prompt received, 0x8AE0 command (iodebug)\n");
      bx_guard.interrupt_requested=1;
      break;

    case( 0x8AE2):
      fprintf( stderr, "request made by the guest os to disable tracing, iodebug port 0x8A00->0x8AE2\n");
      BX_CPU(dbg_cpu)->trace = 0;
      break;

    case( 0x8AE3 ):
      fprintf( stderr, "request made by the guest os to enable tracing, iodebug port 0x8A00->0x8AE3\n");
      BX_CPU(dbg_cpu)->trace = 1;
      break;

    case( 0x8AE4 ):
      fprintf( stderr, "request made by the guest os to disable register tracing, iodebug port 0x8A00->0x8AE4\n");
      BX_CPU(dbg_cpu)->trace_reg = 0;
      break;

    case( 0x8AE5 ):
      fprintf( stderr, "request made by the guest os to enable register tracing, iodebug port 0x8A00->0x8AE5\n");
      BX_CPU(dbg_cpu)->trace_reg = 1;
      break;

#endif

    case( 0x8AFF ):
      bx_iodebug_s.enabled = 0;
//      fprintf( stderr, "IODEBUG device deactivated\n");
//      break;

//    default:
//      fprintf(stderr,"IODEBUG unsupported register code\n");
  }
}








// Static function
void bx_iodebug_c::mem_write( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
{
  Bit32u data32;
  Bit16u data16;
  Bit8u  data8;

  unsigned int area;
  if( !bx_iodebug_s.enabled ) return;

  area = bx_iodebug_c::range_test( addr, len );
  // Device is enabled, testing address ranges
  if( area )
  {
    area--;
#if BX_DEBUGGER
  fprintf( stdout, "%s @ eip: %08X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr);
  bx_guard.interrupt_requested=1;
#else
    fprintf( stderr,
             "IODEBUG write to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t",
	     area,
	     cpu->get_EIP(),
	     bx_iodebug_s.monitored_mem_areas_start[area],
	     bx_iodebug_s.monitored_mem_areas_end[area],
	     (unsigned int)addr);

    data32 = * (Bit32u *)data;
    data16 = (Bit16u)data32;
    data8  = (Bit8u)data32;

    switch(len)
    {
      case(1):
        fprintf(stderr,"%02X\n", (unsigned int)data8);
	break;

      case(2):
        fprintf(stderr,"%04X\n", (unsigned int)data16);
	break;

      case(4):
        fprintf(stderr,"%08X\n", (unsigned int)data32);
	break;

      default:
        fprintf(stderr, "unsupported write size\n");
    }
#endif
  }
}








void bx_iodebug_c::mem_read( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
{
  Bit32u data32;
  Bit16u data16;
  Bit8u  data8;

  unsigned int area;
  if( !bx_iodebug_s.enabled ) return;

  area = bx_iodebug_c::range_test( addr, len );
  // Device is enabled, testing address ranges
  if( area )
  {
    area--;
#if BX_DEBUGGER
  fprintf( stdout, "%s @ eip: %8X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr);
  bx_guard.interrupt_requested=1;
#else
    fprintf( stderr,
             "IODEBUG read to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t",
	     area,
	     cpu->get_EIP(),
	     bx_iodebug_s.monitored_mem_areas_start[area],
	     bx_iodebug_s.monitored_mem_areas_end[area],
	     (unsigned int)addr);
    data32 = * (Bit32u *)data;
    data16 = (Bit16u)data32;
    data8  = (Bit8u)data32;

    switch(len)
    {
      case(1):
        fprintf(stderr,"%02X\n", (unsigned int)data8);
	break;

      case(2):
        fprintf(stderr,"%04X\n", (unsigned int)data16);
	break;

      case(4):
        fprintf(stderr,"%08X\n", (unsigned int)data32);
	break;

      default:
        fprintf(stderr, "unsupported write size\n");
    }
#endif
  }
}







unsigned int bx_iodebug_c::range_test( Bit32u addr, unsigned int len )
{
  unsigned int i;

  for(i=0;i<BX_IODEBUG_MAX_AREAS;i++)
  {
    if( (bx_iodebug_s.monitored_mem_areas_start[i]!=0) ||
        (bx_iodebug_s.monitored_mem_areas_end[i]!=0) )
    {
      if( (Bit32u)(addr+len-1) < bx_iodebug_s.monitored_mem_areas_start[i] )
        continue;
      if( addr < bx_iodebug_s.monitored_mem_areas_end[i] )
      {
        return(++i);
      }
    }	
  }
  return(0);
}






void bx_iodebug_c::add_range( Bit32u addr_start, Bit32u addr_end )
{
  unsigned int i;
  for(i=0;i<BX_IODEBUG_MAX_AREAS;i++)
  {
    if( !bx_iodebug_s.monitored_mem_areas_start[i] &&
        !bx_iodebug_s.monitored_mem_areas_end[i] )
    {
	bx_iodebug_s.monitored_mem_areas_start[i] = addr_start;
	bx_iodebug_s.monitored_mem_areas_end[i] = addr_end;
//	fprintf(stderr, "IODEBUG added range successfully in slot: %i\n",i);
	return;
    }
  }
//  fprintf(stderr, "IODEBUG unable to register memory range, all slots taken\n");
}
#endif /* if BX_IODEBUG_SUPPORT */
