/* hercules.c - hercules console interface */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2001,2002  Free Software Foundation, Inc.
 *
 *  This program 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 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef SUPPORT_HERCULES

#include <shared.h>
#include <hercules.h>
#include <term.h>

/* The position of the cursor.  */
static int herc_x;
static int herc_y;

static int herc_standard_color = A_NORMAL;
static int herc_normal_color = A_NORMAL;
static int herc_highlight_color = A_REVERSE;
static int herc_helptext_color = A_NORMAL;
static int herc_heading_color = A_NORMAL;
static int herc_current_color = A_NORMAL;
static color_state herc_color_state = COLOR_STATE_STANDARD;
static int herc_cursor_state = 1;

/* Write a byte to a port.  */
static inline void
outb (unsigned short port, unsigned char value)
{
  asm volatile ("outb	%b0, %w1" : : "a" (value), "Nd" (port));
}

static void
herc_set_cursor (void)
{
  unsigned offset = herc_y * HERCULES_WIDTH + herc_x;
  
  outb (HERCULES_INDEX_REG, 0x0f);
  outb (0x80, 0);
  outb (HERCULES_DATA_REG, offset & 0xFF);
  outb (0x80, 0);
  
  outb (HERCULES_INDEX_REG, 0x0e);
  outb (0x80, 0);
  outb (HERCULES_DATA_REG, offset >> 8);
  outb (0x80, 0);
}

void
hercules_putchar (int c)
{
  switch (c)
    {
    case '\b':
      if (herc_x > 0)
	herc_x--;
      break;
      
    case '\n':
      herc_y++;
      break;
      
    case '\r':
      herc_x = 0;
      break;

    case '\a':
      break;

    default:
      {
	volatile unsigned short *video
	  = (unsigned short *) HERCULES_VIDEO_ADDR;
	
	video[herc_y * HERCULES_WIDTH + herc_x]
	  = (herc_current_color << 8) | c;
	herc_x++;
	if (herc_x >= HERCULES_WIDTH)
	  {
	    herc_x = 0;
	    herc_y++;
	  }
      }
      break;
    }

  if (herc_y >= HERCULES_HEIGHT)
    {
      volatile unsigned long *video = (unsigned long *) HERCULES_VIDEO_ADDR;
      int i;
      
      herc_y = HERCULES_HEIGHT - 1;
      grub_memmove ((char *) HERCULES_VIDEO_ADDR,
		    (char *) HERCULES_VIDEO_ADDR + HERCULES_WIDTH * 2,
		    HERCULES_WIDTH * (HERCULES_HEIGHT - 1) * 2);
      for (i = HERCULES_WIDTH * (HERCULES_HEIGHT - 1) / 2;
	   i < HERCULES_WIDTH * HERCULES_HEIGHT / 2;
	   i++)
	video[i] = 0x07200720;
    }
}

void
hercules_cls (void)
{
  int i;
  volatile unsigned long *video = (unsigned long *) HERCULES_VIDEO_ADDR;
  
  for (i = 0; i < HERCULES_WIDTH * HERCULES_HEIGHT / 2; i++)
    video[i] = 0x07200720;

  herc_x = herc_y = 0;
  herc_set_cursor ();
}

int
hercules_getxy (void)
{
  return (herc_x << 8) | herc_y;
}

void
hercules_gotoxy (int x, int y)
{
  herc_x = x;
  herc_y = y;
  herc_set_cursor ();
}

void
hercules_setcolorstate (color_state state)
{
  switch (state) {
    case COLOR_STATE_STANDARD:
      herc_current_color = herc_standard_color;
      break;
    case COLOR_STATE_NORMAL:
      herc_current_color = herc_normal_color;
      break;
    case COLOR_STATE_HIGHLIGHT:
      herc_current_color = herc_highlight_color;
      break;
    case COLOR_STATE_HELPTEXT:
      herc_current_color = herc_helptext_color;
      break;
    case COLOR_STATE_HEADING:
      herc_current_color = herc_heading_color;
      break;
    default:
      herc_current_color = herc_standard_color;
      break;
  }

  herc_color_state = state;
}

void
hercules_setcolor (int normal_color, int highlight_color, int helptext_color, int heading_color)
{
  herc_normal_color = normal_color;
  herc_highlight_color = highlight_color;
  herc_helptext_color = helptext_color;
  herc_heading_color = heading_color;
  
  hercules_setcolorstate (herc_color_state);
}

int
hercules_setcursor (int on)
{
  int old_state = herc_cursor_state;
  
  outb (HERCULES_INDEX_REG, 0x0a);
  outb (0x80, 0);
  outb (HERCULES_DATA_REG, on ? 0 : (1 << 5));
  outb (0x80, 0);
  herc_cursor_state = on;

  return old_state;
}

#endif /* SUPPORT_HERCULES */
