/*
  This file assumes 132 column textmode :-).
 
  This program tries to fix problems with extended textmodes on some cards. The problem is that for 132x43 textmode, some
  BIOSes set the vertical display end register to 349 (350), instead of 343 (344 = 3 * 8 scanlines). Because in Linux textmode
  video memory is usually filled with old text that has already scrolled away (this includes the area below the 43rd textmode
  line, which changes when the console scrolls), the top half of a constantly changing irrelevant text line is visible
  at the bottom of the screen, which is very annoying.

  This program sets the VGA Vertical Display End register to the proper value.

  The problem is at least present in the BIOS of most Cirrus Logic 542x based cards, and some WD90C03x based cards.


  You can also change the number scanlines and number of lines per character of 132x43 textmode to improve readability.
 
  
VGA CRTC 0x12	bits 0-7	Vertical Display End Register bits 0-7
VGA CRTC 0x07	bit 1		Vertical Display End Register bit 8

Cirrus 542x BIOS v1.10 132x43:	0x15d (349)	(this is the wrong value that the BIOS sets)
Correct value for 132x43:	0x157 (343)
Correct value for 132x44:	0x15f (351)


VGA CRTC 0x09	bits 0-4	Number of scanlines in a textmode font character.
VGA MiscOut	bits 6-7	Indicates number of scanlines: 1 = 400, 2 = 350, 3 = 480.
VGA CRTC 0x06   bits 0-7	Vertical Total register bits 0-7 (should be #scanlines - 2)
				Bit 8 is in VGA CRTC 0x07, bit 0
				Bit 9 is in VGA CRTC 0x07, bit 5
VGA CRTC 0x10	bits 0-7	Vertical Retrace Start
				Bit 8 is in VGA CRTC 0x07, bit 2
				Bit 9 is in VGA CRTC 0x07, bit 7
VGA CRTC 0x11	bits 0-3	Vertical Retrace End (last 4 bits)
VGA CRTC 0x15	bits 0-7	Vertical Blank Start
				Bit 8 is in VGA CRTC 0x07 bit 3

*/


/* Comment this out if setting graphics modes is undesirable. You can load proper fonts with restorefont. */
#define FIX_FONT


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <vga.h>
#include "../libvga.h"	/* For port I/O macros. */



static void fixfont( int );


void main( int argc, char *argv[] ) {
	int vgaIOBase;
	unsigned char val;

	vga_disabledriverreport();
	vga_setchipset(VGA);
	vga_init();

	if (argc == 1) {
		printf("Fiddle with 132x43 textmode VGA registers.\n");
		printf("Syntax: fix132x43 option (one at a time).\n");
		printf("	-f	Fix problem of annoying changing line of text at bottom of screen.\n");
		printf("	-v	Switch to 9 line characters (400 line frame, 70 Hz).\n");
#ifdef INCLUDE_480LINEMODE
		printf("	-w	Switch to 11 line characters (480 line frame, 60 Hz).\n");
#endif
		printf("	-r	Switch to 8 line characters again (350 line frame, 70 Hz).\n");
		exit(0);
	}

	if (argv[1][0] != '-') {
	
		printf("Must specify -f, -v or -r.\n");
		exit(1);
	}
	
	if (argv[1][1] != 'f' && argv[1][1] != 'v' && argv[1][1] != 'w' && argv[1][1] != 'r') {
		printf("Must specify -f, -v, or -r.\n");
		exit(1);
	}

	/* Deprotect CRT registers 0-7. */
	vgaIOBase = (inb(0x3cc) & 0x01) ? 0x3D0 : 0x3B0;
        outb(vgaIOBase + 4, 0x11);
        val = inb(vgaIOBase + 5);
        outb(vgaIOBase + 5, val & 0x7f);

	if (argv[1][1] == 'f') {
		/* Fix stupid bottom line. */
		outb(vgaIOBase + 4, 0x12);
		outb(vgaIOBase + 5, 0x57);		/* Value for 43 lines (343). */
	}

	if (argv[1][1] == 'r') {
		/* Set 8 line characters, 350 line frame (344 used). */

		outb(vgaIOBase + 4, 0x09);
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xe0) | 7);	/* Set 8-line characters. */

		val = inb(0x3cc);
		outb(0x3c2, (val & 0x3f) | (2 << 6));	/* 350 scanlines. */

		outb(vgaIOBase + 4, 0x12);		/* Vertical Display End */
		outb(vgaIOBase + 5, 0x57);		/* Value for 43 lines (343). */

		outb(vgaIOBase + 4, 0x10);		/* Retrace Start */
		outb(vgaIOBase + 5, 0x83);		/* Value for 350 line frame. */
		
		outb(vgaIOBase + 4, 0x11);		/* Retrace End */
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xf0) | 0x05);

		outb(vgaIOBase + 4, 0x15);		/* Vertical Blank Start */
		outb(vgaIOBase + 5, 0x63);		/* Value for 350 line frame. */
	}

	if (argv[1][1] == 'v') {
		/* Set 9 line characters, 400 line frame (387 used). */

		outb(vgaIOBase + 4, 0x09);
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xe0) | 8);	/* Set 9-line characters. */

		val = inb(0x3cc);
		outb(0x3c2, (val & 0x3f) | (1 << 6));	/* 400 scanlines. */

		outb(vgaIOBase + 4, 0x12);
		outb(vgaIOBase + 5, 0x82);		/* End scanline is 43 * 9 - 1 = 386 */

		outb(vgaIOBase + 4, 0x10);		/* Retrace Start */
		outb(vgaIOBase + 5, 0x9c);		/* Value for 400 line frame. */

		outb(vgaIOBase + 4, 0x11);		/* Retrace End */
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xf0) | 0x0e);

		outb(vgaIOBase + 4, 0x15);		/* Vertical Blank Start */
		outb(vgaIOBase + 5, 0x95);		/* Value for 400 line frame. */

		#ifdef FIX_FONT		
		fixfont(9);
		#endif
	}

#ifdef INCLUDE_480LINEMODE
	if (argv[1][1] == 'w') {
		/* Set 11 line characters, 480 line frame (473 used). */

		outb(vgaIOBase + 4, 0x09);
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xe0) | 10);	/* Set 11-line characters. */

		outb(0x3c2, 0xeb);

		outb(vgaIOBase + 4, 0x12);
		outb(vgaIOBase + 5, 0xd8);		/* End scanline is 43 * 11 - 1 = 472 */

		outb(vgaIOBase + 4, 0x10);		/* Retrace Start */
		outb(vgaIOBase + 5, 0xf4 /*0xea*/);	/* Value for 480 line frame. ?? */

		outb(vgaIOBase + 4, 0x11);		/* Retrace End */
		val = inb(vgaIOBase + 5);
		outb(vgaIOBase + 5, (val & 0xf0) | 0x0c);

		outb(vgaIOBase + 4, 0x15);		/* Vertical Blank Start */
		outb(vgaIOBase + 5, 0xf4 /*0xe7*/);	/* Value for 480 line frame. */
		
		#ifdef FIX_FONT
		fixfont(11);
		#endif
	}
#endif

	exit(0);
}



/* Clear offsets 8-31 of each character bitmap (the BIOS usually leaves some trash here from the 16 line font that was loaded */
/* prior to setting 132x43 (8 line font) textmode). */

static void fixfont( int lines ) {
	unsigned char font[8192];
	int i;
	vga_setmode(G640x480x16);
	vga_gettextfont(font);

	for (i = 0; i < 256; i++)
		memset(font + i * 32 + 8, 0, 32 - 8);		/* Clear remaining part of character bitmap buffer. */

	vga_puttextfont(font);
	vga_setmode(TEXT);
}
