/*
 * DANG_BEGIN_MODULE
 *
 * Description:
 *   This module contains the BIOS default interrupt handlers for DOSEMU.
 *   Most of these functions will never be invoked (at least in the i386
 *   versions) because they will be emulated in the DOSEMU BIOS itself.
 *
 * DANG_END_MODULE
 *
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 *
 * DANG_END_CHANGELOG
 */

#include "dosemu.h"
#include "base/int.h"
#include "base/bios.h"
#include "base/port.h"
#include "base/memory.h"
#include "base/timers.h"
#include "base/mouse.h"
#include "base/bios_entry.h"
#include "base/disks.h"
#include "dosext/xms.h"

#include <fcntl.h>
#include <ctype.h>
#include <sys/wait.h>
#include <sys/time.h>

static void int08(Bit8u i)
{
  Bit32u ticks;
  g_printf("BIOS: int08 timer tick\n");

  ticks = READ_DWORD(bios_timer);
  if (++ticks >= 0x001b001b) {
    ticks -= 0x001b001b;
    WRITE_BYTE(bios_timer_overflow, READ_BYTE(bios_timer_overflow) + 1);
  }
  WRITE_DWORD(bios_timer, ticks);
  __cli__;
  int_run(0x1c);
  port_outb(0x20, 0x20);
  WRITE_FLAGSE(READ_FLAGSE() | VIF);
}

static void int09(Bit8u i)
{
  g_printf("BIOS: int09 keyboard\n");
  /* what to do here??? */
  port_outb(0x20, 0x20);
}

static void int_irq_lo(Bit8u i)
{
  g_printf("BIOS: irq %d reached dosemu...\n", i-0x08);
  port_outb(0x20, 0x20);
}

static void int11(Bit8u i)
{
  _AX = READ_WORD(BIOS_CONFIGURATION);
}

static void int12(Bit8u i)
{
  _AX = READ_WORD(BIOS_MEMORY_SIZE);
}

static void int15(Bit8u i)
{
  static struct timeval scr_tv;
  struct timeval wait_time;

  g_printf("INT15: entered w/AX=0x%04x\n", _AX);

  if (_AH != 0x4f)
    __clc__;
  WRITE_FLAGSE(READ_FLAGSE() | VIF);

  switch (_AH) {
  case 0x10:			/* TopView/DESQview */
    if (_AL == 0) {             /* give up timeslice */
      usleep(INT15_IDLE_USECS);
    }
    __stc__;
    break;

  case 0x41:			/* wait on external event */
    __clc__;
    break;

  case 0x4f:			/* Keyboard intercept */
    _AH = 0x86;
    k_printf("INT15 0x4f CARRY=%lx AX=%x\n", (READ_FLAGS() & CF), _AX);
/*
    __stc__;
    if (_AL & 0x80 )
      if (1 || !(_AL&0x80) ){
	fprintf(stderr, "Carrying it out\n");
        __stc__;
      }
      else
	__clc__;
*/
    break;

  case 0x80:			/* default BIOS device open event */
    __stc__;
    _AH = 0;
    break;

  case 0x81:
    __stc__;
    _AH = 0;
    break;

  case 0x82:
    __stc__;
    _AH = 0;
    break;

  case 0x83:
    h_printf("int 15h event wait:\n");
    show_regs(__FILE__, __LINE__);
    __stc__;
    break;

  case 0x84:
    __stc__;
    break;

  case 0x85:		/* default bios handler for sysreq key */
    if (_AL == 0 || _AL == 1) {
      _AH = 0;
      break;
    }
    _AL = 1;
    __stc__;
    break;

  case 0x86:		/* wait...cx:dx=time in usecs */
    g_printf("doing int15 wait...ah=0x86\n");
    show_regs(__FILE__, __LINE__);
    wait_time.tv_sec = 0;
    wait_time.tv_usec = (LWORD(ecx) << 16) | LWORD(edx);
    RPT_SYSCALL(select(STDIN_FILENO, NULL, NULL, NULL, &scr_tv));
    __clc__;
    break;

  case 0x87:
    if (config.xms_size)
      xms_int15();
    else {
      _AH = 0x03;
      __stc__;
    }
    break;

  case 0x88:
    if (config.xms_size) {
      xms_int15();
    }
    else {
      _AX = 0;                  /* no extended ram if it's not XMS */
      __clc__;
    }
    break;

  case 0x89:			/* enter protected mode : kind of tricky! */
    _AL = 0;
    __stc__;
    break;

  case 0x90:			/* no device post/wait stuff */
    __stc__;
    break;

  case 0x91:
    __stc__;
    break;

  case 0xc0:
    _ES = ROM_CONFIG_SEG;
    _BX = ROM_CONFIG_OFF;
    _AL = 0;
    __clc__;
    break;

  case 0xc1:
    __stc__;
    break;

  case 0xc2:
    m_printf("PS2MOUSE: Call ax=0x%04x\n", LWORD(eax));
    if (!mice->intdrv)
      if (mice->type != MOUSE_PS2) {
	REG(eax) = 0500;        /* No ps2 mouse device handler */
	__stc__;
	return;
      }
                
    switch (REG(eax) &= 0x00FF)
      {
      case 0x0000:                    
	mouse.ps2.state = _BH;
	if (mouse.ps2.state == 0)
	  mice->intdrv = FALSE;
	else
	  mice->intdrv = TRUE;
 	_AL = 0;
	__clc__;		
 	break;
      case 0x0001:
	_AL = 0;
	_BX = 0xAAAA;    /* we have a ps2 mouse */
	__clc__;
	break;
      case 0x0003:
	if (_BH != 0) {
	  __stc__;
	  _AH = 1;
	} else {
	  __clc__;
	  _AH = 0;
	}
	break;
      case 0x0004:
	HI(bx) = 0xAA;
	_AH = 0;
	__clc__;
	break;
      case 0x0005:			/* Initialize ps2 mouse */
	_AH = 0;
	mouse.ps2.pkg = HI(bx);
	__clc__;
	break;
      case 0x0006:
	switch (HI(bx)) {
	case 0x00:
	  LO(bx)  = (mouse.rbutton ? 1 : 0);
	  LO(bx) |= (mouse.lbutton ? 4 : 0);
	  LO(bx) |= 0; 	/* scaling 1:1 */
	  LO(bx) |= 0x20;	/* device enabled */
	  LO(bx) |= 0;	/* stream mode */
	  LO(cx) = 0;		/* resolution, one count */
	  LO(dx) = 0;		/* sample rate */
	  _AH = 0;
	  __clc__;
	  break;
	case 0x01:
	  _AH = 0;
	  __clc__;
	  break;
	case 0x02:
	  _AH = 1;
	  __stc__;
	  break;
	}
	break;
#if 0
      case 0x0007:  /* ??? -- Scott */
	_pushw(0x000B);
	_pushw(0x0001);
	_pushw(0x0001);
	_pushw(0x0000);
	_CS = _ES;
	_EIP = _EBX;
	_AH = 0;
	__clc__;
	break;
#endif
     default:
	_AH = 1;
	g_printf("PS2MOUSE: Unknown call ax=0x%04x\n", LWORD(eax));
	__stc__;
      }
    return;

  case 0xc3:
    /* no watchdog */
    __stc__;
    break;

  case 0xc4:
    /* no post */
    __stc__;
    break;

  default:
    g_printf("int 15h error: ax=0x%04x\n", LWORD(eax));
    __stc__;
    _AH = 0x86;
    break;
  }
}

static void int16(Bit8u i)
{
  int_run_vm86(i);
}

/* int17 -- printer, handled elsewhere */

static void int18(Bit8u i)
{
  g_printf("BASIC interrupt being attempted.\n");
}

static void int19(Bit8u i)
{
  char           *buffer;
  struct disk    *dp = NULL;

  g_printf("BIOS: int19 invoked\n");

  switch (config.hdiskboot) {
  case 0:
    if (config.bootdisk)
      dp = &bootdisk;
    else
      dp = &disktab[0];
    break;
  case 1:
    dp = &hdisktab[0];
    break;
  case 2:
    dp = &disktab[1];
    break;
  default:
    error("ERROR: unexpected value for config.hdiskboot\n");
    leavedos(15);
  }
  
  disk_close();
  disk_open(dp);

  buffer = (char *) 0x7c00;
  
  if (dp->boot_name) {/* Boot from the specified file */
    int bfd;
    d_printf ("Booting from bootfile=%s...\n",dp->boot_name);
    bfd = open (dp->boot_name, O_RDONLY);
    if (bfd == -1) {/* Abort with error */
      error("ERROR: Boot file %s missing\n",dp->boot_name);
      leavedos(16);
    }
    if (read(bfd, buffer, SECTOR_SIZE) != SECTOR_SIZE) {
      error("ERROR: Failed to read exactly %d bytes from %s\n",
	    SECTOR_SIZE, dp->boot_name);
      leavedos(16);
    }
    close(bfd);
  }
  else
    if (dp->type == PARTITION) {/* we boot partition boot record, not MBR! */
      d_printf("Booting partition boot record from part=%s....\n", dp->dev_name);

      if (RPT_SYSCALL(read(dp->fdesc, buffer, SECTOR_SIZE)) != SECTOR_SIZE) {
	error("ERROR: reading partition boot sector using partition %s.\n", dp->dev_name);
	leavedos(16);
      }
    }
    else if (read_sectors(dp, buffer, 0, 0, 0, 1) != SECTOR_SIZE) {
      error("ERROR: can't boot from %s, using harddisk\n", dp->dev_name);
      dp = hdisktab;
      if (read_sectors(dp, buffer, 0, 0, 0, 1) != SECTOR_SIZE) {
	error("ERROR: can't boot from hard disk\n");
	leavedos(16);
      }
    }
  disk_close();
}

static void int1a(Bit8u i)
{
  unsigned int test_date;
  time_t akt_time; 
  time_t time_val;
  struct tm *tm;

  g_printf("BIOS: int1a fcn 0x%02x invoked\n", _AH);

  switch (_AH) {

    /* A timer read should reset the overflow flag */
  case 0:			/* read time counter */
    time(&akt_time);
    tm = localtime((time_t *) &akt_time);
    test_date = tm->tm_year * 10000 + tm->tm_mon * 100 + tm->tm_mday;
    if ( check_date != test_date ) {
      start_time = akt_time;
      check_date = tm->tm_year * 10000 + tm->tm_mon * 100 + tm->tm_mday;
      g_printf("Over 24hrs forward or backward\n");
      WRITE_BYTE(BIOS_24_HOURS_FLAG, READ_BYTE(BIOS_24_HOURS_FLAG)+1);
    }
    last_ticks = (tm->tm_hour * 60 * 60 + tm->tm_min * 60 + tm->tm_sec)*18.206;
    set_ticks(last_ticks);

    _AL = READ_BYTE(BIOS_24_HOURS_FLAG);
    _CX = (READ_DWORD(BIOS_TIMER) >> 16) & 0xffff;
    _DX = READ_DWORD(BIOS_TIMER) & 0xffff;
#if 0
    g_printf("read timer st:%u ticks:%u act:%u, actdate:%d\n",
	     start_time, last_ticks, akt_time, tm->tm_mday);
#endif
    break;
  case 1:			/* write time counter */
    last_ticks = ((Bit32u)_CX << 16) | (Bit32u)_DX;
    set_ticks(last_ticks);
    time(&start_time);
    g_printf("set timer to %lu \n", last_ticks);
    break;
  case 2:			/* get time */
    time(&time_val);
    tm = localtime((time_t *) &time_val);
    g_printf("get time %d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec);
    _CH = tm->tm_hour % 10;
    tm->tm_hour /= 10;
    _CH |= tm->tm_hour << 4;
    _CL = tm->tm_min % 10;
    tm->tm_min /= 10;
    _CL |= tm->tm_min << 4;
    _DH = tm->tm_sec % 10;
    tm->tm_sec /= 10;
    _DH |= tm->tm_sec << 4;
    /* _DL = tm->tm_isdst; */
    __clc__;
    break;
  case 4:			/* get date */
    time(&time_val);
    tm = localtime((time_t *) &time_val);
    tm->tm_year += 1900;
    tm->tm_mon++;
    g_printf("get date %02d.%02d.%04d\n", tm->tm_mday, tm->tm_mon, tm->tm_year);
#if 0
    gettimeofday(&tp, &tzp);
    ticks = tp.tv_sec - (tzp.tz_minuteswest * 60);
#endif
    _CX = tm->tm_year % 10;
    tm->tm_year /= 10;
    _CX |= (tm->tm_year % 10) << 4;
    tm->tm_year /= 10;
    _CH = tm->tm_year % 10;
    tm->tm_year /= 10;
    _CL |= (tm->tm_year) << 4;
    _DL = tm->tm_mday % 10;
    tm->tm_mday /= 10;
    _DL |= tm->tm_mday << 4;
    _DH = tm->tm_mon % 10;
    tm->tm_mon /= 10;
    _DH |= tm->tm_mon << 4;
    __clc__;
    break;
  case 3:			/* set time */
  case 5:			/* set date */
    g_printf("ERROR: timer: can't set time/date\n");
    break;
  default:
    g_printf("ERROR: timer error AX=0x%04x\n", LWORD(eax));
    break;
  }
}

static void int_irq_8(Bit8u i)
{
  g_printf("BIOS: int75 -- irq8 (rtc) unhandled by DOSEMU\n");
  port_outb(0x80, 0x20);
  port_outb(0x20, 0x20);
}

static void int_irq_9(Bit8u i)
{
  g_printf("BIOS: irq 9, redirect to irq 2\n");
  port_outb(0x80, 0x20);
  int_run(0x0a);
}

static void int_irq_hi(Bit8u i)
{
  g_printf("BIOS: irq %d unhandled by DOSEMU\n", i-0x70);
  port_outb(0x80, 0x20);
  port_outb(0x20, 0x20);
}

static void int_irq_d(Bit8u i)
{
  g_printf("BIOS: int75 -- irq13 unhandled by DOSEMU\n");
  port_outb(0x80, 0x20);
  port_outb(0x20, 0x20);
  /* Norton's SYSINFO says, "Redirect to NMI interrupt" ??? */
}

static void int_irq_f(Bit8u i)
{
  g_printf("BIOS: int77 -- irq15 unhandled by DOSEMU\n");
  port_outb(0x80, 0x20);
  port_outb(0x20, 0x20);
  /* XXX - what does this do? */
}

void bios_int_init(void)
{
}

void bios_int_reset(void)
{
  /* Most of these will never get used, btw */

  g_printf("BIOS: setting chained BIOS vectors\n");

  int_register_vector(0x08, int08, INT_CHAIN);
  int_register_vector(0x09, int09, INT_CHAIN);
  int_register_vector(0x0a, int_irq_lo, INT_CHAIN);
  int_register_vector(0x0b, int_irq_lo, INT_CHAIN);
  int_register_vector(0x0c, int_irq_lo, INT_CHAIN);
  int_register_vector(0x0d, int_irq_lo, INT_CHAIN);
  int_register_vector(0x0e, int_irq_lo, INT_CHAIN);
  int_register_vector(0x0f, int_irq_lo, INT_CHAIN);
  int_register_vector(0x10, int10, INT_CHAIN);
  int_register_vector(0x11, int11, INT_CHAIN);
  int_register_vector(0x12, int12, INT_CHAIN);
  int_register_vector(0x13, int13, INT_CHAIN);
  int_register_vector(0x14, int14, INT_CHAIN);
  int_register_vector(0x15, int15, INT_CHAIN);
  int_register_vector(0x16, int16, INT_CHAIN);
  int_register_vector(0x17, int17, INT_CHAIN);
  int_register_vector(0x18, int18, INT_CHAIN);
  int_register_vector(0x19, int19, INT_CHAIN);
  int_register_vector(0x1a, int1a, INT_CHAIN);

  int_register_vector(0x70, int_irq_8, INT_CHAIN);
  int_register_vector(0x71, int_irq_9, INT_CHAIN);
  int_register_vector(0x72, int_irq_hi, INT_CHAIN);
  int_register_vector(0x73, int_irq_hi, INT_CHAIN);
  int_register_vector(0x74, int_irq_hi, INT_CHAIN);
  int_register_vector(0x75, int_irq_d, INT_CHAIN);
  int_register_vector(0x76, int_irq_hi, INT_CHAIN);
  int_register_vector(0x77, int_irq_f, INT_CHAIN);
}
