#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <mem.h>

#define _M_CO80 0x3
#define _M_VESA_320x200x256 0x13
#define _M_VESA_640x480x256 0x101
#define _M_VESA_800x600x256 0x103
#define _M_VESA_1024x768x256 0x105

#define FALSE 0
#define TRUE 1

typedef unsigned char BYTE;
typedef unsigned short BOOL;

struct TVESAInfo
{
char VESA_signature[4]; /* 'VESA' 4 byte signature */
short VESA_version; /* VBE version number */
char *OEM_string_ptr; /* Pointer to OEM string */
unsigned int capabilities; /* Capabilities of video card */
unsigned short *video_mode_ptr; /* Pointer to supported modes */
short total_memory; /* Number of 64kb memory blocks */

/* VBE 2.0 extension information */

short OEM_software_rev; /* OEM Software revision number */
char *OEM_vendor_name_ptr; /* Pointer to Vendor Name string */
char *OEM_product_name_ptr; /* Pointer to Product Name string */
char *OEM_product_rev_ptr; /* Pointer to Product Revision str */
char reserved[222]; /* Pad to 256 byte block size */
char OEM_data[256]; /* Scratch pad for OEM data */
};

struct TVESAModeInfo
{
short mode_attributes; /* Mode attributes */
char wina_attributes; /* Window A attributes */
char winb_attributes; /* Window B attributes */
short win_granularity; /* Window granularity in k */
short win_size; /* Window size in k */
unsigned short wina_segment; /* Window A segment */
unsigned short winb_segment; /* Window B segment */
void *win_func_ptr; /* Pointer to window function */
short bytes_per_scan_line; /* Bytes per scanline */
short x_resolution; /* Horizontal resolution */
short y_resolution; /* Vertical resolution */
char x_char_size; /* Character cell width */
char y_char_size; /* Character cell height */
char number_of_planes; /* Number of memory planes */
char bit_per_pixel; /* Bits per pixel */
char number_of_banks; /* Number of CGA style banks */
char memory_model; /* Memory model type */
char bank_size; /* Size of CGA style banks */
char number_of_image_pages; /* Number of images pages */
char res1; /* Reserved */
char red_mask_size; /* Size of direct color red mask */
char red_field_position; /* Bit posn of lsb of red mask */
char green_mask_size; /* Size of direct color green mask */
char green_field_position; /* Bit posn of lsb of green mask */
char blue_mask_size; /* Size of direct color blue mask */
char blue_field_position; /* Bit posn of lsb of blue mask */
char rsvd_mask_size; /* Size of direct color res mask */
char rsvd_field_position; /* Bit posn of lsb of res mask */
char direct_color_mode_info; /* Direct color mode attributes */

/* VBE 2.0 extensions information */

unsigned int phys_base_ptr; /* Physical address for linear buf */
unsigned int off_screen_mem_offset; /* Pointer to start of offscreen mem*/
short off_screen_mem_size; /* Amount of offscreen mem in 1K's */
char res2[206]; /* Pad to 256 byte block size */
};

TVESAInfo VESAInfo;
TVESAModeInfo VESAModeInfo;

BYTE *ScreenPtr = (BYTE *)MK_FP( 0xA000, 0x0000 );

int MAXX = 0;
int MAXY = 0;
int BANK_SHIFT = 0;

BOOL CheckVESABIOS( )
{
struct REGPACK reg;
memset( &reg, 0, sizeof( reg ));

reg.r_ax = 0x4F00;
reg.r_es = FP_SEG( &VESAInfo );
reg.r_di = FP_OFF( &VESAInfo );
intr( 0x10, &reg );

if(( reg.r_ax == 0x4F ) && !memicmp( VESAInfo.VESA_signature, "VESA", 4 ))
{
return TRUE;
}

return FALSE;
}

BOOL CheckVESAMode( unsigned short mode )
{
struct REGPACK reg;
memset( &reg, 0, sizeof( reg ));

reg.r_ax = 0x4F01;
reg.r_cx = mode;
reg.r_es = FP_SEG( &VESAModeInfo );
reg.r_di = FP_OFF( &VESAModeInfo );
intr( 0x10, &reg );

if( reg.r_ax == 0x4F )
{
MAXX = VESAModeInfo.x_resolution;
MAXY = VESAModeInfo.y_resolution;
BANK_SHIFT = (VESAModeInfo.win_granularity == 64) ? 16:12;

return TRUE;
}

return FALSE;
}

void SetVESAMode( unsigned short mode )
{
union REGS regs;

if( mode < 0x100 )
{
regs.x.ax = mode;
} else
{
regs.x.ax = 0x4F02;
regs.x.bx = mode;
}

int86( 0x10, &regs, &regs );
}


void SelectBank( unsigned short id )
{
union REGS regs;

regs.x.ax = 0x4F05;
regs.x.bx = 0;
regs.x.dx = id;
int86(0x10, &regs, &regs);

regs.x.ax = 0x4F05;
regs.x.bx = 1;
regs.x.dx = id;
int86(0x10, &regs, &regs);
}

void CloseVESAMode( )
{
SetVESAMode( _M_CO80 );
}

void PutPixel( long x, long y, BYTE color )
{
unsigned long pos = (y * MAXX) + x;
unsigned short bank = pos >> BANK_SHIFT;
pos -= (bank << BANK_SHIFT);

SelectBank( bank );
*(ScreenPtr + (unsigned short)pos) = color;
}

int main( )
{
if( !CheckVESABIOS( ))
{
printf( "Not found VESA BIOS.\n" );
return 0;
}

if( !CheckVESAMode( _M_VESA_640x480x256 ))
{
printf( "This mode not support.\n" );
return 0;
}

SetVESAMode( _M_VESA_640x480x256 );

PutPixel( 0, 0, 15 );
PutPixel( MAXX-1, 0, 15 );
PutPixel( MAXX-1, MAXY-1, 15 );
PutPixel( 0, MAXY-1, 15 );

getch( );

CloseVESAMode( );
return 0;
}