/*
 * Extensions by Robert Sanders, 1992-93
 * 
 * DANG_BEGIN_MODULE
 * 
 * Here is where DOSEMU gets booted. From emu.c external calls are made to
 * the specific I/O systems (video/keyboard/serial/etc...) to initialize
 * them. Memory is cleared/set up and the boot sector is read from the
 * boot drive. Many SIGNALS are set so that DOSEMU can exploit things like
 * timers, I/O signals, illegal instructions, etc... When every system
 * gives the green light, vm86() is called to switch into vm86 mode and
 * start executing i86 code.
 * 
 * The vm86() function will return to DOSEMU when certain `exceptions` occur
 * as when some interrupt instructions occur (0xcd).
 * 
 * The top level function emulate() is called from dos.c by way of a dll
 * entry point.
 * 
 * DANG_END_MODULE
 * 
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 *
 * DANG_END_CHANGELOG
 */

/*
 * DANG_BEGIN_REMARK DOSEMU must not work within the 1 meg DOS limit, so
 * start of code is loaded at a higher address, at some time this could
 * conflict with other shared libs. If DOSEMU is compiled statically
 * (without shared libs), and org instruction is used to provide the jump
 * above 1 meg. DANG_END_REMARK
 */

#ifndef __elf__
/*
 * DANG_BEGIN_FUNCTION jmp_emulate
 * 
 * description: This function allows the startup program `dos` to know how to
 * call the emulate function by way of the dll headers. Always make sure
 * that this line is the first of emu.c and link emu.o as the first object
 * file to the lib
 * 
 * DANG_END_FUNCTION
 */
__asm__("___START___: jmp _emulate\n");
#endif

#include "dosemu.h"
#include "base/phase.h"
#include "base/timers.h"
#include "base/keyb.h"
#include "base/int.h"
#include "base/serial.h"
#include "base/pic.h"
#include "base/signals.h"
#include "base/misc.h"
#include "base/bios.h"
#include "arch/signals.h"
#include "env/vc.h"

/*
 * DANG_BEGIN_FUNCTION emulate
 * 
 * arguments: argc - Argument count. argv - Arguments.
 * 
 * description: Emulate gets called from dos.c. It initializes DOSEMU to
 * prepare it for running in vm86 mode. This involves catching signals,
 * preparing memory, calling all the initialization functions for the I/O
 * subsystems (video/serial/etc...), getting the boot sector instructions
 * and calling vm86().
 * 
 * DANG_END_FUNCTION
 */
#ifdef __ELF__
void main(int argc, char **argv)
#else
void emulate(int argc, char **argv)
#endif
{
  phase0_init(argc, argv);  /* set up the environment               */
  phase1_init();            /* set up the virtual PC 'box'          */
  phase2_init();            /* set up any DOS-specific features     */

  phase1_reset();           /* do reboot code for virtual 'box'     */
  phase2_reset();           /* do reboot code for DOS features      */

  if (config.exitearly) {
    dbug_printf("Leaving DOS before booting\n");
    leavedos(0);
  }

  timer_block(); /* a sad hack... */

  if (not_use_sigio)
    k_printf("KEYB: NON-SIGIO file handles in use.\n");
  else
    k_printf("KEYB: No NON-SIGIO file handles in use.\n");
  g_printf("EMULATE\n");

  show_regs(__FILE__, __LINE__);

  fflush(stdout);
  
  while (!fatalerr) {
    ++pic_vm86_count;
    run_vm86();
    serial_run();
    run_irqs();
  }
  
  error("error exit: (%d,0x%04x) in_sigsegv: %d ignore_segv: %d\n",
	fatalerr, fatalerr, in_sigsegv, ignore_segv);
  
  sync();
  fprintf(stderr, "Not a good day to die!!!!!\n");
  leavedos(99);
}

void dos_ctrl_alt_del(void)
{
  dbug_printf("DOS: Rebooting!\n");
  clear_screen(READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), 7);
  p_dos_str("Rebooting DOS.  Be careful, this is partially implemented\r\n");

  timer_block(); /* a sad hack... */

  phase1_reset();
  phase2_reset();
}

void dos_ctrlc(void)
{
  k_printf("DOS ctrl-c!\n");
  p_dos_str("^C\n\r");	/* print ctrl-c message */
  keybuf_clear();  
  int_run(0x23);
}

static void ign_sigs(int sig)
{
  static int timerints = 0;
  static int otherints = 0;

  g_printf("ERROR: signal %d received in leavedos()\n", sig);
  show_regs(__FILE__, __LINE__);
  if (sig == SIG_TIME)
    timerints++;
  else
    otherints++;
  
#define LEAVEDOS_TIMEOUT (3 * 18)
#define LEAVEDOS_SIGOUT  5
  if ((timerints >= LEAVEDOS_TIMEOUT) || (otherints >= LEAVEDOS_SIGOUT)) {
    error("ERROR: timed/signalled out in leavedos()\n");
    exit(1);
  }
}

/* "graceful" shutdown */
void leavedos(int sig)
{
  static int recurse_check = 0;

  if (recurse_check) return;
  recurse_check = 1;

#ifndef CONFIG_RUN_AS_ROOT
  if (i_am_root)
    priv_off();
#endif

  if (in_vm86) {
    g_printf("\nkilled while in vm86(), trying to dump DOS-registers:\n");
    show_regs(__FILE__, __LINE__);
  }
  in_vm86 = 0;

  setitimer(ITIMER_REAL, NULL, NULL);
  SETSIG(SIG_TIME, ign_sigs);
  SETSIG(SIGSEGV, ign_sigs);
  SETSIG(SIGILL, ign_sigs);
  SETSIG(SIGFPE, ign_sigs);
  SETSIG(SIGTRAP, ign_sigs);
  SETSIG(SIGHUP, ign_sigs);
  SETSIG(SIGTERM, ign_sigs);
  warn("leavedos(%d) called - shutting down\n", sig);

  phase2_term();
  phase1_term();
  phase0_term();

  _exit(sig);
}

