/*
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 <time.h>
#include "iodev.h"

static Bit8u  cmos_mem_address = 0;


static void bx_bios_int1a_handler(int vector);
static void set_ticks(void);


  Bit8u
bx_cmos_ram_io_read_handler(Bit32u address)
{
  /* CMOS RAM */
  switch (address) {
    case 0x0071:
      switch (cmos_mem_address) {
        case 0x0a: return(0x26);
        case 0x0b: return(0x02);
        case 0x0f: return(0x04);
        default: bx_printf(1, "unsupported cmos io function!\n");
        }
      break;
    }
  return(0);
}

  void
bx_cmos_ram_io_write_handler(Bit32u address, Bit8u value)
{
  /* CMOS RAM */
  switch (address) {
    case 0x0070:
      cmos_mem_address = value & 0x3F;
      break;
    case 0x0071:
      switch (cmos_mem_address) {
        case 0x0a:
          if (value != 0x26) bx_printf(1, "unsupported cmos io write!\n");
          break;
        case 0x0b:
          if (value != 0x02) bx_printf(1, "unsupported cmos io write!\n");
          break;
	case 0x0f:
          if (value != 0x04) bx_printf(1, "unsupported cmos io write!\n");
          break;
        default:
          bx_printf(1, "unsupported cmos io function!\n");
        }
      break;
    }
}

  void
bx_cmos_rtc_int_handler(int vector)
{
}


  void
bx_init_cmos(void)
{
  time_t time_seconds;
  struct tm *time_calendar;
  Bit32u days_since_1980;

  typical_int_code[2] = 0x1a;
  bx_register_int_vector(0x1a, typical_int_code, sizeof(typical_int_code),
      bx_bios_int1a_handler);

  typical_int_code[2] = 0x70;
  bx_register_int_vector(0x70, typical_int_code, sizeof(typical_int_code),
      bx_cmos_rtc_int_handler);

  time_seconds = time(NULL);
  time_calendar = localtime(&time_seconds);
  days_since_1980 = (time_calendar->tm_year - 80) * 365 +
    time_calendar->tm_yday;
  bx_access_physical(0x4ce, 4, BX_WRITE, &days_since_1980);

  set_ticks();
}


  static void
bx_bios_int1a_handler(int vector)
{
  time_t time_seconds;
  struct tm *time_calendar;
  unsigned century, year, month, day;
  Bit16u dx, cx;
  Bit8u  al;

  /* time of day functions */

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

  switch (AH) {
    case 0x00: /* get current clock count */
      /* set_ticks(); */
      bx_access_physical(0x046c, 2, BX_READ, &dx);
      bx_access_physical(0x046e, 2, BX_READ, &cx);
      bx_access_physical(0x0470, 1, BX_READ, &al);
      CX = cx;
      DX = dx;
      AL = al;
      bx_cpu.eflags.cf = 0;
      break;

    case 0x01: /* set current clock count */
bx_printf(1, "unsupported int1ah function (01h)\n");
      break;

    case 0x02: /* read CMOS time */
      time_seconds = time(NULL);
      time_calendar = localtime(&time_seconds);

      bx_cpu.eflags.cf = 0;
      CH = ((time_calendar->tm_hour / 10) << 4)
          | (time_calendar->tm_hour % 10);
      CL = ((time_calendar->tm_min  / 10) << 4)
          | (time_calendar->tm_min % 10);
      DH = ((time_calendar->tm_sec  / 10) << 4)
          | (time_calendar->tm_sec % 10);
      DL = (time_calendar->tm_isdst) > 0;
      break;

    case 0x03: /* set CMOS time */
      break;

    case 0x04: /* read CMOS date */
      time_seconds = time(NULL);
      time_calendar = localtime(&time_seconds);

      century = 19 + (time_calendar->tm_year / 100);
      year    = time_calendar->tm_year % 100;
      month   = time_calendar->tm_mon + 1;
      day     = time_calendar->tm_mday;

      bx_cpu.eflags.cf = 0;
      CH = ((century / 10) << 4) | (century % 10);
      CL = ((year  / 10) << 4) | (year % 10);
      DH = ((month / 10) << 4) | (month % 10);
      DL = ((day   / 10) << 4) | (day % 10);
      break;

    case 0x05: /* set CMOS date */
      break;

    default:
      bx_printf(1, "*** int 1A function %d not yet supported!\n",
        (int) AH);
    }
}

  static void
set_ticks(void)
{
  time_t time_seconds;
  struct tm *time_calendar;
  Bit32u ticks;
  Bit8u  rollover;

  /* return the number of ticks since midnight */

  time_seconds = time(NULL);
  time_calendar = localtime(&time_seconds);

  ticks = (time_calendar->tm_hour * 3600 + time_calendar->tm_min * 60 +
    time_calendar->tm_sec) * 18.2;
  rollover = 0;
  bx_access_physical(0x046c, 4, BX_WRITE, &ticks);
  bx_access_physical(0x0470, 1, BX_WRITE, &rollover);
}

