/* hw.c */

#include <sys/types.h>
#include <sys/kd.h>
#include <sys/mman.h>
#include <signal.h>
#include <asm/io.h>

#define VGABASE		0x3d4

static struct regs {
	unsigned char clock;
	unsigned char sequencer[5];
	unsigned char graphics[9];
	unsigned char attribute[21];
	unsigned char crtc[25];
} graph_regs = {
	0x63,
	3,1,15,0,14,
	0,0,0,0,0,64,5,15,255,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,65,0,15,0,0,
	0x5f,0x4f,0x50,0x82,0x54,0x80,0xbf,0x1f,
	0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,
	0x9c,0x8e,0x8f,0x28,0x40,0x96,0xb9,0xa3,0xff
};

#define VIDEORAMBASE	0xa0000

unsigned char *vram;

static unsigned char font[8192],pal[768];
static struct regs oldregs;

static mode = 0;

static
wait_blank()
{
	int i;

	for (i=0; i<100000; i++)
		if ((inb(VGABASE+6) & 8))
			break;
	for (i=0; i<100000; i++)
		if ((inb(VGABASE+6) & 8) == 0)
			break;
}

hwrestorestuff(a)
struct regs *a;
{
	int i;

	wait_blank();
	outw((a->sequencer[0]&0xfc) << 8,0x3c4);
	outb(a->clock,0x3c2);
	for (i=1; i<5; i++)
		outw(a->sequencer[i]<<8 | i,0x3c4);
	inb(VGABASE+6);
	for (i=0; i<16; i++) {
		outb(i,0x3c0);
		outb(a->attribute[i],0x3c0);
	}
	for (; i<21; i++) {
		outb(i+32,0x3c0);
		outb(a->attribute[i],0x3c0);
	}
	a->crtc[3] |= 0x80;
	a->crtc[17] &= 0x7f;
	outw(a->crtc[3]<<8 | 3,VGABASE);
	outw(a->crtc[17]<<8 | 17,VGABASE);
	for (i=0; i<25; i++)
		outw(a->crtc[i]<<8 | i,VGABASE);
	for (i=0; i<9; i++)
		outw(a->graphics[i]<<8 | i,0x3ce);
	outw(0x0300,0x3c4);
}

static
hwsavestuff(a)
struct regs *a;
{
	int i;

	a->clock = inb(0x3cc);
	for (i=0; i<25; i++) {
		outb(i,VGABASE);
		a->crtc[i] = inb(VGABASE+1);
	}
	for (i=0; i<5; i++) {
		outb(i,0x3c4);
		a->sequencer[i] = inb(0x3c5);
	}
	for (i=0; i<9; i++) {
		outb(i,0x3ce);
		a->graphics[i] = inb(0x3cf);
	}
	inb(VGABASE+6);
	for (i=0; i<16; i++) {
		outb(i,0x3c0);
		a->attribute[i] = inb(0x3c1);
		outb(a->attribute[i],0x3c0);
	}
	for (; i<21; i++) {
		outb(i+32,0x3c0);
		a->attribute[i] = inb(0x3c1);
		outb(a->attribute[i],0x3c0);
	}
	outw(0x0300,0x3c4);
}

text_mode()
{
	int i;

	if (mode == 0)
		return;
	mode = 0;
	hwrestorestuff(&oldregs);
	outw(0x0000,0x3c4);
	outb(0,0x3c8);
	for (i=0; i<256; i++)
		outb(pal[i],0x3c9);
	wait_blank();
	inb(VGABASE+6);
	outb(0x30,0x3c0);
	outb(1,0x3c0);
	outw(0x0402,0x3c4);
	outw(0x0604,0x3c4);
	outw(0x0204,0x3ce);
	outw(0x0005,0x3ce);
	outw(0x0506,0x3ce);
	memcpy(vram,font,8192);
	inb(VGABASE+6);
	outb(0x30,0x3c0);
	outb(oldregs.attribute[16],0x3c0);
	outw(oldregs.sequencer[2]<<8 | 2,0x3c4);
	outw(oldregs.sequencer[4]<<8 | 4,0x3c4);
	outw(oldregs.graphics[4]<<8 | 4,0x3ce);
	outw(oldregs.graphics[5]<<8 | 5,0x3ce);
	outw(oldregs.graphics[6]<<8 | 6,0x3ce);
	outw(0x0300,0x3c4);
	ioctl(2,KDSETMODE,KD_TEXT);
}

static
hwsaveall()
{
	int i;

	hwsavestuff(&oldregs);
	outb(0,0x3c7);
	for (i=0; i<768; i++)
		pal[i] = inb(0x3c9);
	inb(VGABASE+6);
	outb(0x30,0x3c0);
	outb(1,0x3c0);
	outw(0x0402,0x3c4);
	outw(0x0604,0x3c4);
	outw(0x0204,0x3ce);
	outw(0x0005,0x3ce);
	outw(0x0506,0x3ce);
	memcpy(font,vram,8192);
}

void
eexit()
{
	text_mode();
	_exit(0);
}

get_rid_of_root()
{
	int i;

	if ((i = open("/dev/mem",2)) < 0) {
		write_2("can't open /dev/mem\n");
		_exit(1);
	}
	ioperm(0x3b4,0x3e0-0x3b4,1);
	vram = mmap(0,65536,PROT_READ|PROT_WRITE,MAP_SHARED,i,VIDEORAMBASE);
	if ((int)vram == -1) {
		write_2("mmap failed\n");
		_exit(1);
	}
	close(i);
	setuid(getuid());
}

graph_mode(c)
unsigned char *c;
{
	int i;

	ioctl(2,KDSETMODE,KD_GRAPHICS);
	hwsaveall();
	for (i=0; i<NSIG; i++)
		signal(i,eexit);
	hwrestorestuff(&graph_regs);
	outb(0,0x3c8);
	for (i=0; i<768; i++)
		outb(c[i]>>2,0x3c9);
	mode = 1;
	memset(vram,0,320*200);
}
