// Wengier: TEXT BLINKING support

#include <string.h>

#include "vDos.h"
#include "ttf.h"
#include "mem.h"
#include "inout.h"
#include "int10.h"
#include "mouse.h"
#include "vga.h"
#include "bios.h"

#define SEQ_REGS 0x05
#define GFX_REGS 0x09
#define ATT_REGS 0x15

VideoModeBlock ModeList_VGA[]={
//mode,		type,	sw,		sh,		tw,	th,	cw,	ch,	pt,	pstart,		plength,htot,	vtot,	hde,vde
{ 0x002,	M_TEXT,	720,	400,	80,	25,	8,	16,	8,	0xB8000,	0x1000,	100,	449,	80,	400 },
{ 0x003,	M_TEXT,	720,	400,	80,	25,	8,	16,	8,	0xB8000,	0x1000,	100,	449,	80,	400 },
{ 0x007,	M_TEXT,	720,	400,	80,	25,	8,	16,	8,	0xB0000,	0x1000,	100,	449,	80,	400 },

{ 0x00f,	M_EGA,	640,	350,	80,	25,	8,	14,	2,	0xA0000,	0x8000,	100,	449,	80,	350 },	// was EGA_2
{ 0x010,	M_EGA,	640,	350,	80,	25,	8,	14,	2,	0xA0000,	0x8000,	100,	449,	80,	350 },
{ 0x011,	M_EGA,	640,	480,	80,	30,	8,	16,	1,	0xA0000,	0xA000,	100,	525,	80,	480 },	// was EGA_2
{ 0x012,	M_EGA,	640,	480,	80,	30,	8,	16,	1,	0xA0000,	0xA000,	100,	525,	80,	480 },

{ 0xFFFF,	M_ERROR,0,		0,		0,	0,	0,	0,	0,	0x00000,	0x0000,	0,		0,		0,	0,	},
};

static Bit8u text_palette[64*3]=
{
	0x00,0x00,0x00,	0x00,0x00,0x2a,	0x00,0x2a,0x00,	0x00,0x2a,0x2a,	0x2a,0x00,0x00,	0x2a,0x00,0x2a,	0x2a,0x2a,0x00,	0x2a,0x2a,0x2a,
	0x00,0x00,0x15,	0x00,0x00,0x3f,	0x00,0x2a,0x15,	0x00,0x2a,0x3f,	0x2a,0x00,0x15,	0x2a,0x00,0x3f,	0x2a,0x2a,0x15,	0x2a,0x2a,0x3f,
	0x00,0x15,0x00,	0x00,0x15,0x2a,	0x00,0x3f,0x00,	0x00,0x3f,0x2a,	0x2a,0x15,0x00,	0x2a,0x15,0x2a,	0x2a,0x3f,0x00,	0x2a,0x3f,0x2a,
	0x00,0x15,0x15,	0x00,0x15,0x3f,	0x00,0x3f,0x15,	0x00,0x3f,0x3f,	0x2a,0x15,0x15,	0x2a,0x15,0x3f,	0x2a,0x3f,0x15,	0x2a,0x3f,0x3f,
	0x15,0x00,0x00,	0x15,0x00,0x2a,	0x15,0x2a,0x00,	0x15,0x2a,0x2a,	0x3f,0x00,0x00,	0x3f,0x00,0x2a,	0x3f,0x2a,0x00,	0x3f,0x2a,0x2a,
	0x15,0x00,0x15,	0x15,0x00,0x3f,	0x15,0x2a,0x15,	0x15,0x2a,0x3f,	0x3f,0x00,0x15,	0x3f,0x00,0x3f,	0x3f,0x2a,0x15,	0x3f,0x2a,0x3f,
	0x15,0x15,0x00,	0x15,0x15,0x2a,	0x15,0x3f,0x00,	0x15,0x3f,0x2a,	0x3f,0x15,0x00,	0x3f,0x15,0x2a,	0x3f,0x3f,0x00,	0x3f,0x3f,0x2a,
	0x15,0x15,0x15,	0x15,0x15,0x3f,	0x15,0x3f,0x15,	0x15,0x3f,0x3f,	0x3f,0x15,0x15,	0x3f,0x15,0x3f,	0x3f,0x3f,0x15,	0x3f,0x3f,0x3f
};

static Bit8u mtext_palette[64*3]=
{
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f 
};

static Bit8u mtext_s3_palette[64*3]=
{
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,
	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,	0x00,0x00,0x00,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,	0x2a,0x2a,0x2a,
	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f,	0x3f,0x3f,0x3f 
};

static Bit8u ega_palette[64*3]=
{
	0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
	0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
	0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
	0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
	0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
	0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
	0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
	0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f
};

VideoModeBlock * CurMode;
extern bool blinking;

static bool SetCurMode(Bit16u mode)
	{
	for (Bitu i = 0; ModeList_VGA[i].mode != 0xffff; i++)
		if (ModeList_VGA[i].mode == mode)
			{
			CurMode = &ModeList_VGA[i];
			return true;
			}
	return false;
	}

void FinishSetMode(bool clearmem)
	{
	if (clearmem)																	// Clear video memory if requested
		{
		switch (CurMode->type)
			{
		case M_TEXT:
			{
			Mem_rStosw(CurMode->mode == 7 ? 0xb0000 : 0xb8000, 0x0720, 16*1024);
			if (newAttrChar)
				for (int i = ttf.cols*ttf.lins-1; i >= 0; i--)						// Force text screen to redraw
					curAttrChar[i] = newAttrChar[i]-1;
			break;
			}
		case M_EGA:
			memset(vga.memlinear, 0, vga.vmemsize);									// Hack we just acess the memory directly
			memset(vga.fastmem, 0, vga.vmemsize<<1);
			break;
			}
		}

	Mem_aStosb(BIOS_VIDEO_MODE, (Bit8u)CurMode->mode);								// Setup the BIOS
	Mem_aStosw(BIOS_SCREEN_COLUMNS, (Bit16u)CurMode->twidth);
	Mem_aStosw(BIOS_VIDEO_PAGE_SIZE, (Bit16u)CurMode->plength);
	Mem_aStosw(BIOS_VIDEO_PORT, ((CurMode->mode==7) || (CurMode->mode==0x0f)) ? 0x3b4 : 0x3d4);
	Mem_aStosb(BIOS_ROWS_ON_SCREEN_MINUS_1, (Bit8u)(CurMode->theight-1));
	Mem_aStosw(BIOS_FONT_HEIGHT, (Bit16u)CurMode->cheight);
	Mem_aStosb(BIOS_VIDEO_INFO_0, (0x60|(clearmem ? 0 : 0x80)));
	Mem_aStosb(BIOS_VIDEO_INFO_1, 0x09);

	Mem_aStosb(BIOS_VIDEO_COMBO, 0x0b);												// This is an index into the dcc table:
	Mem_aStosd(BIOS_VS_POINTER, int10.rom.video_save_pointers);

	if (CurMode->type == M_TEXT)													// Set cursor shape
		INT10_SetCursorShape(6, 7);
	for (Bit8u ct = 0; ct < 8; ct++)												// Set cursor pos for page 0..7
		INT10_SetCursorPos(0, 0, ct);
	INT10_SetActivePage(0);															// Set active page 0

	RealSetVec(0x43, CurMode->cheight == 14 ? int10.rom.font_14 : int10.rom.font_16);	// Set vector to font
	}

bool INT10_SetVideoMode(Bit8u mode)
	{
	bool clearmem = true;
	if (mode&0x80)
		{
		clearmem = false;
		mode -= 0x80;
		}

	if (!SetCurMode(mode))
		return false;

	// Setup the VGA to the correct mode
	IO_WriteB(0x3c4, 0);
	IO_WriteB(0x3c5, 1);															// Reset
	IO_WriteB(0x3c4, 1);
	IO_WriteB(0x3c5, 0x20);															// Screen off

	bool mono_mode = (mode == 7)|(mode == 0xf);  
	Bit16u crtc_base = mono_mode ? 0x3b4 : 0x3d4;
	Bit8u misc_output = 2 | (mono_mode ? 0 : 1);									// Setup MISC Output Register
	Bitu vretrace, vblank_trim;

	switch (CurMode->vdispend)
		{
	case 400: 
		misc_output |= 0x60;
		vretrace = CurMode->vdispend+12;
		vblank_trim = 6;
		break;
	case 480:
		misc_output |= 0xe0;
		vretrace = CurMode->vdispend+10;
		vblank_trim = 7;
		break;
	case 350:
		misc_output |= 0xa0;
		vretrace = CurMode->vdispend+37;
		vblank_trim = 5;
		break;
		}
	IO_WriteB(0x3c2, misc_output);													// Setup for 3b4 or 3d4

	Bit8u seq_data[SEQ_REGS];														// Program Sequencer
	memset(seq_data, 0, SEQ_REGS);
	
	seq_data[0] = 0x3;																// Not reset
	seq_data[1] = 0x21;																// Screen still disabled, will be enabled at end of setmode
	
	seq_data[4] |= 2;																// More than 64kb
	if (CurMode->type == M_TEXT)
		{
		seq_data[2] |= 3;															// Enable plane 0 and 1
		seq_data[4] |= 1;															// Alpanumeric
		seq_data[4] |= 4;															// Odd/even enabled
		}
	else
		seq_data[2] |= 0xf;															// Enable all planes for writing
	for (Bit8u ct = 0; ct < SEQ_REGS; ct++)
		{
		IO_WriteB(0x3c4, ct);
		IO_WriteB(0x3c5, seq_data[ct]);
		}

	// Program CRTC
	for (Bit8u ct = 0; ct <= 0x18; ct++)											// Clear all the regs
		{
		IO_WriteB(crtc_base, ct);
		IO_WriteB(crtc_base+1, 0);
		}

	IO_WriteB(crtc_base, 0);														// Horizontal total
	IO_WriteB(crtc_base+1, (Bit8u)(CurMode->htotal-5));
	IO_WriteB(crtc_base, 1);														// Horizontal display end
	IO_WriteB(crtc_base+1, (Bit8u)(CurMode->hdispend-1));
	IO_WriteB(crtc_base, 2);														// Start horizontal blanking
	IO_WriteB(crtc_base+1, (Bit8u)CurMode->hdispend);
	Bitu blank_end = (CurMode->htotal-2)&0x7f;										// End horizontal blanking
	IO_WriteB(crtc_base, 3);
	IO_WriteB(crtc_base+1, 0x80|(blank_end&0x1f));
	IO_WriteB(crtc_base, 4);														// Start Horizontal Retrace
	IO_WriteB(crtc_base+1, (Bit8u)(CurMode->hdispend+(CurMode->type == M_TEXT ? 5 : 4)));

	Bitu ret_end;																	// End horizontal retrace
	if (CurMode->type == M_TEXT)
		ret_end = (CurMode->htotal-3) & 0x1f;
	else
		ret_end = (CurMode->htotal-4) & 0x1f;
	IO_WriteB(crtc_base, 5);
	IO_WriteB(crtc_base+1, (Bit8u)(ret_end|(blank_end&0x20)<<2));

	IO_WriteB(crtc_base, 6);															// Vertical total
	IO_WriteB(crtc_base+1, (Bit8u)(CurMode->vtotal-2));

	IO_WriteB(crtc_base, 0x10);														// Vertical retrace start
	IO_WriteB(crtc_base+1, (Bit8u)vretrace);

	IO_WriteB(crtc_base, 0x11);														// Vertical retrace end
	IO_WriteB(crtc_base+1, (vretrace+2) & 0xF);

	IO_WriteB(crtc_base, 0x12);														// Vertical display end
	IO_WriteB(crtc_base+1, (Bit8u)(CurMode->vdispend-1));
	
	Bit8u max_scanline = ((CurMode->vdispend+vblank_trim) & 0x200) >> 4;

	Bitu line_compare = (CurMode->vtotal < 1024) ? 1023 : 2047;						// Line compare
	IO_WriteB(crtc_base, 0x18);
	IO_WriteB(crtc_base+1, line_compare&0xff);
	max_scanline |= (line_compare & 0x200) >> 3;
	Bit8u underline = 0;

	if (CurMode->type == M_TEXT)
		{
		max_scanline |= CurMode->cheight-1;
		underline = mono_mode ? 0x0f : 0x1f;										// Mode 7 uses a diff underline position
		}
	else if (CurMode->vdispend == 350)
		underline = 0x0f;

	IO_WriteB(crtc_base, 0x09);
	IO_WriteB(crtc_base+1, max_scanline);
	IO_WriteB(crtc_base, 0x14);
	IO_WriteB(crtc_base+1, underline);

	IO_WriteB(crtc_base, 0x07);														// OverFlow
	IO_WriteB(crtc_base+1, CurMode->type == M_TEXT ? 31 : 62);

	IO_WriteB(crtc_base, 0x13);														// Offset register
	IO_WriteB(crtc_base+1, (CurMode->hdispend/2) & 0xff);

	Bit8u mode_control = 0;															// Mode control
	switch (CurMode->type)
		{
	case M_TEXT:
		mode_control = 0xa3;
		break;
	case M_EGA:
		if (CurMode->mode == 0x11)													// 0x11 also sets address wrap.  thought maybe all 2 color modes did but 0x0f doesn't.
			mode_control = 0xc3;													// so.. 0x11 or 0x0f a one off?
		else
			mode_control = 0xe3;
		break;
		}

	IO_WriteB(crtc_base, 0x17);
	IO_WriteB(crtc_base+1, mode_control);

	IO_WriteB(0x3c2, misc_output);													// Write Misc Output
	
	Bit8u gfx_data[GFX_REGS];														// Program Graphics controller
	memset(gfx_data, 0, GFX_REGS);
	gfx_data[0x7] = 0xf;															// Color don't care
	gfx_data[0x8] = 0xff;															// Bitmask
	switch (CurMode->type)
		{
	case M_TEXT:
		gfx_data[0x5] |= 0x10;														// Odd/even mode
		gfx_data[0x6] |= mono_mode ? 0x0a : 0x0e;									// Either b800 or b000
		break;
	case M_EGA:
		gfx_data[0x6] |= 0x05;														// Graphics mode at 0xa000-affff
		break;
		}
	for (Bit8u ct = 0; ct < GFX_REGS; ct++)
		{
		IO_WriteB(0x3ce, ct);
		IO_WriteB(0x3cf, gfx_data[ct]);
		}
	Bit8u att_data[ATT_REGS];
	memset(att_data, 0, ATT_REGS);
	att_data[0x12] = 0xf;															// Always have all color planes enabled

	switch (CurMode->type)															// Program Attribute Controller
		{
	case M_EGA:
		att_data[0x10] = 1;															// Color Graphics
		switch (CurMode->mode)
			{
		case 0x0f:
			att_data[0x10] |= 0x0a;													// Monochrome
			att_data[0x01] = 0x08;
			att_data[0x04] = 0x18;
			att_data[0x05] = 0x18;
			att_data[0x09] = 0x08;
			att_data[0x0d] = 0x18;
			break;
		case 0x11:
			for (int i = 1; i < 16; i++)
				att_data[i] = 0x3f;
			break;
		case 0x10:
		case 0x12: 
			goto att_text16;
			}
		break;
	case M_TEXT:
		att_data[0x13] = 0;
		att_data[0x10] = 0;															// Color text no blinking, 8 Bit characters
		Mem_aStosb(BIOS_VDU_COLOR_REGISTER, 0x30);
att_text16:
		if (CurMode->mode == 7)
			{
			att_data[0] = 0x00;
			att_data[8] = 0x10;
			for (int i = 1; i < 8; i++)
				{
				att_data[i] = 0x08;
				att_data[i+8] = 0x18;
				}
			}
		else
			{
			for (Bit8u ct = 0; ct < 8; ct++)
				{
				att_data[ct] = ct;
				att_data[ct+8] = ct+0x38;
				}
			att_data[0x06] = 0x14;													// Odd Color 6 yellow/brown.
			}
		break;
		}
	IO_ReadB(mono_mode ? 0x3ba : 0x3da);

	Bit8u modeset_ctl = Mem_aLodsb(BIOS_VIDEO_INFO_2);
	if ((modeset_ctl & 8) == 0)
		{
		for (Bit8u ct = 0; ct < ATT_REGS; ct++)
			{
			IO_WriteB(0x3c0, ct);
			IO_WriteB(0x3c0, att_data[ct]);
			}
		IO_WriteB(0x3c0, 0x20);														// Disable palette access
		IO_WriteB(0x3c0, 0x00);
		IO_WriteB(0x3c6, 0xff);														// Reset Pelmask

		IO_WriteB(0x3c8, 0);														// Setup the DAC
		if (CurMode->type == M_TEXT)
			for (int i = 0; i < 64*3; i++)
				IO_WriteB(0x3c9, CurMode->mode == 7 ? mtext_palette[i] : text_palette[i]);
		else
			for (int i = 0; i < 64*3; i++)
				IO_WriteB(0x3c9, CurMode->mode > 0xf ? text_palette[i] : mtext_s3_palette[i]);
		}
	else
		for (Bit8u ct = 0x10; ct < ATT_REGS; ct++)
			if (ct != 0x11)															// Skip overscan register
				{
				IO_WriteB(0x3c0, ct);
				IO_WriteB(0x3c0, att_data[ct]);
				}

	if (CurMode->type == M_TEXT)													// Setup some special stuff for different modes
		Mem_aStosb(BIOS_VDU_CONTROL, (blinking ? 0x20 : 0) + (CurMode->mode == 2 ? 0x0d : 0x09));

	FinishSetMode(clearmem);

	IO_ReadB(mono_mode ? 0x3ba : 0x3da);											// Set vga attrib register into defined state
	IO_WriteB(0x3c0, 0x20);

	IO_WriteB(0x3c4, 1);															// Enable screen memory access
	IO_WriteB(0x3c5, seq_data[1] & ~0x20);
	return true;
	}
