/*
Copyright Notice
================
BOCHS is Copyright 1994 by Kevin P. Lawton.

BOCHS is shareware for PERSONAL USE only.

For more information, read the file 'LICENSE' included in the bochs
distribution.  If you don't have access to this file, or have questions
regarding the licensing policy, the author may be contacted via:

    US Mail:  Kevin Lawton
              528 Lexington St.
              Waltham, MA 02154

    EMail:    bochs@tiac.net
*/






#define BX_MAIN_C 1

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>



#include "bx_bochs.h"
#include "iodev/iodev.h"


extern Bit32u instruction_count;


/* prototypes */
static void bx_usage(char *progname);
static void bx_init_processor(void);
void bx_cpu_loop(void);

/* typedefs */


/* the device id and stepping id are loaded into DH & DL upon processor
   startup.  for device id: 3 = 80386, 4 = 80486.  just make up a
   number for the stepping (revision) id. */
#define BX_DEVICE_ID     2
#define BX_STEPPING_ID   0


bx_cpu_t bx_cpu;




FILE *bx_logfd; /* for logging bx_printf() messages */
Boolean bx_use_video = 0; /* default to stdout */



void bx_sanity_checks();

extern Boolean report_vm_access;
extern Bit32u bx_prev_eip;



Bit8u bx_io_handler_id[0x10000];  /* 64K */

struct {
  Bit8u (* read_funct) (Bit32u addr);               /* read handler */
  void  (* write_funct) (Bit32u addr, Bit8u value); /* write handler */
  char *        handler_name;                       /* name of device */
  int           irq;                                /* associated irq if any */
  } bx_io_handler[BX_MAX_IO_DEVICES];

/* more for informative purposes, the names of the divices which
 * are use each of the IRQ 0..15 lines are stored here
 */
char *bx_irq_handler_name[BX_MAX_IRQS];

/* number of currently registered io handlers (devices) */
Bit8u bx_num_io_handles = 0;




/* for efficient repetitive checking of multiple ansynchronous
 * events, the bx_async_event flag is set if any async event occur.
 * That is, for timer events, file descriptor events, and by the PIC.
 */
Boolean bx_async_event = 0;
   
/* after an asynchronous event is determined, the following flags
 * determine which type of event(s) occured */
Boolean bx_timer_event = 0; /* the timer expired */
int     bx_timer_index;

Boolean bx_fd_event    = 0; /* a file descriptor event occured */


/* define a variable signifying an event on the corresponding pins.
   INTR is the maskable interrupt pin.  on the x86, it is checked
     after completing every instruction to determine whether a hardware
     interrupt request is pending.  this check can be masked (blocked)
     by clearing the interrupt flag.
     The bx_INTR flag is set by the PIC.
     The bx_INTR_vector variable is also set by the PIC so the CPU can
     determine which interrupt occurred.
   NMI is he non-maskable interrupt pin.  Upon an NMI, the x86 aborts
     program execution immediately after completing the current instruction.
 */
Boolean bx_INTR = 0;
Bit8u   bx_INTR_vector;
Boolean bx_NMI  = 0;



/* for now, if an INTR occurs and is not masked, a C function is called
 * to handle the interrupt.  Functions register themselves with and
 * associated interrupt number 0..255 by means of bx_register_int_vector()
 * NOTE: IRQ's 0..7  = interrupt 08h..0Fh
 *       IRQ's 8..15 = interrupt 70h..77h
 */
void (*bx_interrupt_table[256])(int);


/* To allow IO devices to register functions to be called when events
 * occur on associated file descriptors, we need to define a set of
 * file descriptors to select() on, and a couple arrays to hold the
 * file descriptors, and the function pointers
 */
fd_set fd_read_set, fd_write_set, fd_exception_set; /* for select() */
int max_fd = 0; /* maximum fd (to pass to select() ) */


int registered_fds[BX_MAX_REGISTERED_FDS]; /* values of fd's registered */
void (* registered_fd_handler[BX_MAX_REGISTERED_FDS])(void); /* function ptrs*/
int num_registered_fds = 0; /* number of currently registered fd's */


bx_timer_t bx_timer[BX_MAX_TIMERS];
int        bx_num_timers = 0;
int        bx_min_timer_index;


/* local prototypes */
void bx_iodev_init(void);
void bx_start_timers(void);
void bx_fd_poll_timer(void);

  sigset_t set, oldset;
  unsigned i, b = 1;
  Bit8u val;
  fd_set read_set, write_set, exception_set;
  struct timeval timeout;





  int
main(int argc, char *argv[])
{
  int n;

  bx_logfd = stderr;


  n = 2;
  while (n <= argc) {
    if (!strcmp(argv[n-1], "-log")) {
      bx_logfd = fopen(argv[n], "w");
      if (!bx_logfd) {
        fprintf(stderr, "could not open log file '%s'\n", argv[n]);
        exit(1);
        }
      n += 2;
      }
    else if (!strcmp(argv[n-1], "-sanity-check")) {
      bx_sanity_checks();
      exit(0);
      n += 1;
      }
    else if (!strcmp(argv[n-1], "-use-mda")) {
      bx_use_video = 1;
      n += 1;
      }
    else {
      bx_usage(argv[0]);
      }
    }


  bx_init_processor();
  bx_iodev_setup();

  bx_invoke_interrupt(0x19); /* bootstrap loader */

  sigemptyset( &set );
  sigaddset( &set, SIGVTALRM );

  val = bx_inp(0x21);
  bx_outp(0x21, val & ~(1<<0));

  bx_cpu_loop();

  return(0);
}

  void
bx_usage(char *progname)
{
  fprintf(stderr, "Usage: %s [-sanity-check] \n", progname);
  exit(-1);
}

  void
bx_invalid_instruction(void)
{
  bx_printf(0, "eip = %08x\n", bx_cpu.eip);
  bx_printf(0, "CS = %04x\n", bx_cpu.cs.selector.value);
  bx_printf(0, "count = %08x\n", instruction_count);
  bx_printf(1, "Invalid instruction\n");
}


  Bit8u
bx_fetch_next_byte(void)
{
  Bit8u next_bit8;

  bx_access_virtual(&bx_cpu.cs, bx_cpu.eip, 1, BX_READ, &next_bit8);
  bx_cpu.eip++;
  return(next_bit8);
}


  Bit8u
bx_peek_next_byte(void)
{
  Bit8u next_bit8;

  bx_access_virtual(&bx_cpu.cs, bx_cpu.eip, 1, BX_READ, &next_bit8);
  return(next_bit8);
}

  Bit32u
bx_fetch_next_dword(void)
{
  Bit32u next_bit32;

  bx_access_virtual(&bx_cpu.cs, bx_cpu.eip, 4, BX_READ, &next_bit32);
  bx_cpu.eip += 4;
  return(next_bit32);
}

  Bit16u
bx_fetch_next_word(void)
{
  Bit16u next_bit16;

  bx_access_virtual(&bx_cpu.cs, bx_cpu.eip, 2, BX_READ, &next_bit16);
  bx_cpu.eip += 2;
  return(next_bit16);
}


  void
bx_init_processor(void)
{
  /* general registers */
  bx_cpu.eax = 0; /* processor passed test :-) */
  bx_cpu.ebx = 0; /* undefined */
  bx_cpu.ecx = 0; /* undefined */
  bx_cpu.edx = (BX_DEVICE_ID << 8) | BX_STEPPING_ID;
  bx_cpu.ebp = 0; /* undefined */
  bx_cpu.esi = 0; /* undefined */
  bx_cpu.edi = 0; /* undefined */
  bx_cpu.esp = 0; /* undefined */

  /* status and control flags register set */
  bx_cpu.eflags.cf = 0;
  bx_cpu.eflags.pf = 0;
  bx_cpu.eflags.af = 0;
  bx_cpu.eflags.zf = 0;
  bx_cpu.eflags.sf = 0;
  bx_cpu.eflags.tf = 0;
  bx_cpu.eflags.if_ = 0;
  bx_cpu.eflags.df = 0;
  bx_cpu.eflags.of = 0;
  bx_cpu.eflags.iopl = 0;
  bx_cpu.eflags.nt = 0;
  bx_cpu.eflags.rf = 0;
  bx_cpu.eflags.vm = 0;
  bx_cpu.eflags.ac = 0;

  bx_cpu.eip    = 0x0000FFF0; /* instruction pointer */

  /* segment registers & descriptor caches */

  bx_cpu.cs.selector.value =     0xF000;
  bx_cpu.cs.selector.index =     0xF000;
  bx_cpu.cs.base           = 0xFFFF0000;
  bx_cpu.cs.limit          =     0xFFFF;
  bx_cpu.cs.attributes.d   =          0;
  bx_cpu.cs.attributes.dpl =          0;

  bx_cpu.ds.selector.value =     0x0000;
  bx_cpu.ds.selector.index =     0x0000;
  bx_cpu.ds.base           = 0x00000000;
  bx_cpu.ds.limit          =     0xFFFF;
  bx_cpu.ds.attributes.d   =          0;
  bx_cpu.ds.attributes.dpl =          0;

  bx_cpu.ss.selector.value =     0x0000;
  bx_cpu.ss.selector.index =     0x0000;
  bx_cpu.ss.base           = 0x00000000;
  bx_cpu.ss.limit          =     0xFFFF;
  bx_cpu.ss.attributes.d   =          0;
  bx_cpu.ss.attributes.dpl =          0;

  bx_cpu.es.selector.value =     0x0000;
  bx_cpu.es.selector.index =     0x0000;
  bx_cpu.es.base           = 0x00000000;
  bx_cpu.es.limit          =     0xFFFF;
  bx_cpu.es.attributes.d   =          0;
  bx_cpu.es.attributes.dpl =          0;

  bx_cpu.fs.selector.index =     0x0000;
  bx_cpu.fs.selector.index =     0x0000;
  bx_cpu.fs.base           = 0x00000000;
  bx_cpu.fs.limit          =     0xFFFF;
  bx_cpu.fs.attributes.d   =          0;
  bx_cpu.fs.attributes.dpl =          0;

  bx_cpu.gs.selector.index =     0x0000;
  bx_cpu.gs.selector.index =     0x0000;
  bx_cpu.gs.base           = 0x00000000;
  bx_cpu.gs.limit          =     0xFFFF;
  bx_cpu.gs.attributes.d   =          0;
  bx_cpu.gs.attributes.dpl =          0;

  bx_cpu.gdtr.base         = 0x00000000;  /* undefined */
  bx_cpu.gdtr.limit        =     0x0000;  /* undefined */

  bx_cpu.idtr.base         = 0x00000000;
  bx_cpu.idtr.limit        =     0x03FF; /* always byte granular */

  bx_cpu.ldtr.selector.index =     0x0000; /* undefined */
  bx_cpu.ldtr.base           = 0x00000000; /* undefined */
  bx_cpu.ldtr.limit          =     0x0000; /* undefined */

  bx_cpu.tr.selector.index   =     0x0000; /* undefined */
  bx_cpu.tr.base             = 0x00000000; /* undefined */
  bx_cpu.tr.limit            =     0x0000; /* undefined */

  bx_cpu.cr0.pg = 0; /* disable paging */
  bx_cpu.cr0.cd = 1; /* caching disabled */
  bx_cpu.cr0.nw = 1; /* no write-through */
  bx_cpu.cr0.am = 0; /* disable alignment check */
  bx_cpu.cr0.wp = 0; /* disable write-protect */
  bx_cpu.cr0.ne = 0; /* ndp exceptions through int 13H, DOS compat */
  bx_cpu.cr0.et = 0; /* coprocessor not present (for now) */
  bx_cpu.cr0.ts = 0; /* no task switch */
  bx_cpu.cr0.em = 1; /* emulate math coprocessor */
  bx_cpu.cr0.mp = 0; /* wait instructions not trapped */
  bx_cpu.cr0.pe = 0; /* real mode */


  /* debug registers (unimplemented) */
  bx_cpu.dr0 = 0;   /* undefined */
  bx_cpu.dr1 = 0;   /* undefined */
  bx_cpu.dr2 = 0;   /* undefined */
  bx_cpu.dr3 = 0;   /* undefined */
  bx_cpu.dr4 = 0;   /* undefined */
  bx_cpu.dr5 = 0;   /* undefined */
  bx_cpu.dr6 = 0xFFFF0FF0;   /* unimplemented */
  bx_cpu.dr7 = 0x00000000;   /* unimplemented */

  /* test registers 3-7 (unimplemented) */
  bx_cpu.tr3 = 0;   /* undefined */
  bx_cpu.tr4 = 0;   /* undefined */
  bx_cpu.tr5 = 0;   /* undefined */
  bx_cpu.tr6 = 0;   /* undefined */
  bx_cpu.tr7 = 0;   /* undefined */

  bx_init_memory(BX_PHY_MEMSIZE);
}


  void
bx_sanity_checks(void)
{
  Bit8u al, cl, dl, bl, ah, ch, dh, bh;
  Bit16u ax, cx, dx, bx, sp, bp, si, di;
  Bit32u eax, ecx, edx, ebx, esp, ebp, esi, edi;

  bx_cpu.eax = 0xFFEEDDCC;
  bx_cpu.ecx = 0xBBAA9988;
  bx_cpu.edx = 0x77665544;
  bx_cpu.ebx = 0x332211FF;
  bx_cpu.esp = 0xEEDDCCBB;
  bx_cpu.ebp = 0xAA998877;
  bx_cpu.esi = 0x66554433;
  bx_cpu.edi = 0x2211FFEE;

  al = AL;
  cl = CL;
  dl = DL;
  bl = BL;
  ah = AH;
  ch = CH;
  dh = DH;
  bh = BH;

  if ( al != (bx_cpu.eax & 0xFF) ||
       cl != (bx_cpu.ecx & 0xFF) ||
       dl != (bx_cpu.edx & 0xFF) ||
       bl != (bx_cpu.ebx & 0xFF) ||
       ah != ((bx_cpu.eax >> 8) & 0xFF) ||
       ch != ((bx_cpu.ecx >> 8) & 0xFF) ||
       dh != ((bx_cpu.edx >> 8) & 0xFF) ||
       bh != ((bx_cpu.ebx >> 8) & 0xFF) ) {
    bx_printf(1, "problems using BX_READ_8BIT_REG()!\n");
    }

  ax = AX;
  cx = CX;
  dx = DX;
  bx = BX;
  sp = SP;
  bp = BP;
  si = SI;
  di = DI;

  if ( ax != (bx_cpu.eax & 0xFFFF) ||
       cx != (bx_cpu.ecx & 0xFFFF) ||
       dx != (bx_cpu.edx & 0xFFFF) ||
       bx != (bx_cpu.ebx & 0xFFFF) ||
       sp != (bx_cpu.esp & 0xFFFF) ||
       bp != (bx_cpu.ebp & 0xFFFF) ||
       si != (bx_cpu.esi & 0xFFFF) ||
       di != (bx_cpu.edi & 0xFFFF) ) {
    bx_printf(1, "problems using BX_READ_16BIT_REG()!\n");
    }


  eax = EAX;
  ecx = ECX;
  edx = EDX;
  ebx = EBX;
  esp = ESP;
  ebp = EBP;
  esi = ESI;
  edi = EDI;
  

  if ( eax != bx_cpu.eax ||
       ecx != bx_cpu.ecx ||
       edx != bx_cpu.edx ||
       ebx != bx_cpu.ebx ||
       esp != bx_cpu.esp ||
       ebp != bx_cpu.ebp ||
       esi != bx_cpu.esi ||
       edi != bx_cpu.edi ) {
    bx_printf(1, "problems using BX_READ_32BIT_REG()!\n");
    }


  if (sizeof(Bit8u)  != 1  ||  sizeof(Bit8s)  != 1)
    bx_printf(1, "data type Bit8u or Bit8s is not of length 1 byte!\n");
  if (sizeof(Bit16u) != 2  ||  sizeof(Bit16s) != 2)
    bx_printf(1, "data type Bit16u or Bit16s is not of length 2 bytes!\n");
  if (sizeof(Bit32u) != 4  ||  sizeof(Bit32s) != 4)
    bx_printf(1, "data type Bit32u or Bit32s is not of length 4 bytes!\n");
  
  fprintf(stderr, "all sanity checks passed!\n");
}


/* bx_printf()
 *
 * To print warnings, errors, etc, use the variable argument list
 * bx_printf() function.
 *
 * bomb_out       0 = continue, 1 = exit
 * ...            the format and parameters typically passed to printf()
 */

  void
bx_printf(int bomb_out, ...)
{
  va_list ap;
  char *fmt;

  if (bomb_out)
    fprintf(bx_logfd, "bochs: error, ");

  va_start(ap, bomb_out);
  fmt = va_arg(ap, char *);
  vfprintf(bx_logfd, fmt, ap);
  va_end(ap);

  if (bomb_out != 0) exit(bomb_out);
}

  void
bx_iodev_setup(void)
{
  int i;

  bx_iodev_init();

  bx_register_io_handlers();

  bx_bios_post();

  bx_video_message("Devices:\n");
  for (i=0; i < bx_num_io_handles; i++) {
    bx_video_message("  ");
    bx_video_message(bx_io_handler[i].handler_name);
    bx_video_message("\n");
    }


  bx_register_timer( bx_fd_poll_timer, 1000000); /* 10 Hz */

  bx_start_timers();
}





  int
bx_register_io_handler(bx_iodev_t device)
{
  int handle, i;

  if (device.irq != BX_NO_IRQ  &&  device.irq >= BX_MAX_IRQS) {
    bx_printf(1, "IO device %s registered with IRQ=%d above %lu\n",
      device.handler_name, device.irq, (UL) BX_MAX_IRQS-1);
    }

  /* first find existing handle for function or create new one */
  for (handle=0; handle < (int) bx_num_io_handles; handle++) {
    if (bx_io_handler[handle].read_funct == device.read_funct) break;
    }
  if (handle >= (int) bx_num_io_handles) {
    /* no existing handle found, create new one */
    if (bx_num_io_handles >= BX_MAX_IO_DEVICES) {
      bx_printf(0, "too many IO devices installed.\n");
      bx_printf(1, "  try increasing BX_MAX_IO_DEVICES\n");
      }
    if (device.irq != BX_NO_IRQ  &&  bx_irq_handler_name[device.irq]) {
      bx_printf(1, "IRQ %d conflict.  IO devices %s & %s\n",
        device.irq, bx_irq_handler_name[device.irq], device.handler_name);
      }
    if (device.irq != BX_NO_IRQ)
      bx_irq_handler_name[device.irq] = device.handler_name;
    bx_num_io_handles++;
    bx_io_handler[handle].read_funct     = device.read_funct;
    bx_io_handler[handle].write_funct    = device.write_funct;
    bx_io_handler[handle].handler_name   = device.handler_name;
    bx_io_handler[handle].irq            = device.irq;
    }

  /* change table to reflect new handler id for that address */
  for (i=device.start_addr; i <= device.end_addr; i++) {
    if (bx_io_handler_id[i] != 0) {
      bx_printf(0, "IO device address conflict at IO address %lXh\n", (UL) i);
      bx_printf(1, "  conflicting devices: %s & %s\n",
        bx_io_handler[handle].handler_name, bx_io_handler[bx_io_handler_id[i]].handler_name);
      }
    bx_io_handler_id[i] = handle;
    }
  return(1);
}



  void
bx_sigvtalrm_handler(int sig)
{
  Bit32u previous_min;
  Bit32u min_timer;
  struct itimerval new_timer_value;
  int i;

  /* only reinstate the signal handler for those platforms that can't be
     told to automatically */
#ifndef HAVE_SIGACTION
  signal(sig, bx_sigvtalrm_handler);
#endif

  previous_min = bx_timer[bx_min_timer_index].remaining;
  for (i=0; i < bx_num_timers; i++) {
    bx_timer[i].remaining -= previous_min;
    }
  bx_timer[bx_min_timer_index].remaining = bx_timer[bx_min_timer_index].period;

  bx_timer_event = 1;
  bx_timer_index = bx_min_timer_index;
  bx_async_event = 1;


  min_timer = bx_timer[0].remaining;
  bx_min_timer_index = 0;
  bx_timer[0].active = 1;

  /* find minimum remaining timer value */
  for (i=1; i < bx_num_timers; i++) {
    bx_timer[i].active = 1;
    if (bx_timer[i].remaining < min_timer) {
      min_timer = bx_timer[i].remaining;
      bx_min_timer_index = i;
      }
    }

  /* system timer period = 0, not using cyclical timer */
  new_timer_value.it_interval.tv_sec  = 0;
  new_timer_value.it_interval.tv_usec = 0;

  /* system timer remaining value */
  new_timer_value.it_value.tv_usec = min_timer % 1000000;
  new_timer_value.it_value.tv_sec  = min_timer / 1000000;

  setitimer(ITIMER_VIRTUAL, &new_timer_value, NULL);
}




/* bx_outp()
 *
 * Write a byte of data to the IO memory address space.
 */

  void
bx_outp(Bit16u addr, Bit8u value)
{
  Bit8u handle;

#ifdef BX_DEBUG
bx_printf(0, "*** outp(%x, %x)\n", (int) addr, (int) value);
#endif

  handle = bx_io_handler_id[addr];
  (* bx_io_handler[handle].write_funct)((Bit32u) addr, value);
}



/* bx_inp()
 *
 * Read a byte of data from the IO memory address space
 */

  Bit8u
bx_inp(Bit16u addr)
{
  Bit8u handle;
  Bit8u ret;

#ifdef BX_DEBUG
bx_printf(0, "*** inp(%x)", (int) addr);
#endif

  handle = bx_io_handler_id[addr];
  ret = (* bx_io_handler[handle].read_funct)((Bit32u) addr);

#ifdef BX_DEBUG
bx_printf(0, " returns %x\n", (int) ret);
#endif

  return(ret);
}





/* bx_register_int_vector()
 *
 * register a C function to be called when there is an interrupt
 * designated by vector.  The bios_addr is currently unused, but
 * will in the future designate a ROM based address. more later...
 */

  void
bx_register_int_vector(Bit8u vector,
    Bit8u *code, int code_size,
    void (* funct)(int vector))
{
  int i;
  static Bit32u next_bios_addr = 0xE0000;

  /* for now, just calculate bios_addr */

  bx_interrupt_table[vector] = funct;

  bx_set_interrupt_vector(vector, next_bios_addr);

  for (i=0; i < code_size; i++) {
    bx_access_physical(next_bios_addr + i, 1, BX_WRITE, &code[i]);
    }
  next_bios_addr += code_size;
}


#if 0
  void
bx_register_default_int_vector(Bit8u vector, Bit32u bios_addr,
    void (* funct)(int vector))
{
  int i;
  Bit8u code[7];

  /* for now, just calculate bios_addr */
  bios_addr = 0xE0000 + vector*sizeof(code);

  bx_interrupt_table[vector] = funct;

  bx_set_interrupt_vector(vector, bios_addr);

  code[0] = 0xCF; /* IRET */
  code[1] = 0x00;
  code[2] = 0x00;
  code[3] = 0x00;
  code[4] = 0x00;
  code[5] = 0x00;
  code[6] = 0x00;

  for (i=0; i<sizeof(code); i++) {
    bx_access_physical(bios_addr + i, 1, BX_WRITE, &code[i]);
    }
}
#endif

 
/* bx_iodev_init()
 *
 * intialize local constructs used to all the IO device handling features
 */

  void
bx_iodev_init(void)
{
  unsigned i;
  int ret;
  struct sigaction action;

  /* handle 0 maps to the unmapped IO device handler.  Basically any
     ports which don't map to any other device get mapped to this
     handler which does absolutely nothing.
   */
  bx_io_handler[0].read_funct     = bx_unmapped_io_read_handler;
  bx_io_handler[0].write_funct    = bx_unmapped_io_write_handler;
  bx_io_handler[0].handler_name   = "unmapped io";
  bx_io_handler[0].irq            = BX_NO_IRQ;
  bx_num_io_handles = 1;

  /* set unused elements to appropriate values */
  for (i=1; i < BX_MAX_IO_DEVICES; i++) {
    bx_io_handler[i].read_funct          = NULL;
    bx_io_handler[i].write_funct         = NULL;
    }

  for (i=0; i < 0x10000; i++)
    bx_io_handler_id[i] = 0;  /* unmapped IO handle */

  for (i=0; i < BX_MAX_IRQS; i++) {
    bx_irq_handler_name[i] = NULL;
    }

  for (i=0; i < 256; i++) {
    bx_interrupt_table[i] = NULL;
    }

  FD_ZERO(&fd_read_set);
  FD_ZERO(&fd_write_set);
  FD_ZERO(&fd_exception_set);


  /* --- TIMERS --- */
  bx_num_timers = 0;  /* no timers yet installed */

#ifndef HAVE_SIGACTION
  signal(SIGVTALRM, bx_sigvtalrm_handler);
#else /* use POSIX sigaction() instead */

  /* set signal action:  auto restart system calls,  handler remains
     installed after invokation of handler */

  /* get current signal handling features */
  ret = sigaction(SIGVTALRM, NULL, &action);
  if (ret != 0) {
    perror("bx_iodev_init()");
    exit(0);
    }

  /* set up signal handler function */
  action.sa_handler = bx_sigvtalrm_handler;

  /* automatic system call restart when interrupted by signal */
#if defined SA_RESTART
  action.sa_flags |= SA_RESTART;
#else
  siginterrupt(SIGVTALRM, 0);
#endif

  /* reinstate handler function upon exit from handler */
#if defined SA_ONESHOT
  action.sa_flags &=  ~SA_ONESHOT;   /* linux */
#elif defined SA_RESETHAND
  action.sa_flags &= ~SA_RESETHAND; /* Solaris2.? */
#else
  /* ---                                    --- */
  /* --- insert code for your platform here --- */
  /* ---                                    --- */
#endif
/* ifdef SA_ONESHOT */

  ret = sigaction(SIGVTALRM, &action, NULL);
  if (ret != 0) {
    perror("bx_iodev_init()");
    exit(0);
    }

#endif /* #ifndef HAVE_SIGACTION */
}



/* bx_add_input()
 *
 * register a C function to be called when there is an event on the
 * associated file descriptor fd.
 * 
 * event_type is comprised of the bit-wise or of one or more of the
 * following flags:
 *   BX_FD_EVENT_READ:          per select(), a read event
 *   BX_FD_EVENT_WRITE:         per select(), a write event
 *   BX_FD_EVENT_EXCEPTION:     per select(), an exception event
 */

  void
bx_add_input(int fd, int event_type, void (* funct)(void))
{
  if (num_registered_fds >= BX_MAX_REGISTERED_FDS) return;

  registered_fds[num_registered_fds] = fd;
  registered_fd_handler[num_registered_fds] = funct;
  num_registered_fds++;

  if (event_type & BX_FD_EVENT_READ) {
    FD_SET(fd, &fd_read_set);
    }
  if (event_type & BX_FD_EVENT_WRITE) {
    FD_SET(fd, &fd_write_set);
    }
  if (event_type & BX_FD_EVENT_EXCEPTION) {
    FD_SET(fd, &fd_exception_set);
    }

  if (fd >= max_fd)
    max_fd = fd + 1;
}



  int
bx_register_timer( void (*funct)(void), Bit32u useconds)
{
  if (bx_num_timers >= BX_MAX_TIMERS) {
    bx_printf(1, "bx_register_timer: too many registered timers.");
    }

  bx_num_timers++;
  bx_timer[bx_num_timers - 1].period    = useconds;
  bx_timer[bx_num_timers - 1].remaining = useconds;
  bx_timer[bx_num_timers - 1].active    = 0;
  bx_timer[bx_num_timers - 1].funct     = funct;

  /* return timer id */
  return(bx_num_timers - 1);
}

  void
bx_start_timers(void)
{
  Bit32u min_timer;
  struct itimerval new_timer_value;
  int i;

  if (!bx_num_timers) return;

  min_timer = bx_timer[0].remaining;
  bx_min_timer_index = 0;
  bx_timer[0].active = 1;

  /* find minimum remaining timer value */
  for (i=1; i < bx_num_timers; i++) {
    bx_timer[i].active = 1;
    if (bx_timer[i].remaining < min_timer) {
      min_timer = bx_timer[i].remaining;
      bx_min_timer_index = i;
      }
    }

  /* system timer period = 0, not using cyclical timer */
  new_timer_value.it_interval.tv_sec  = 0;
  new_timer_value.it_interval.tv_usec = 0;

  /* system timer remaining value */
  new_timer_value.it_value.tv_usec = min_timer % 1000000;
  new_timer_value.it_value.tv_sec  = min_timer / 1000000;

  setitimer(ITIMER_VIRTUAL, &new_timer_value, NULL);
}


  void
bx_fd_poll_timer(void)
{
  bx_async_event = 1;
  bx_fd_event    = 1;
}




/* manually invoke an interrupt */

  void
bx_invoke_interrupt(int interrupt)
{
  (*bx_interrupt_table[interrupt])(interrupt);
}

  void
bx_PSEUDO_INT_Ib()
{
  Bit8u imm8;

  imm8 = bx_fetch_next_byte();
  if (bx_interrupt_table[imm8]) {
#ifdef BX_DEBUG
    bx_printf(0, "*** calling PSEUDO-INT %02x\n", (int) imm8);
#endif
    (*bx_interrupt_table[imm8])(imm8);
    }
  else {
    bx_printf(1, "*** no PSEUDO-INT handler defined for INT %02x!\n",
      imm8);
    }
}
