{-----------------------------------------------------------------------}
{VESAPCX						GL:01/15/90	}
{-----------------------------------------------------------------------}
{Program for decoding a 4-plane or 8-bit PCX file using the facilities	}
{defined by the VESA Super VGA BIOS Extensions to provide for using	}
{the extended resolutions of a Super VGA card in a hardware independent	}
{manner.  Zsoft's PCX file format was chosen because of its relative	}
{simplicity, so that this demonstration program would not become overly	}
{cluttered with image decoding information.  PCX is the native file	}
{format of Zsoft Paintbrush Plus and other paint programs.		}
{-----------------------------------------------------------------------}
{The following program is written to loosely conform to the VESA 	}
{Super VGA BIOS Extension document VS891001.  The program is intended	}
{as a demonstration and is not intended to be an example of a 		}
{high-performance implementations of the VESA standard.			}
{						Gary Lorensen		}
{						Everex Systems, Inc.	}
{						48571 Milmont Dr. B3	}
{						Fremont, CA   94538	}
{-----------------------------------------------------------------------}

uses
    dos;

{-----------------------------------------------------------------------}

const
    rSequAddr = $3C4;

{-----------------------------------------------------------------------}

type
    ColorType = record
        r,g,b : byte;
    end;

    PCXHeaderType = record
        manu : byte;	{10d=0Ah= ZSoft PCX}
	vers : byte;	{0=v2.5,2=v2.8 w/pal,3=v2.8 wo/pal,5=v3.0}
	code : byte;	{1=PCX run length}
	bpp  : byte;	{bits/pixel/plane}
	xmin : word;	{image dimensions}
	ymin : word;
	xmax : word;
	ymax : word;
	hres : word;	{Source device horz res}
	vres : word;	{Source device vert res}
	pal  : array [$00..$0F] of ColorType;
	res0 : byte;	{reserved}
	npln : byte;	{Number of color planes}
	bpl  : word;	{Bytes/scan/plane (usually even)}
	pinf : word;	{Palette info (1=color/BW, 2=gray, 9=????)}
	dpi  : word;	{Scanner dpi or VRes}
	res1 : array [$00..$37] of byte;	{reserved}
    end;

    ByteBufferPtrType = ^ByteBufferType;
    ByteBufferType = array [$00..$00] of byte;

{-----------------------------------------------------------------------}

    s80 = string[80];
    s8  = string[8];

    DacType = array [$00..$FF] of ColorType;

{-----------------------------------------------------------------------}

    CharString = array [$00..$03] of char;

    ModeListType = array [$00..$00] of word;

    PageFuncPtrType = pointer;

    VgaInfoBlockType = record
        VESASignature	: CharString;
	VESAVersion     : word;
	OEMStringPtr	: ^CharString;
	Capabilities	: array [$00..$03] of byte;
	VideoModePtr    : ^ModeListType;
	reserved	: array [$00..$ED] of byte;	{Pad to 256}
    end;

    ModeInfoBlockType = record
	 				{mandatory information}
	ModeAttributes	: word;
	WinAAttributes	: byte;
	WinBAttributes	: byte;
	WinGranularity	: word;
	WinSize		: word;
	WinASegment	: word;
	WinBSegment	: word;
	WinFuncPtr	: PageFuncPtrType;
	BytesPerScanLine : word;

					{optional information}
	XResolution	: word;
	YResolution	: word;
	XCharSize	: byte;
	YCharSize	: byte;
	NumberOfPlanes	: byte;
	BitsPerPixel	: byte;
	NumberOfBanks	: byte;
	MemoryModel	: byte;
	BankSize	: byte;
	reserved	: array [$00..$E2] of byte;	{Pad to 256}
    end;

{-----------------------------------------------------------------------}
{-----------------------------------------------------------------------}

var
    reg : Registers;
    VesaVgaInfo : VgaInfoBlockType;
    VesaModeInfo : ModeInfoBlockType;
    prevVesaMode : word;
    VesaMode : word;
    PcxHeader    : PCXHeaderType;
    pcxfile : file;
    pcxfname : s80;
    i,j : word;
    ch : char;

{-----------------------------------------------------------------------}
{-----------------------------------------------------------------------}

function decval(ch : char) : byte;

begin
    decval := 0;
    if ((ch>='0') and (ch<='9')) then
        decval := ord(ch)-ord('0');
    if ((ch>='A') and (ch<='F')) then
        decval := ord(ch)-ord('A')+$0A;
    if ((ch>='a') and (ch<='f')) then
        decval := ord(ch)-ord('a')+$0A;
end;

function hex2dec(s : s80) : word;

var
    i     : byte;
    tmp   : word;
    place : word;

begin
    i := ord(s[0]);
    place := 1;
    tmp := 0;
    while (i>0) do begin
        tmp := tmp+place*decval(s[i]);
	i:=i-1;
	place := place*$10;
    end;
    hex2dec := tmp;
{    writeln('hex2dec(',s,') = ',tmp);}
end;

{-----------------------------------------------------------------------}

function hexval(x : byte) : char;

begin
    hexval := '0';
    if ((x>=0) and (x<=9)) then
        hexval := chr(x+ord('0'));
    if ((x>=10) and (x<=15)) then
        hexval := chr(x-10+ord('A'));
end;

function dec2hex(x : word) : s8;

var
    tmp   : s8;
    place : word;

begin
{    tmp   := '0';}
    tmp := ' ';
    if (x>=$100) then
        place := $1000
    else
        place := $10;

    repeat
        tmp := tmp+hexval(x div place);
	x := x mod place;
	place := place div $10;
    until (place=$0000);

    dec2hex := tmp+'h';
end;


function hex(x : word) : s8;

var
    tmp   : s8;
    place : word;

begin
    tmp := '0';
    if (x>=$100) then
        place := $1000
    else
        place := $10;

    repeat
        tmp := tmp+hexval(x div place);
	x := x mod place;
	place := place div $10;
    until (place=$0000);

    hex := tmp+'h';
end;

function addrhex(x : word) : s8;

var
    tmp   : s8;
    place : word;

begin
    tmp := '';
    place := $1000;

    repeat
        tmp := tmp+hexval(x div place);
	x := x mod place;
	place := place div $10;
    until (place=$0000);

    addrhex := tmp;
end;

{-----------------------------------------------------------------------}

function Min(m,n : longint) : longint;

begin
    if (m<n) then
        Min := m
    else
        Min := n;
end;

function Max(m,n : longint) : longint;

begin
    if (m>n) then
        Max := m
    else
        Max := n;
end;

{-----------------------------------------------------------------------}

procedure SetVesaBank(win  : byte;
                      bank : byte);

var
    reg : Registers;

begin
    reg.AX := $4F05;
    reg.BH := $00;
    reg.BL := win;
    reg.DX := bank;
    intr($10,reg);
end;

{-----------------------------------------------------------------------}

procedure GetVesaBank(win  : byte;
                      var bank : byte);

var
    reg : Registers;

begin
    reg.AX := $4F05;
    reg.BH := $01;
    reg.BL := win;
    intr($10,reg);
    bank := reg.DX;
end;

{-----------------------------------------------------------------------}

procedure FindVesaMode(xsize,ysize : word;
                      memmodel : byte;
		      bitpixel : byte;
		      var bestVesaMode : word;
		      var bestVesaModeInfo : ModeInfoBlockType);
var
    tmpVesaModeInfo : ModeInfoBlockType;
    i : byte;
    error : boolean;

begin
    i := $00;
    bestVesaMode := $FFFF;
    error := false;
    while ((VesaVgaInfo.VideoModePtr^[i]<>$FFFF) and not(error)) do begin

        reg.AX := $4F01;
	reg.CX := VesaVgaInfo.VideoModePtr^[i];
        reg.ES := Seg(tmpVesaModeInfo);
        reg.DI := Ofs(tmpVesaModeInfo);
        intr($10,reg);

	error := (reg.AX<>$004F);

	if (tmpVesaModeInfo.ModeAttributes and $02=$00) then begin
	    case VesaVgaInfo.VideoModePtr^[i] of
	        $0100,$0101,$0103,$0105,$0107 : begin
		    tmpVesaModeInfo.XCharSize   := 0;
		    tmpVesaModeInfo.YCharSize   := 0;
		    tmpVesaModeInfo.NumberOfPlanes:= 1;
		    tmpVesaModeInfo.BitsPerPixel:= 8;
		    tmpVesaModeInfo.NumberOfBanks:= 1;
		    tmpVesaModeInfo.MemoryModel := $04;
		    tmpVesaModeInfo.BankSize    := 0;
		end;
	        $0102,$0104,$0106 : begin
		    tmpVesaModeInfo.XCharSize   := 0;
		    tmpVesaModeInfo.YCharSize   := 0;
		    tmpVesaModeInfo.NumberOfPlanes:= 4;
		    tmpVesaModeInfo.BitsPerPixel:= 4;
		    tmpVesaModeInfo.NumberOfBanks:= 1;
		    tmpVesaModeInfo.MemoryModel := $03;
		    tmpVesaModeInfo.BankSize    := 0;
		end;
	    else
		tmpVesaModeInfo.XCharSize   := 0;
		tmpVesaModeInfo.YCharSize   := 0;
		tmpVesaModeInfo.NumberOfPlanes:= 0;
		tmpVesaModeInfo.BitsPerPixel:= 0;
		tmpVesaModeInfo.NumberOfBanks:= 0;
		tmpVesaModeInfo.MemoryModel := $FF;
		tmpVesaModeInfo.BankSize    := 0;
	    end;

	    case VesaVgaInfo.VideoModePtr^[i] of
		$0100 : begin
		    tmpVesaModeInfo.XResolution := 640;
		    tmpVesaModeInfo.YResolution := 400;
		end;
		$0101 : begin
		    tmpVesaModeInfo.XResolution := 640;
		    tmpVesaModeInfo.YResolution := 480;
		end;
		$0102,$0103 : begin
		    tmpVesaModeInfo.XResolution := 800;
		    tmpVesaModeInfo.YResolution := 600;
		end;
		$0104,$0105 : begin
		    tmpVesaModeInfo.XResolution := 1024;
		    tmpVesaModeInfo.YResolution := 768;
		end;
		$0106,$0107 : begin
		    tmpVesaModeInfo.XResolution := 1280;
		    tmpVesaModeInfo.YResolution := 1024;
		end;
	    else
		tmpVesaModeInfo.XResolution := 0;
		tmpVesaModeInfo.YResolution := 0;
	    end;
	end;

	if ((tmpVesaModeInfo.XResolution>=xsize)
	    and (tmpVesaModeInfo.YResolution>=ysize)
	    and (tmpVesaModeInfo.MemoryModel=memmodel)
	    and (tmpVesaModeInfo.BitsPerPixel=bitpixel)
	    and (tmpVesaModeInfo.ModeAttributes and $01=$01)
	    ) then begin

	    if (bestVesaMode=$FFFF) then begin
	        bestVesaMode := VesaVgaInfo.VideoModePtr^[i];
	        bestVesaModeInfo := tmpVesaModeInfo;
	    end else if ((tmpVesaModeInfo.XResolution>=xsize) 
		and (tmpVesaModeInfo.XResolution<=bestVesaModeInfo.XResolution))
		and ((tmpVesaModeInfo.YResolution>=ysize) 
		and (tmpVesaModeInfo.YResolution<=bestVesaModeInfo.YResolution))
		then begin

		bestVesaModeInfo := tmpVesaModeInfo;
		bestVesaMode := VesaVgaInfo.VideoModePtr^[i];

	    end;
	end;

	i:=i+1;
    end;
end;

{-----------------------------------------------------------------------}

procedure SetVesaMode(VesaMode : word);

var
    reg : Registers;

begin
    reg.AX := $4F02;
    reg.BX := VesaMode;
    intr($10,reg);
end;

{-----------------------------------------------------------------------}

procedure GetVesaMode(var VesaMode : word);

var
    reg : Registers;

begin
    reg.AX := $4F03;
    intr($10,reg);
    VesaMode := reg.BX;
end;

{-----------------------------------------------------------------------}

procedure DecodeVesaPcx4(var pcxfile : file;
                         pcxheader : PCXHeaderType;
			 VesaModeInfo : ModeInfoBlockType);
var
    pcxbuffptr : ByteBufferPtrType;
    decodebuffptr : ByteBufferPtrType;
    pcxfileind : longint;
    pcxind : word;
    decodeind : word;
    scan : word;
    i,tmp : byte;
    mapmask : byte;
    scrnptr : ByteBufferPtrType;
    writebank : byte;
    readbank : byte;
    scrnofs : word;
    vesabank : word;
    scanrun : word;
    dac : DacType;
    pal : array [$00..$10] of byte;

begin
    GetMem(pcxbuffptr,pcxheader.bpl*2*4);
    GetMem(decodebuffptr,pcxheader.bpl*4);

    for i := $00 to $0F do
        pal[i] := i;
    pal[$10] := $00;

    reg.AX := $1002;
    reg.ES := Seg(pal);
    reg.DX := Ofs(pal);
    intr($10,reg);

    Seek(pcxfile,FileSize(pcxfile)-(3*256)-1);
    i := 0;
    BlockRead(pcxfile,i,1);
    if (i=$0C) then begin
        BlockRead(pcxfile,dac,3*256);
	for i := $00 to $FF do begin
	    dac[i].r := (dac[i].r {+ $03}) shr 2;
	    dac[i].g := (dac[i].g {+ $03}) shr 2;
	    dac[i].b := (dac[i].b {+ $03}) shr 2;
	end;
	reg.CX := $0100;
    end else begin
        for i := $00 to $0F do begin
	    dac[i].r := (pcxheader.pal[i].r{+3}) shr 2;
	    dac[i].g := (pcxheader.pal[i].g{+3}) shr 2;
	    dac[i].b := (pcxheader.pal[i].b{+3}) shr 2;
        end;
	reg.CX := $0010;
    end;
    reg.AX := $1012;
    reg.BX := $0000;
    reg.ES := Seg(dac);
    reg.DX := Ofs(dac);
    intr($10,reg);

    if (VesaModeInfo.WinAAttributes and $05 = $05) then begin
        writebank := $00;
        scrnptr := Ptr(VesaModeInfo.WinASegment,$0000);
    end else if (VesaModeInfo.WinBAttributes and $05 = $05) then begin
        writebank := $01;
        scrnptr := Ptr(VesaModeInfo.WinBSegment,$0000);
    end else begin
        writebank := $00;
        scrnptr := Ptr($A000,$0000);
    end;

    if (VesaModeInfo.WinAAttributes and $03 = $03) then begin
        readbank := $00;
    end else if (VesaModeInfo.WinBAttributes and $03 = $03) then begin
        readbank := $01;
    end else begin
        readbank := $00;
    end;

    Seek(pcxfile,SizeOf(pcxheader));
    pcxfileind := FilePos(pcxfile);

    for scan := $0000 to (pcxheader.ymax-pcxheader.ymin) do begin

        pcxind := 0;
        decodeind := 0;

	BlockRead(pcxfile,pcxbuffptr^,Min(pcxheader.bpl*2*4,
	    FileSize(pcxfile)-pcxfileind));

	while (decodeind<pcxheader.bpl*4) do begin
	    if (pcxbuffptr^[pcxind] and $C0 = $C0) then begin
	        for i := $00 to (pcxbuffptr^[pcxind] and $3F)-1 do
		    decodebuffptr^[decodeind+i] :=  pcxbuffptr^[pcxind+1];
		decodeind := decodeind + (pcxbuffptr^[pcxind] and $3F);
		pcxind := pcxind+2;
	    end else begin
		decodebuffptr^[decodeind] :=  pcxbuffptr^[pcxind];
		decodeind := decodeind+1;
		pcxind := pcxind+1;
	    end;
	end;

        pcxfileind := pcxfileind+pcxind;
	Seek(pcxfile,pcxfileind);

	for mapmask := $00 to $03 do begin

	    Port[rSequAddr  ] := $02;
	    Port[rSequAddr+1] := ($01 shl mapmask);

	    vesabank := longint(longint(scan)*longint(VesaModeInfo.BytesPerScanLine))
	        div longint(longint(VesaModeInfo.WinSize)*longint(1024));
	    SetVesaBank(writebank,vesabank);
	    SetVesaBank(readbank,vesabank);
	    scrnofs := longint(longint(scan)*longint(VesaModeInfo.BytesPerScanLine))
	        mod longint(longint(VesaModeInfo.WinSize)*longint(1024));

	    scanrun := Min((pcxheader.xmax-pcxheader.xmin+1) div 8,VesaModeInfo.XResolution div 8);

	    if (longint(longint(scrnofs)+longint(scanrun))
	        >longint(longint(VesaModeInfo.WinSize)*longint(1024))) then begin

	        for i := $00 to (longint(longint(VesaModeInfo.WinSize)*longint(1024))-longint(scrnofs))-1 do begin
		    tmp := scrnptr^[scrnofs + i];
	            scrnptr^[scrnofs + i] := decodebuffptr^[mapmask*pcxheader.bpl+i];
		end;
	        j := i+1;
	        vesabank := vesabank+1;
	        SetVesaBank(writebank,vesabank);
	        SetVesaBank(readbank,vesabank);
	        scanrun := (longint(longint(scrnofs)+longint(scanrun)-1)-longint(longint(VesaModeInfo.WinSize)*longint(1024)));
	        scrnofs := $0000;
	        for i := $00 to scanrun do begin
		    tmp := scrnptr^[scrnofs + i];
	            scrnptr^[scrnofs + i] := decodebuffptr^[mapmask*pcxheader.bpl+j+i];
		end;
	    end else begin
	        for i := $00 to scanrun-1 do begin
		    tmp := scrnptr^[scrnofs + i];
	            scrnptr^[scrnofs + i] := decodebuffptr^[mapmask*pcxheader.bpl+i];
		end;
	    end;

	end;

    end;
    
end;

{-----------------------------------------------------------------------}

procedure DecodeVesaPcx8(var pcxfile : file;
                         pcxheader : PCXHeaderType;
			 VesaModeInfo : ModeInfoBlockType);
var
    pcxbuffptr : ByteBufferPtrType;
    decodebuffptr : ByteBufferPtrType;
    pcxfileind : longint;
    pcxind : word;
    decodeind : word;
    scan : word;
    scanrun : word;
    i,j : word;
    vesabank : word;
    scrnptr : ByteBufferPtrType;
    writebank : byte;
    readbank : byte;
    scrnofs : word;
    dac : DacType;
    reg : Registers;

begin
    GetMem(pcxbuffptr,pcxheader.bpl*2);
    GetMem(decodebuffptr,pcxheader.bpl);

    Seek(pcxfile,FileSize(pcxfile)-(3*256)-1);
    i := 0;
    BlockRead(pcxfile,i,1);
    if (i=$0C) then begin
        BlockRead(pcxfile,dac,3*256);
	for i := $00 to $FF do begin
	    dac[i].r := (dac[i].r {+ $03}) shr 2;
	    dac[i].g := (dac[i].g {+ $03}) shr 2;
	    dac[i].b := (dac[i].b {+ $03}) shr 2;
	end;
	reg.CX := $0100;
    end else begin
        for i := $00 to $0F do begin
	    dac[i].r := (pcxheader.pal[i].r{+3}) shr 2;
	    dac[i].g := (pcxheader.pal[i].g{+3}) shr 2;
	    dac[i].b := (pcxheader.pal[i].b{+3}) shr 2;
        end;
	reg.CX := $0010;
    end;
    reg.AX := $1012;
    reg.BX := $0000;
    reg.ES := Seg(dac);
    reg.DX := Ofs(dac);
    intr($10,reg);

    if (VesaModeInfo.WinAAttributes and $05 = $05) then begin
        writebank := $00;
        scrnptr := Ptr(VesaModeInfo.WinASegment,$0000);
    end else if (VesaModeInfo.WinBAttributes and $05 = $05) then begin
        writebank := $01;
        scrnptr := Ptr(VesaModeInfo.WinBSegment,$0000);
    end else begin
        writebank := $00;
        scrnptr := Ptr($A000,$0000);
    end;

    if (VesaModeInfo.WinAAttributes and $03 = $03) then begin
        readbank := $00;
    end else if (VesaModeInfo.WinBAttributes and $03 = $03) then begin
        readbank := $01;
    end else begin
        readbank := $00;
    end;

    Seek(pcxfile,SizeOf(pcxheader));
    pcxfileind := FilePos(pcxfile);

    for scan := $0000 to (pcxheader.ymax-pcxheader.ymin) do begin

        pcxind := 0;
        decodeind := 0;

	BlockRead(pcxfile,pcxbuffptr^,Min(pcxheader.bpl*2,FileSize(pcxfile)-pcxfileind));

	while (decodeind<pcxheader.bpl) do begin
	    if (pcxbuffptr^[pcxind] and $C0 = $C0) then begin
	        for i := $00 to (pcxbuffptr^[pcxind] and $3F)-1 do
		    decodebuffptr^[decodeind+i] :=  pcxbuffptr^[pcxind+1];
		decodeind := decodeind + (pcxbuffptr^[pcxind] and $3F);
		pcxind := pcxind+2;
	    end else begin
		decodebuffptr^[decodeind] :=  pcxbuffptr^[pcxind];
		decodeind := decodeind+1;
		pcxind := pcxind+1;
	    end;
	end;

        pcxfileind := pcxfileind+pcxind;
	Seek(pcxfile,pcxfileind);

	if (VesaModeInfo.MemoryModel=$04) then begin

	    vesabank := longint(longint(scan)*longint(VesaModeInfo.BytesPerScanLine))
	        div longint(longint(VesaModeInfo.WinSize)*longint(1024));
	    SetVesaBank(writebank,vesabank);
	    SetVesaBank(readbank,vesabank);
	    scrnofs := longint(longint(scan)*longint(VesaModeInfo.BytesPerScanLine))
	        mod longint(longint(VesaModeInfo.WinSize)*longint(1024));

	    scanrun := Min(pcxheader.xmax-pcxheader.xmin,VesaModeInfo.XResolution);

	    if (longint(longint(scrnofs)+longint(scanrun))
	        >longint(longint(VesaModeInfo.WinSize)*longint(1024))) then begin
	        for i := $00 to (longint(longint(VesaModeInfo.WinSize)*longint(1024))-longint(scrnofs))-1 do
	            scrnptr^[scrnofs + i] := decodebuffptr^[i];
	        j := i+1;
	        vesabank := vesabank+1;
	        SetVesaBank(writebank,vesabank);
	        SetVesaBank(readbank,vesabank);
	        scanrun := (longint(longint(scrnofs)+longint(scanrun)-1)
		    -longint(longint(VesaModeInfo.WinSize)*longint(1024)));
	        scrnofs := $0000;
	        for i := $00 to scanrun do
	            scrnptr^[scrnofs + i] := decodebuffptr^[j+i];
	    end else begin
	        for i := $00 to scanrun-1 do
	            scrnptr^[scrnofs + i] := decodebuffptr^[i];
	    end;

        end else begin			{Sequential 256-color mode}

	    scrnofs := longint(longint(scan)*longint(VesaModeInfo.BytesPerScanLine));
	    scanrun := Min(pcxheader.xmax-pcxheader.xmin,VesaModeInfo.XResolution);

	    for i := $00 to scanrun-1 do begin
	        Port[rSequAddr  ] := $02;
		Port[rSequAddr+1] := ($01 shl (i mod 4));

		j := scrnptr^[scrnofs + (i div 4)];
	        scrnptr^[scrnofs + (i div 4)] := decodebuffptr^[i];
	    end;

	end;

    end;
    
end;

{-----------------------------------------------------------------------}
{-----------------------------------------------------------------------}

begin
    writeln;
    writeln('VESA PCX file decoder');
    writeln('A demonstration of hardware independent VESA programming.');
    writeln('1990 Everex Systems, Inc.');
    writeln;

    reg.AX := $4F00;
    reg.ES := Seg(VesaVgaInfo);
    reg.DI := Ofs(VesaVgaInfo);
    intr($10,reg);

    if (reg.AL<>$4F) then begin
        writeln('ERROR: VESA Function 00h: Return Super VGA Information not supported.');
	halt(1);
    end;

    if (reg.AH<>$00) then begin
        writeln('ERROR: VESA Function 00h: Return Super VGA Information failed.');
	halt(2);
    end;

    if (ParamCount=0) then begin
        write('Input name of PCX file to display >> ');
	readln(pcxfname);
    end else
        pcxfname := ParamStr(1);

    writeln('---------------------------------------');
    writeln;

    writeln('VESA VGA Information:');

    write('    VESA Signature: ');
    for i := $00 to $03 do
        write(VesaVgaInfo.VesaSignature[i]);
    writeln;

    write('    VESA Version  : v');
    write(VesaVgaInfo.VesaVersion div $100);
    write('.');
    write(VesaVgaInfo.VesaVersion mod $100);
    writeln;

    write('    OEM String    : ');
    i := $00;
    while (VesaVgaInfo.OEMStringPtr^[i]<>#00) do begin
        write(VesaVgaInfo.OEMStringPtr^[i]);
	i:=i+1;
    end;
    writeln;

    write('    Capabilities  : ');
    for j := $00 to $03 do
        for i := $00 to $07 do
	    if ((VesaVgaInfo.Capabilities[j] and ($80 shr i))=$00) then
	        write('0')
	    else
	        write('1');
    writeln;

    write  ('    Modes         : ');
    i := $00;
    while (VesaVgaInfo.VideoModePtr^[i]<>$FFFF) do begin
        if ((i mod 8)=0) then begin
	    writeln;
	    write('        ');
	end;
        write(addrhex(VesaVgaInfo.VideoModePtr^[i]),'h ');
	i:=i+1;
    end;
    writeln;

    assign(pcxfile,pcxfname);
    reset(pcxfile,1);

    if (FileSize(pcxfile)<SizeOf(PcxHeader)) then begin
        writeln('ERROR: Invalid PCX image file specified.');
	halt(1);
    end;

    BlockRead(pcxfile,PcxHeader,sizeof(PcxHeader));

    if (PcxHeader.manu<>$0A) then begin	{Check if PCX file}
	writeln('ERROR: Invalid PCX file format.');
	halt(2);
    end;

    if ((PcxHeader.vers<>5)		{Check for right version}
	or (PcxHeader.code<>1))		{Check for encoding technique}
	then begin
	writeln('ERROR: Unsupported PCX file format.');
	halt(2);
    end;

    writeln;
    with PcxHeader do begin
        writeln('PCX image size: ',(xmax-xmin+1),'x',(ymax-ymin+1),'x',bpp*npln);

        if ((PcxHeader.bpp=1) and (PcxHeader.npln=4)) then begin
            FindVesaMode((xmax-xmin+1),(ymax-ymin+1),$03,4,VesaMode,VesaModeInfo);
	    if (VesaMode=$FFFF) then begin
	        writeln('ERROR: Can not find an adequate VESA 4-bit mode.');
	        halt(2);
	    end else begin
	        write('VESA mode res : ',VesaModeInfo.XResolution,'x');
		writeln(VesaModeInfo.YResolution,'x',VesaModeInfo.BitsPerPixel);
		readln;
	        GetVesaMode(prevVesaMode);
	        SetVesaMode(VesaMode);
		DecodeVesaPcx4(pcxfile,pcxheader,VesaModeInfo);
		readln;
	        SetVesaMode(prevVesaMode);
	    end;
        end else if ((PcxHeader.bpp=8) and (PcxHeader.npln=1)) then begin
            FindVesaMode((xmax-xmin+1),(ymax-ymin+1),$04,8,VesaMode,VesaModeInfo);
	    if (VesaMode=$FFFF) then
                FindVesaMode((xmax-xmin+1),(ymax-ymin+1),$05,8,VesaMode,VesaModeInfo);
	    if (VesaMode=$FFFF) then begin
	        writeln('ERROR: Can not find an adequate VESA 8-bit mode.');
	        halt(2);
	    end else begin
	        write('VESA mode res : ',VesaModeInfo.XResolution,'x');
		writeln(VesaModeInfo.YResolution,'x',VesaModeInfo.BitsPerPixel);
		readln;
	        GetVesaMode(prevVesaMode);
	        SetVesaMode(VesaMode);
		DecodeVesaPcx8(pcxfile,pcxheader,VesaModeInfo);
		readln;
	        SetVesaMode(prevVesaMode);
	    end;
        end else begin
            writeln('ERROR: VESA PCX file decoder only supports VGA 4 and 8 bit formats.');
	    halt(1);
        end;
    end;


end.

{-----------------------------------------------------------------------}
{-----------------------------------------------------------------------}

