/*
 *  ReactOS kernel
 *  Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
 *
 *  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.
 */
/* $Id: display.c,v 1.4 2002/09/08 10:22:24 chorns Exp $
 *
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS kernel
 * FILE:            ntoskrnl/hal/x86/display.c
 * PURPOSE:         Blue screen display
 * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
 * UPDATE HISTORY:
 *                  Created 08/10/99
 */

#include <ddk/ntddk.h>
#include <mps.h>

#define SCREEN_SYNCHRONIZATION


#define CRTC_COMMAND       0x3d4
#define CRTC_DATA          0x3d5

#define CRTC_COLUMNS       0x01
#define CRTC_OVERFLOW      0x07
#define CRTC_ROWS          0x12
#define CRTC_SCANLINES     0x09

#define CRTC_CURHI         0x0e
#define CRTC_CURLO         0x0f


#define CHAR_ATTRIBUTE     0x17  /* grey on blue */


/* VARIABLES ****************************************************************/

static ULONG CursorX = 0;      /* Cursor Position */
static ULONG CursorY = 0;
static ULONG SizeX = 80;       /* Display size */
static ULONG SizeY = 25;

static BOOLEAN DisplayInitialized = FALSE;
static BOOLEAN HalOwnsDisplay = TRUE;

static WORD *VideoBuffer = NULL;

static PHAL_RESET_DISPLAY_PARAMETERS HalResetDisplayParameters = NULL;


/* STATIC FUNCTIONS *********************************************************/

static VOID
HalClearDisplay (VOID)
{
   WORD *ptr = (WORD*)VideoBuffer;
   ULONG i;

  for (i = 0; i < SizeX * SizeY; i++, ptr++)
    *ptr = ((CHAR_ATTRIBUTE << 8) + ' ');

  CursorX = 0;
  CursorY = 0;
}


VOID
HalScrollDisplay (VOID)
{
  WORD *ptr;
  int i;

  ptr = VideoBuffer + SizeX;
  RtlMoveMemory(VideoBuffer,
		ptr,
		SizeX * (SizeY - 1) * 2);

  ptr = VideoBuffer  + (SizeX * (SizeY - 1));
  for (i = 0; i < SizeX; i++, ptr++)
    {
      *ptr = (CHAR_ATTRIBUTE << 8) + ' ';
    }
}


static VOID
HalPutCharacter (CHAR Character)
{
  WORD *ptr;

  ptr = VideoBuffer + ((CursorY * SizeX) + CursorX);
  *ptr = (CHAR_ATTRIBUTE << 8) + Character;
}


/* PRIVATE FUNCTIONS ********************************************************/

VOID
HalInitializeDisplay (PLOADER_PARAMETER_BLOCK LoaderBlock)
/*
 * FUNCTION: Initalize the display
 * ARGUMENTS:
 *         InitParameters = Parameters setup by the boot loader
 */
{
  if (DisplayInitialized == FALSE)
    {
      ULONG ScanLines;
      ULONG Data;

      VideoBuffer = (WORD *)(0xd0000000 + 0xb8000);
//      VideoBuffer = HalMapPhysicalMemory (0xb8000, 2);

      /* Set cursor position */
//      CursorX = LoaderBlock->cursorx;
//      CursorY = LoaderBlock->cursory;
      CursorX = 0;
      CursorY = 0;

      /* read screen size from the crtc */
      /* FIXME: screen size should be read from the boot parameters */
      WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_COLUMNS);
      SizeX = READ_PORT_UCHAR((PUCHAR)CRTC_DATA) + 1;
      WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_ROWS);
      SizeY = READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
      WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_OVERFLOW);
      Data = READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
      SizeY |= (((Data & 0x02) << 7) | ((Data & 0x40) << 3));
      SizeY++;
      WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_SCANLINES);
      ScanLines = (READ_PORT_UCHAR((PUCHAR)CRTC_DATA) & 0x1F) + 1;
      SizeY = SizeY / ScanLines;

#ifdef BOCHS_30ROWS
      SizeY=30;
#endif
      HalClearDisplay();

      DisplayInitialized = TRUE;
    }
}


VOID
HalResetDisplay(VOID)
/*
 * FUNCTION: Reset the display
 * ARGUMENTS:
 *         None
 */
{
  if (HalResetDisplayParameters == NULL)
    return;

  if (HalOwnsDisplay == TRUE)
    return;

  if (HalResetDisplayParameters(SizeX, SizeY) == TRUE)
    {
      HalOwnsDisplay = TRUE;
      HalClearDisplay();
    }
}


/* PUBLIC FUNCTIONS *********************************************************/

VOID STDCALL
HalAcquireDisplayOwnership(IN PHAL_RESET_DISPLAY_PARAMETERS ResetDisplayParameters)
/*
 * FUNCTION: 
 * ARGUMENTS:
 *         ResetDisplayParameters = Pointer to a driver specific
 *         reset routine.
 */
{
  HalOwnsDisplay = FALSE;
  HalResetDisplayParameters = ResetDisplayParameters;
}


VOID STDCALL
HalDisplayString(IN PCH String)
/*
 * FUNCTION: Switches the screen to HAL console mode (BSOD) if not there
 * already and displays a string
 * ARGUMENT:
 *        string = ASCII string to display
 * NOTE: Use with care because there is no support for returning from BSOD
 * mode
 */
{
  PCH pch;
#ifdef SCREEN_SYNCHRONIZATION
  int offset;
#endif
  static KSPIN_LOCK Lock;
  ULONG Flags;

  pch = String;

  pushfl(Flags);
  __asm__ ("cli\n\t");
  KeAcquireSpinLockAtDpcLevel(&Lock);
  
  if (HalOwnsDisplay == FALSE)
    {
      HalResetDisplay ();
    }
  
#ifdef SCREEN_SYNCHRONIZATION
  WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURHI);
  offset = READ_PORT_UCHAR((PUCHAR)CRTC_DATA)<<8;
  WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURLO);
  offset += READ_PORT_UCHAR((PUCHAR)CRTC_DATA);
  
  CursorY = offset / SizeX;
  CursorX = offset % SizeX;
#endif
  
  while (*pch != 0)
    {
      if (*pch == '\n')
	{
	  CursorY++;
	  CursorX = 0;
	}
      else if (*pch != '\r')
	{
	  HalPutCharacter (*pch);
	  CursorX++;
	  
	  if (CursorX >= SizeX)
	    {
	      CursorY++;
	      CursorX = 0;
	    }
	}
  
      if (CursorY >= SizeY)
	{
	  HalScrollDisplay ();
	  CursorY = SizeY - 1;
	}
  
      pch++;
    }
  
#ifdef SCREEN_SYNCHRONIZATION
  offset = (CursorY * SizeX) + CursorX;
  
  WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURLO);
  WRITE_PORT_UCHAR((PUCHAR)CRTC_DATA, offset & 0xff);
  WRITE_PORT_UCHAR((PUCHAR)CRTC_COMMAND, CRTC_CURHI);
  WRITE_PORT_UCHAR((PUCHAR)CRTC_DATA, (offset >> 8) & 0xff);
#endif
  KeReleaseSpinLockFromDpcLevel(&Lock);
  popfl(Flags);
}


VOID STDCALL
HalQueryDisplayParameters(OUT PULONG DispSizeX,
			  OUT PULONG DispSizeY,
			  OUT PULONG CursorPosX,
			  OUT PULONG CursorPosY)
{
  if (DispSizeX)
    *DispSizeX = SizeX;
  if (DispSizeY)
    *DispSizeY = SizeY;
  if (CursorPosX)
    *CursorPosX = CursorX;
  if (CursorPosY)
    *CursorPosY = CursorY;
}


VOID STDCALL
HalSetDisplayParameters(IN ULONG CursorPosX,
			IN ULONG CursorPosY)
{
  CursorX = (CursorPosX < SizeX) ? CursorPosX : SizeX - 1;
  CursorY = (CursorPosY < SizeY) ? CursorPosY : SizeY - 1;
}

/* EOF */
