/*
Copywrite 1994 by Kevin P. Lawton 

This file is part of the IODEV (Input Output DEVices) component of BOCHS.

The IODEV component is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

The IODEV component 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with the IODEV component; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include "iodev.h"

static void bx_bios_default_int_handler(int vector);
static void bx_bios_bootstrap_loader(int vector);
static void bx_bios_int11_handler(int vector);
static void bx_bios_int12_handler(int vector);
static void bx_bios_int14_handler(int vector);
static void bx_bios_int15_handler(int vector);
static void bx_bios_int17_handler(int vector);

static Bit8u bios_config_table[10];
static Bit8u bios_date[] = "09/27/94";




  void
bx_bios_post(void)
{
  Bit32u phy_addr;
  Bit16u memory_in_k, equip_word;
  Bit8u  zero8, manufact_test;
  int    interrupt;
  int    i;
  Bit8u  sys_model_id, sys_submodel_id;
  Bit16u warm_boot_flag;

  zero8 = 0;

  for (interrupt=0; interrupt < 256; interrupt++) {
    typical_int_code[2] = interrupt;
    bx_register_int_vector((Bit8u) interrupt,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_default_int_handler);
    }


  /* zero out BIOS data area (0x400 .. 0x500 inclusive) */
  for (phy_addr = 0x400; phy_addr <= 0x500 ; phy_addr++) {
    bx_access_physical(phy_addr, 1, BX_WRITE, &zero8);
    }

  bx_access_physical(0x410, 2, BX_READ, &equip_word);
  equip_word |= 0x01; /* boot diskette drive installed */
  bx_access_physical(0x410, 2, BX_WRITE, &equip_word);

  /* set the manufacturing test BIOS data item to normal operation */
  manufact_test = 0; /* normal operation */
  bx_access_physical(0x412, 1, BX_WRITE, &manufact_test);

  /* set the main memory size BIOS data item (in Kbytes) */
  memory_in_k = 640; /* report only base memory, not extended mem */
  bx_access_physical(0x413, 2, BX_WRITE, &memory_in_k);

  warm_boot_flag = 0x1234;
  bx_access_physical(0x472, 2, BX_WRITE, &warm_boot_flag);

  /* INT 12h = Memory Size */
  typical_int_code[2] = 0x12;
  bx_register_int_vector(0x12,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_int12_handler);

  /* INT 11h = Equipment Configuration */
  typical_int_code[2] = 0x11;
  bx_register_int_vector(0x11,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_int11_handler);

  typical_int_code[2] = 0x14;
  bx_register_int_vector(0x14,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_int14_handler);

  typical_int_code[2] = 0x15;
  bx_register_int_vector(0x15,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_int15_handler);

  typical_int_code[2] = 0x17;
  bx_register_int_vector(0x17,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_int17_handler);

  sys_model_id    = 0xFB; /* PC XT */
  sys_submodel_id = 0x00; /* PC XT */

  bios_config_table[0] = 0x08; /* 8 bytes following this word */
  bios_config_table[1] = 0x00;
  bios_config_table[2] = sys_model_id;
  bios_config_table[3] = sys_submodel_id;
  bios_config_table[4] = 0x00; /* BIOS revision number */
  bios_config_table[5] = (0 << 7) | /* DMA channel 3 not used by hard disk */
                         (1 << 6) | /* 1=2 interrupt controllers present */
                         (0 << 5) | /* 1=RTC present */
                         (0 << 4) | /* 1=BIOS calls int 15h, 4Fh every key */
                         (0 << 3) | /* 1=wait for extern event supported */
                         (0 << 2) | /* no extended BIOS data area used */
                         (0 << 1);  /* AT bus */
  bios_config_table[6] = 0x40; /* int16h funct 9 supported */
  bios_config_table[7] = 0x00;
  bios_config_table[8] = 0x00;
  bios_config_table[9] = 0x00;

  for (i=0; i<10; i++) {
    bx_access_physical(BX_BIOS_CONFIG_TABLE + i, 1, BX_WRITE,
      &bios_config_table[i]);
    }

  bx_access_physical(0xFFFFE, 1, BX_WRITE, &sys_model_id);

  for (i=0; i<strlen(bios_date); i++) {
    bx_access_physical(0xFFFF5, 1, BX_WRITE, &bios_date[i]);
    }

  /*--- MDA adapter ---*/
  bx_init_mda();

  /*--- 8259A PIC ---*/
  bx_init_pic();

  /*--- 8254 PIT ---*/
  bx_init_pit();

  /*--- 8237 DMA controller ---*/
  bx_init_dma();

  /*--- 8042 keyboard controller ---*/
  bx_init_keyboard();

  /*--- CMOS RAM & RTC ---*/
  bx_init_cmos();

  /*--- HARD DRIVE ---*/
  bx_init_hard_drive();

  /*--- PARALLEL PORT ---*/
  bx_init_parallel();

  /*--- SERIAL PORT ---*/
  bx_init_serial();

  /*--- FLOPPY DRIVE CONTROLLER ---*/
  bx_init_floppy();

  /* should insert int handlers for various CPU & BIOS services here */


  /* bootstrap */
  typical_int_code[2] = 0x19;
  bx_register_int_vector((Bit8u) 0x19,
      typical_int_code, sizeof(typical_int_code),
      bx_bios_bootstrap_loader);
}

  static void
bx_bios_default_int_handler(int vector)
{
  bx_printf(0, "*** default int handler called for int %d\n", vector);
  bx_printf(0, "    ah = %x, al = %x\n", (int) AH, (int) AL);
  bx_printf(1, "Please implement this function!!!\n");
}

  static void
bx_bios_bootstrap_loader(int vector)
{
  /* The first 512 bytes from 1st logical sector of floppy are loaded
     starting at location 0:7C00 and control is passed to 0:7C00.
     DL holds the drive no where the sector was last read
   */

  bx_printf(0, "BIOS bootstrap loader invoked.\n");

  /* use Int 13h, funct 2 to read 1st logical sector */

  /* point es:bx to 0:7C00 */
  bx_load_seg_reg(&bx_cpu.es, 0x0000);
  BX = 0x7C00;

  AH = 2; /* function 2 */
  AL = 1; /* read 1 sector */
  CH = 0; /* track 0 */
  CL = 1; /* sector 1 */
  DH = 0; /* head 0 */
  DL = 0; /* drive 0 */

  bx_invoke_interrupt(0x13);

  if ( (AL != 1) || (bx_cpu.eflags.cf != 0) ) {
    bx_printf(1, "BIOS boostrap: INT 13h failed to load 1st sector.\n");
    }

  bx_load_seg_reg(&bx_cpu.cs, 0x0000);
  bx_cpu.eip = 0x7C00;
  DL = 0; /* make sure DL points to boot drive */
}

  static void
bx_bios_int12_handler(int vector)
{
  Bit16u memory_in_k;

  bx_access_physical(0x413, 2, BX_READ, &memory_in_k);

  AX = memory_in_k;

#ifdef BX_DEBUG
  bx_printf(0, "*** INT 12h called, returning memory size of %dK\n",
    (int) AX);
#endif
}

  static
void bx_bios_int11_handler(int vector)
{
  Bit16u equip_word;

  bx_access_physical(0x410, 2, BX_READ, &equip_word);

  AX = equip_word;

#ifdef BX_DEBUG
  bx_printf(0, "*** INT 11h called, returning equip_word of %04x\n",
    (int) AX);
#endif
}

  static
void bx_bios_int15_handler(int vector)
{
  Bit16u es, bx;

#ifdef BX_DEBUG
  bx_printf(0, "*** INT 15h called with AH=%02x, AL=%02x\n",
    (int) AH, (int) AL);
#endif

  switch (AH) {
    case 0x41:
      bx_cpu.eflags.cf = 1;
      AH = 0x80;
      break;

    case 0x88: /* extended memory size */
      bx_cpu.eflags.cf = 0;
      AX = 0; /* for now, no extended memory */
      break;

    case 0xC0:
      bx_cpu.eflags.cf = 0;
      AH = 0;
      es = ((BX_BIOS_CONFIG_TABLE & 0xF0000) >> 4);
      bx =  (BX_BIOS_CONFIG_TABLE & 0x0FFFF);
      bx_load_seg_reg(&bx_cpu.es, es);
      BX = bx;
      break;

    default:
      bx_printf(1, "*** function not yet supported!\n");
    }
}

  static
void bx_bios_int14_handler(int vector)
{
  /* serial communications */

#ifdef BX_DEBUG
  bx_printf(0, "*** INT 14h called with AH=%02x, AL=%02x, DX=%04x\n",
    (int) AH, (int) AL, (int) DX);
#endif

  switch (AH) {
    case 0x00:
      if (DX > 3) {
        bx_printf(1, "INT 14h, funct 0 called with bogus port #\n");
        }
      AH = 0; /* bogus values */
      AL = 0;
      break;

    default:
      bx_printf(1, "*** function not yet supported!\n");
    }
}


  static void
 bx_bios_int17_handler(int vector)
{
#ifdef BX_DEBUG
  bx_printf(0, "*** INT 17h called with AH=%02x, DX=%04x\n",
    (int) AH, (int) DX);
#endif

  switch (AH) {
    case 0x01: /* intialize printer */
      if (DX > 3) {
        bx_printf(1, "INT 17h funct 1, called with bogus port #\n");
        }
      AH = 0; /* bogus value */
      break;

    default:
      bx_printf(1, "*** function not yet supported!\n");
    }
}

