/* vi: set sw=4 ts=4: */
/*
 * KON2 - Kanji ON Console -
 * Copyright (C) 1992-1996 Takashi MANABE (manabe@papilio.tutics.tut.ac.jp)
 *
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 */

/* This file provides generic VGA operation
 
     640x480x16 colors (4 planes)  tested on all supported platforms  
	     80 bytes * 480 = 38400 bytes (37KB)
	 800x600x16 colors (4 planes)  tested on FreeBSD
	     100 bytes * 600 = 60000 bytes (58KB)

   It can only support 8/16/24-pixel width font.
*/
#include	<stdio.h>
#include	<string.h>
#include	<unistd.h>
#include	<stdlib.h>
#include    <fcntl.h>

#include	<defs.h>
#include	<vc.h>
#include	<vga.h>

#if defined(SUPPORT_VGA) || defined(SUPPORT_FRAMEBUF)

#ifdef HAVE_SYS_MMAN_H
#include 	<sys/mman.h>
#endif

static inline void VgaSetColor(int color)
{
	static int oldcol = -1; 
    
    if (oldcol == color) return;
    PortOutw(color << 8, VGAGRP_ADDR); 
    oldcol = color;

}

static inline void	VgaOutByte(u_char value)
{
	PortOutw( (((u_short)value) << 8) | 0x08, VGAGRP_ADDR);

}

#if !defined(USE_DEVMEM_FILE) || defined(SUPPORT_FRAMEBUF)

void VgaOpDraw(u_char *code, u_char fc, u_char bc, int width, int height)
{
	
    volatile char	*gram, *vcls;
    int x, i, bytes;

    vcls = gram = gramMem + writeAddr;
	bytes = (width+7)/8;

	VgaResetMode();

	VgaSetColor( bc & 7);
    for (x = 0; x < /* dispInfo.glineChar */ height; x++)
	{
		for(i = 0; i < bytes; i++) vcls[i] = 0;
		vcls += dispInfo.glineByte;
	}

	VgaSetColor(fc);
    if (bc & 0x8)  /* underline? */
    {
		vcls -= dispInfo.glineByte;
		for(i = 0; i < bytes; i++) vcls[i] = 0; /* FIXME: For what? */
    }

    for ( x = 0; x < height; x++)
    {
		for(i = 0; i < bytes; i++)
		{
			if (*code)  /* non-zero, output, otherwise keep background */
        	{
	    		VgaOutByte(*code);
	    		gram[i] = gram[i];
			}
			code++;
		}
		gram += dispInfo.glineByte;
    }
    VgaOutByte(0xFF);
}

void VgaOpCursor(CursorInfo *ci, int xpos, int ypos)
{
    volatile char	*gram;
    u_char	x;
	int i, bytes;
    int	bottom = (cursorBtm + 1 <= dispInfo.glineChar ?
		cursorBtm + 1 : dispInfo.glineChar);
    
    VgaSetColor(15);
   	gram = gramMem + ypos * dispInfo.glineByte + xpos /8;

    PortOutw(0x0F00, VGAGRP_ADDR);	/* color white */
    PortOutw(0x1803, VGAGRP_ADDR);	/* XOR mode */

    x = cursorTop;
    if (kanjiCursor && ci->kanji) 
		bytes = (fontwidth * 2 + 7)/8;
	else bytes = (fontwidth + 7)/8;

	for (;x < bottom;x ++, gram += dispInfo.glineByte) 
    {
		for(i = 0; i < bytes; i++) gram[i] = gram[i];
    } 
    
    PortOutw(0x0003, VGAGRP_ADDR);	/* unmodify mode */
}

void VgaOpClear(int y, int height, int color)
{
	VgaResetMode();

    VgaSetColor(color);  /* 0xXX00 color (index=0) */
	lzero(gramMem + y * dispInfo.glineByte, height * dispInfo.glineByte);
}

void VgaOpFont(u_char *font, int size, int op)
{
	if (!gramMem) return; 
	
	switch(op)
	{
	case SAVE_FONT:
		memcpy(font, gramMem, size);
		break;
	case RESTORE_FONT:
		memcpy(gramMem, font, size);
		break;
	}
}

#if defined(linux)
#define MEM_DEVICE "/dev/mem"    
#elif defined(__SunOS__)
#define MEM_DEVICE "/dev/xsvc"

#elif defined(__FreeBSD__)
#if (__FreeBSD__ <= 3)
#define MEM_DEVICE "/dev/vga" /* "/dev/ttyv0" */
#else
#define MEM_DEVICE "/dev/mem"
#endif

#elif defined(__OpenBSD__)
#define MEM_DEVICE "/dev/xf86"
#else 
#define MEM_DEVICE "/dev/mem"
#endif

int VgaOpMapVidMem(void)
{

#if defined(__QNX__)
#undef mmap   /* otherwise mmap is defined as mmap_file */
	gramMem = (unsigned char *)mmap(0, dispInfo.gsize, PROT_READ|PROT_WRITE,
			MAP_PHYS|MAP_SHARED, NOFD, GRAPH_BASE);

#elif defined(__Minix__)
	extern int MinixMmapVideoRAM(unsigned char **gMem);

	return MinixMmapVideoRAM(&gramMem);

#elif defined(__Mach__)
	extern int MachMmapVideoRAM(unsigned char **gMem);

	return MachMmapVideoRAM(&gramMem);

#elif defined(__MSDOS__)
	extern int DJGPPMmapVideoRAM(unsigned char **gMem);

	return DJGPPMmapVideoRAM(&gramMem);

#else  /* non-QNX non-Minix */

    int	devMem;
   
    if ((devMem = open(MEM_DEVICE, O_RDWR|O_NONBLOCK) ) < 0)
    {
		if (strcmp(MEM_DEVICE, "/dev/mem"))  /* try it one more time with /dev/mem */
			devMem = open("/dev/mem", O_RDWR|O_NONBLOCK);
		if (devMem < 0)
		{
        	Perror("Failed to open " MEM_DEVICE);
			return FAILURE;
		}
    }

    gramMem = (unsigned char *)mmap(
#if defined(linux)
	    (__ptr_t)0,
#else
	    0,
#endif
	    dispInfo.gsize, /*  GRAPH_MEM, */
	    PROT_READ|PROT_WRITE, MAP_SHARED, devMem, GRAPH_BASE);

    close(devMem);

#endif 

	if (gramMem == (char *)MAP_FAILED)
    {
		Perror("mmap graphics memory failed");
		return FAILURE;
    }

    return SUCCESS;
}

void VgaOpUnmapVidMem(void)
{
#if defined(__Minix__)
	extern void MinixMunmapVideoRAM(void);
	MinixMunmapVideoRAM();  /* do MIOCUNMAP */
#elif defined(__Mach__)
	extern void MachMunmapVideoRAM(void);
	MachMunmapVideoRAM();
#elif defined(__MSDOS__)
	extern void DJGPPMunmapVideoRAM(void);
	DJGPPMunmapVideoRAM();  /* DJGPP stuff */

#elif defined(__QNX__)
#undef munmap  /* cancel the munmap_file */
#endif

    munmap(gramMem, dispInfo.gsize);  /* For Minix, it'll free it */
    
}

#else   /* USE_DEVMEM_FILE */

static u_char zeroline[80]; /* 80x8 = 640 width  glineByte==80 */

static inline LseekVram(int y, int x)
{
	
	lseek(gramFile, 0xA0000 + y * dispInfo.glineByte + x, SEEK_SET);
}

void VgaOpDraw(u_char *code, u_char fc, u_char bc, int width, int height)
{
	
    int x, i, bytes;

	bytes = (width+7)/8;

	VgaResetMode();

	VgaSetColor( bc & 7);
    for (x = 0; x < /* dispInfo.glineChar */ height; x++)
	{
		LseekVram(x, writeAddr);
		write(gramFile, zeroline, bytes);
	}

	VgaSetColor(fc);

    for ( x = 0; x < height; x++)
    {
		for(i = 0; i < bytes; i++)
		{
			u_char tmp;

			if (*code)  /* non-zero, output, otherwise keep background */
        	{
	    		VgaOutByte(*code);

				LseekVram(x, writeAddr + i);
				read(gramFile, &tmp, 1);
				lseek(gramFile, -1, SEEK_CUR);
				write(gramFile, &tmp, 1);	
			}
			code++;
		}
    }
    VgaOutByte(0xFF);
}

void VgaOpCursor(CursorInfo *ci, int xpos, int ypos)
{
    u_char	x;
	int i, bytes;
    int	bottom = (cursorBtm + 1 <= dispInfo.glineChar ?
		cursorBtm + 1 : dispInfo.glineChar) - cursorTop;
   	u_int addr; 

    VgaSetColor(15);
	addr = ypos * dispInfo.glineByte + xpos /8;

    PortOutw(0x0F00, VGAGRP_ADDR);	/* color white */
    PortOutw(0x1803, VGAGRP_ADDR);	/* XOR mode */

    if (kanjiCursor && ci->kanji) 
		bytes = (fontwidth * 2 + 7)/8;
	else bytes = (fontwidth + 7)/8;

	for ( x = 0; x < bottom; x++ /*, gram += dispInfo.glineByte */) 
    {
		LseekVram(x, addr);
		for(i = 0; i < bytes; i++) 
		{
			u_char tmp;

			read(gramFile, &tmp, 1);
			lseek(gramFile, -1, SEEK_CUR);
			write(gramFile, &tmp, 1);
		}
    } 
    
    PortOutw(0x0003, VGAGRP_ADDR);	/* unmodify mode */
}

void VgaOpClear(int y, int height, int color)
{
	u_char zero = 0x0;
	int i;
	
	VgaResetMode();

    VgaSetColor(color);  /* 0xXX00 color (index=0) */
	
	LseekVram(y, 0);
	for(i = 0; i < height; i++)
		write(gramFile, zeroline, sizeof(zeroline));
}

void VgaOpFont(u_char *font, int size, int op)
{
	if (gramFile < 0) return; 
	
	LseekVram(0, 0);
	switch(op)
	{
	case SAVE_FONT:
		read(gramFile, font, size);
		break;
	case RESTORE_FONT:
		write(gramFile, font, size);
		break;
	}
}

#define MEM_DEVICE "/dev/mem"

int VgaOpMapVidMem(void)
{
	gramFile = open(MEM_DEVICE, O_RDWR|O_NONBLOCK); /* non-blocking */
	if (gramFile < 0)
		return FAILURE;
	return SUCCESS;
}

void VgaOpUnmapVidMem(void)
{
	if (gramFile >= 0) 
	{
		close(gramFile);
		gramFile = -1;
	}
}

#endif  /* USE_DEVMEM_FILE */

#endif /* defined(SUPPORT_VGA) || defined(SUPPORT_FRAMEBUF) */

