fpc/ide/vesa.pas
2014-05-17 16:30:52 +00:00

766 lines
20 KiB
ObjectPascal

{
This file is part of the PinGUI - Platform Independent GUI Project
Copyright (c) 1999 by Berczi Gabor
VESA support routines
See the file COPYING.GUI, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit VESA;
{$ifdef DEBUG}
{$define TESTGRAPHIC}
{$endif DEBUG}
interface
uses
Dos,
Objects,Strings,WUtils;
const
{ Video Mode Attributes mask constants }
vesa_vma_CanBeSetInCurrentConfig = $0001;
vesa_vma_OptionalBlockPresent = $0002;
vesa_vma_BIOSSupport = $0004;
vesa_vma_ColorMode = $0008; { else mono }
vesa_vma_GraphicsMode = $0010; { else text }
{ -- VBE 2.0 --- }
vesa_vma_VGACompatibleMode = $0020;
vesa_vma_VGACompWindowedAvail = $0040;
vesa_vma_LinearFrameBufferAvail = $0080;
{ Windows Attributes mask constants }
vesa_wa_Present = $0001;
vesa_wa_Readable = $0002;
vesa_wa_Writeable = $0004;
{ Memory Model value constants }
vesa_mm_Text = $0000;
vesa_mm_CGAGraphics = $0001;
vesa_mm_HerculesGraphics = $0002;
vesa_mm_4planePlanar = $0003;
vesa_mm_PackedPixel = $0004;
vesa_mm_NonChain4_256color = $0005;
vesa_mm_DirectColor = $0006;
vesa_mm_YUV = $0007;
{ Memory Window value constants }
vesa_mw_WindowA = $0000;
vesa_mw_WindowB = $0001;
type
tregisters=registers;
PtrRec16 = record
Ofs,Seg: word;
end;
TVESAInfoBlock = packed record
Signature : longint; { 'VESA' }
Version : word;
OEMString : PString;
Capabilities : longint;
VideoModeList: PWordArray;
TotalMemory : word; { in 64KB blocks }
Fill : array[1..236] of byte;
VBE2Fill : array[1..256] of byte;
end;
TVESAModeInfoBlock = packed record
Attributes : word;
WinAAttrs : byte;
WinBAttrs : byte;
Granularity : word;
Size : word;
ASegment : word;
BSegment : word;
FuncPtr : pointer;
BytesPerLine : word;
{ optional }
XResolution : word;
YResolution : word;
XCharSize : byte;
YCharSize : byte;
NumberOfPlanes : byte;
BitsPerPixel : byte;
NumberOfBanks : byte;
MemoryModel : byte;
BankSize : byte;
NumberOfImagePages: byte;
Reserved : byte;
{ direct color fields }
RedMaskSize : byte;
RedFieldPosition: byte;
GreenMaskSize : byte;
GreenFieldPosition: byte;
BlueMaskSize : byte;
BlueFieldPosition: byte;
ReservedMaskSize: byte;
ReservedPosition: byte;
DirectColorModeInfo: byte;
{ --- VBE 2.0 optional --- }
LinearFrameAddr : longint;
OffScreenAddr : longint;
OffScreenSize : word;
Reserved2 : array[1..216-(4+4+2)] of byte;
end;
TVESAModeList = record
Count : word;
Modes : array[1..256] of word;
end;
function VESAInit: boolean;
function VESAGetInfo(var B: TVESAInfoBlock): boolean;
function VESAGetModeInfo(Mode: word; var B: TVESAModeInfoBlock): boolean;
function VESAGetModeList(var B: TVESAModeList): boolean;
function VESASearchMode(XRes,YRes,BPX: word; LFB: boolean; var Mode: word; var ModeInfo: TVESAModeInfoBlock): boolean;
function VESAGetOemString: string;
function VESASetMode(Mode: word): boolean;
function VESAGetMode(var Mode: word): boolean;
function VESASelectMemoryWindow(Window: byte; Position: word): boolean;
function VESAReturnMemoryWindow(Window: byte; var Position: word): boolean;
function RegisterVesaVideoMode(Mode : word) : boolean;
Procedure FreeVesaModes;
const
DisableVESA : boolean = false;
implementation
uses
video, mouse,
{$ifdef TESTGRAPHIC}
graph,
{$endif TESTGRAPHIC}
pmode;
type
PVesaVideoMode = ^TVesaVideoMode;
TVesaVideoMode = record
{Col,Row : word;
Color : boolean;}
V : TVideoMode;
Mode : word;
IsGraphic : boolean;
{ zero based vesa specific driver count }
VideoIndex : word;
Next : PVesaVideoMode;
end;
CursorBitMap = Record
width,height,size : longint;
colors : array[0..8*8-1] of word;
end;
const
VesaVideoModeHead : PVesaVideoMode = nil;
VesaRegisteredModes : word = 0;
{$ifdef TESTGRAPHIC}
IsGraphicMode : boolean = false;
GraphDriver : integer = 0;
GraphMode : Integer = 0;
FirstCallAfterSetVesaMode : boolean = false;
LastCursorX : word = $ffff;
LastCursorY : word = $ffff;
LastCursorType : word = crHidden;
var
UnderLineImage : CursorBitMap;
BlockImage : CursorBitMap;
HalfBlockImage : CursorBitMap;
{$endif TESTGRAPHIC}
Var
SysGetVideoModeCount : function : word;
SysSetVideoMode : function (Const VideoMode : TVideoMode) : boolean;
SysGetVideoModeData : function (Index : Word; Var Data : TVideoMode) : boolean;
SysUpdateScreen : procedure(Force : Boolean);
SysDoneVideo : procedure;
SysInitVideo : procedure;
SysSetCursorPos : procedure(NewCursorX, NewCursorY: Word);
SysSetCursorType : procedure(NewCurosrType : word);
function VESAGetInfo(var B: TVESAInfoBlock): boolean;
var r: registers;
OK: boolean;
M: MemPtr;
begin
if disableVESA then
exit(false);
StrToMem('VBE2',B.Signature);
GetDosMem(M,SizeOf(B));
M.MoveDataTo(B,sizeof(B));
r.ah:=$4f; r.al:=0;
r.es:=M.DosSeg; r.di:=M.DosOfs;
realintr($10,r);
M.MoveDataFrom(sizeof(B),B);
FreeDosMem(M);
OK:=(r.ax=$004f){ and (MemToStr(B.Signature,4)='VESA')};
VESAGetInfo:=OK;
end;
function VESAGetModeList(var B: TVESAModeList): boolean;
var OK: boolean;
VI: TVESAInfoBlock;
begin
FillChar(B,SizeOf(B),0);
if disableVESA then
exit(false);
OK:=VESAGetInfo(VI);
if OK then
begin
OK:=MoveDosToPM(VI.VideoModeList,@B.Modes,sizeof(B.Modes));
if OK then
while (B.Modes[B.Count+1]<>$ffff) and (B.Count<High(B.Modes)) do
Inc(B.Count);
end;
VESAGetModeList:=OK;
end;
function VESASearchMode(XRes,YRes,BPX: word; LFB: boolean; var Mode: word; var ModeInfo: TVESAModeInfoBlock): boolean;
var B: TVESAModeList;
OK: boolean;
I: integer;
MI: TVESAModeInfoBlock;
begin
OK:=VESAGetModeList(B);
I:=1; Mode:=0;
repeat
OK:=VESAGetModeInfo(B.Modes[I],MI);
if OK and (MI.XResolution=XRes) and (MI.YResolution=YRes) and
(MI.BitsPerPixel=BPX) and
((LFB=false) or ((MI.Attributes and vesa_vma_LinearFrameBufferAvail)<>0)) then
begin Mode:=B.Modes[I]; ModeInfo:=MI; end;
Inc(I);
until (OK=false) or (I>=B.Count) or (Mode<>0);
OK:=Mode<>0;
VESASearchMode:=OK;
end;
function VESAGetOemString: string;
var OK: boolean;
VI: TVESAInfoBlock;
S: array[0..256] of char;
begin
if disableVESA then
begin
VESAGetOemString:='VESA disabled';
exit;
end;
FillChar(S,SizeOf(S),0);
OK:=VESAGetInfo(VI);
if OK then
OK:=MoveDosToPM(VI.OemString,@S,sizeof(S));
VESAGetOemString:=StrPas(@S);
end;
function VESAGetModeInfo(Mode: word; var B: TVESAModeInfoBlock): boolean;
var r : registers;
M : MemPtr;
OK: boolean;
begin
if disableVESA then
exit(false);
r.ah:=$4f; r.al:=$01; r.cx:=Mode;
GetDosMem(M,sizeof(B));
r.es:=M.DosSeg; r.di:=M.DosOfs; {r.ds:=r.es;}
realintr($10,r);
M.MoveDataFrom(sizeof(B),B);
FreeDosMem(M);
OK:=(r.ax=$004f);
VESAGetModeInfo:=OK;
end;
function RegisterVesaVideoMode(Mode : word) : boolean;
var B: TVESAModeInfoBlock;
VH : PVesaVideoMode;
DoAdd : boolean;
begin
if not VESAGetModeInfo(Mode,B) then
RegisterVesaVideoMode:=false
else
begin
VH:=VesaVideoModeHead;
DoAdd:=true;
RegisterVesaVideoMode:=false;
while assigned(VH) do
begin
if VH^.mode=mode then
DoAdd:=false;
VH:=VH^.next;
end;
if DoAdd then
begin
New(VH);
VH^.next:=VesaVideoModeHead;
VH^.mode:=mode;
VH^.IsGraphic:=(B.Attributes and vesa_vma_GraphicsMode)<>0;
VH^.v.color:=(B.Attributes and vesa_vma_ColorMode)<>0;
if VH^.IsGraphic then
begin
VH^.v.col:=B.XResolution div 8;
VH^.v.row:=B.YResolution div 8;
end
else
begin
VH^.v.col:=B.XResolution;
VH^.v.row:=B.YResolution;
end;
VH^.VideoIndex:=VesaRegisteredModes;
Inc(VesaRegisteredModes);
RegisterVesaVideoMode:=true;
VesaVideoModeHead:=VH;
end;
end;
end;
function VESASetMode(Mode: word): boolean;
var r: registers;
OK: boolean;
begin
if disableVESA then
exit(false);
r.ah:=$4f; r.al:=$02; r.bx:=Mode;
dos.intr($10,r);
OK:=(r.ax=$004f);
VESASetMode:=OK;
end;
function VESAGetMode(var Mode: word): boolean;
var r : registers;
OK: boolean;
begin
if disableVESA then
exit(false);
if disableVESA then
exit(false);
r.ah:=$4f; r.al:=$03;
dos.intr($10,r);
OK:=(r.ax=$004f);
if OK then Mode:=r.bx;
VESAGetMode:=OK;
end;
function VESASelectMemoryWindow(Window: byte; Position: word): boolean;
var r : registers;
OK : boolean;
begin
if disableVESA then
exit(false);
r.ah:=$4f; r.al:=$05; r.bh:=0; r.bl:=Window; r.dx:=Position;
dos.intr($10,r);
OK:=(r.ax=$004f);
VESASelectMemoryWindow:=OK;
end;
function VESAReturnMemoryWindow(Window: byte; var Position: word): boolean;
var r : registers;
OK : boolean;
begin
if disableVESA then
exit(false);
r.ah:=$4f; r.al:=$05; r.bh:=1; r.bl:=Window;
dos.intr($10,r);
OK:=(r.ax=$004f);
if OK then Position:=r.dx;
VESAReturnMemoryWindow:=OK;
end;
function VESAInit: boolean;
var OK: boolean;
VI: TVESAInfoBlock;
begin
if disableVESA then
OK:=false
else
OK:=VESAGetInfo(VI);
VESAInit:=OK;
end;
Function VesaGetVideoModeData (Index : Word; Var Data : TVideoMode) : boolean;
Var
PrevCount : word;
VH : PVesaVideoMode;
begin
PrevCount:=SysGetVideoModeCount();
VesaGetVideoModeData:=(Index<PrevCount);
If VesaGetVideoModeData then
begin
VesaGetVideoModeData:=SysGetVideoModeData(Index,Data);
exit;
end;
VesaGetVideoModeData:=(Index-PrevCount)<VesaRegisteredModes;
If VesaGetVideoModeData then
begin
VH:=VesaVideoModeHead;
while assigned(VH) and (VH^.VideoIndex<>Index-PrevCount) do
VH:=VH^.next;
if assigned(VH) then
Data:=VH^.v
else
VesaGetVideoModeData:=false;
end;
end;
function SetVESAMode(const VideoMode: TVideoMode): Boolean;
var
res : boolean;
VH : PVesaVideoMode;
begin
res:=false;
if disableVESA then
exit(res);
VH:=VesaVideoModeHead;
while assigned(VH) do
begin
if (VideoMode.col=VH^.v.col) and
(VideoMode.row=VH^.v.row) and
(VideoMode.color=VH^.v.color) then
begin
{$ifdef TESTGRAPHIC}
if VH^.IsGraphic then
begin
if IsGraphicMode then
CloseGraph;
GraphDriver:=Graph.Vesa;
if (VideoMode.col = 100) and (VideoMode.row = 75) then
GraphMode:=m800x600x256
else if (VideoMode.col = 80) and (VideoMode.row = 60) then
GraphMode:=m640x480x256
else if (VideoMode.col = 128) and (VideoMode.row = 96) then
GraphMode:=m1024x768x256
else
GraphMode:=Graph.Detect;
InitGraph(GraphDriver,GraphMode,'');
res:=(GraphResult=grOK);
if not res then
begin
SetVesaMode:=false;
exit;
end;
end
else
{$endif TESTGRAPHIC}
res:=VESASetMode(VH^.mode);
if res then
begin
ScreenWidth:=VideoMode.Col;
ScreenHeight:=VideoMode.Row;
ScreenColor:=VideoMode.Color;
{$ifdef TESTGRAPHIC}
IsGraphicMode:=VH^.IsGraphic;
FirstCallAfterSetVesaMode:=true;
LastCursorX:=$ffff;
LastCursorY:=$ffff;
LastCursorType:=crHidden;
if IsGraphicMode then
DoCustomMouse(false)
else
{$endif TESTGRAPHIC}
DoCustomMouse(true);
end;
end;
if res then
begin
SetVesaMode:=true;
exit;
end;
VH:=VH^.next;
end;
SetVESAMode:=SysSetVideoMode(VideoMode);
end;
procedure VesaSetCursorPos(NewCursorX, NewCursorY: Word);
begin
{$ifdef TESTGRAPHIC}
if not IsGraphicMode then
{$endif TESTGRAPHIC}
begin
SysSetCursorPos(NewCursorX,NewCursorY);
exit;
end;
{$ifdef TESTGRAPHIC}
if (NewCursorX<>LastCursorX) or (NewCursorY<>LastCursorY) then
begin
Case GetCursorType of
crHidden : ;
crUnderLine :
Begin
PutImage(LastCursorX*8,LastCursorY*8+7,UnderLineImage,XORPut);
PutImage(NewCursorX*8,NewCursorY*8+7,UnderLineImage,XORPut);
End;
crBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8,BlockImage,XORPut);
PutImage(NewCursorX*8,NewCursorY*8,BlockImage,XORPut);
End;
crHalfBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8+4,HalfBlockImage,XORPut);
PutImage(NewCursorX*8,NewCursorY*8+4,HalfBlockImage,XORPut);
End;
end;
LastCursorX:=NewCursorX;
LastCursorY:=NewCursorY;
end;
{$endif TESTGRAPHIC}
end;
procedure VesaSetCursorType(NewType : Word);
begin
{$ifdef TESTGRAPHIC}
if not IsGraphicMode then
{$endif TESTGRAPHIC}
begin
SysSetCursorType(NewType);
exit;
end;
{$ifdef TESTGRAPHIC}
if (NewType<>LastCursorType) then
begin
Case LastCursorType of
crHidden : ;
crUnderLine :
Begin
PutImage(LastCursorX*8,LastCursorY*8+7,UnderLineImage,XORPut);
End;
crBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8,BlockImage,XORPut);
End;
crHalfBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8+4,HalfBlockImage,XORPut);
End;
end;
SysSetCursorType(NewType);
Case NewType of
crHidden : ;
crUnderLine :
Begin
PutImage(LastCursorX*8,LastCursorY*8+7,UnderLineImage,XORPut);
End;
crBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8,BlockImage,XORPut);
End;
crHalfBlock :
Begin
PutImage(LastCursorX*8,LastCursorY*8+4,HalfBlockImage,XORPut);
End;
end;
LastCursorType:=NewType;
end;
{$endif TESTGRAPHIC}
end;
procedure VesaUpdateScreen(Force: Boolean);
{$ifdef TESTGRAPHIC}
var
StoreDrawTextBackground,
MustUpdate : boolean;
x,y : longint;
w, prevcolor,
prevbkcolor, StoreCursorType : word;
Color,BkCol,Col : byte;
Ch : char;
{$endif TESTGRAPHIC}
begin
{$ifdef TESTGRAPHIC}
if not IsGraphicMode then
{$endif TESTGRAPHIC}
begin
SysUpdateScreen(Force);
exit;
end;
{$ifdef TESTGRAPHIC}
if FirstCallAfterSetVesaMode then
begin
{ Make sure to redraw all }
Fillchar(OldVideoBuf^,VideoBufSize,#0);
FirstCallAfterSetVesaMode:=false;
end;
if not force then
begin
MustUpdate:=false;
asm
movl VideoBuf,%esi
movl OldVideoBuf,%edi
movl VideoBufSize,%ecx
shrl $2,%ecx
repe
cmpsl
setne MustUpdate
end;
end;
StoreDrawTextBackground:=DrawTextBackground;
DrawTextBackground:=true;
if Force or MustUpdate then
begin
PrevColor:=GetColor;
PrevBkColor:=GetBkColor{$ifdef FPC}(){$endif};
for y:=0 to ScreenHeight-1 do
for x:=0 to Screenwidth-1 do
begin
w:=VideoBuf^[x+y*ScreenWidth];
if Force or
(w<>OldVideoBuf^[x+y*ScreenWidth]) then
Begin
Color:=w shr 8;
Ch:=chr(w and $ff);
Col:=Color and $f;
if (Col = 0) and (GetMaxColor=255) then
Col:=255;
SetColor(Col);
BkCol:=(Color shr 4) and 7;
if (BkCol = 0) and (GetMaxColor=255) then
BkCol:=255;
SetBkColor(BkCol);
if (x=LastCursorX) and (Y=LastCursorY) then
begin
StoreCursorType:=LastCursorType;
VesaSetCursorType(crHidden);
end;
OutTextXY(x*8,y*8,Ch);
if (x=LastCursorX) and (Y=LastCursorY) then
VesaSetCursorType(StoreCursorType);
if not force then
OldVideoBuf^[x+y*ScreenWidth]:=w;
End;
end;
if Force then
move(videobuf^,oldvideobuf^,
VideoBufSize);
SetColor(PrevColor);
SetBkColor(GetBkColor{$ifdef FPC}(){$endif});
end;
DrawTextBackground:=StoreDrawTextBackground;
{$endif TESTGRAPHIC}
end;
procedure VesaDoneVideo;
begin
{$ifdef TESTGRAPHIC}
if IsGraphicMode then
begin
CloseGraph;
IsGraphicMode:=false;
end;
{$endif TESTGRAPHIC}
SysDoneVideo();
end;
function SetVESAVideoDriver : boolean; forward;
procedure VesaInitVideo;
begin
if not SetVESAVideoDriver then
exit;
{$ifdef TESTGRAPHIC}
if IsGraphicMode then
begin
SysInitVideo();
InitGraph(GraphDriver,GraphMode,'');
end
else
{$endif TESTGRAPHIC}
SysInitVideo();
end;
Function VesaGetVideoModeCount : Word;
begin
VesaGetVideoModeCount:=SysGetVideoModeCount()+VesaRegisteredModes;
end;
Procedure FreeVesaModes;
var
VH : PVesaVideoMode;
begin
VH:=VesaVideoModeHead;
While assigned(VH) do
begin
VesaVideoModeHead:=VH^.Next;
FreeMem(VH,Sizeof(TVesaVideoMode));
VH:=VesaVideoModeHead;
end;
end;
Var
Driver : TVideoDriver;
{$ifdef TESTGRAPHIC}
i : longint;
{$endif TESTGRAPHIC}
function SetVESAVideoDriver : boolean;
BEGIN
if disableVESA then
exit(false);
{ Get the videodriver to be used }
GetVideoDriver (Driver);
{ Change needed functions }
SysGetVideoModeCount:=Driver.GetVideoModeCount;
Driver.GetVideoModeCount:=@VesaGetVideoModeCount;
SysGetVideoModeData:=Driver.GetVideoModeData;
Driver.GetVideoModeData:=@VesaGetVideoModeData;
SysSetVideoMode:=Driver.SetVideoMode;
Driver.SetVideoMode:=@SetVESAMode;
SysSetCursorPos:=Driver.SetCursorPos;
Driver.SetCursorPos:=@VESASetCursorPos;
SysSetCursorType:=Driver.SetCursorType;
Driver.SetCursorType:=@VESASetCursorType;
SysUpdateScreen:=Driver.UpdateScreen;
Driver.UpdateScreen:=@VesaUpdateScreen;
SysDoneVideo:=Driver.DoneDriver;
Driver.DoneDriver:=@VesaDoneVideo;
SysInitVideo:=Driver.InitDriver;
Driver.InitDriver:=@VesaInitVideo;
{$ifdef TESTGRAPHIC}
BlockImage.width:=7;
BlockImage.height:=7;
For i:=0 to 8*8-1 do
BlockImage.colors[i]:=White;
HalfBlockImage:=BlockImage;
HalfBlockImage.height:=3;
UnderLineImage:=BlockImage;
UnderLineImage.height:=0;
{$endif TESTGRAPHIC}
SetVideoDriver (Driver);
SetVESAVideoDriver:=true;
END;
function ChkWinNT: boolean;
var
R: Registers;
begin
ChkWinNT := false;
R.AX := $3306;
RealIntr ($21, R);
if (R.AL = 255) or (R.BX <> 50 * 256 + 5) then
Exit;
R.AX := $3000;
RealIntr ($21, R);
if (R.AX = 5) and (R.BH = 255) then
ChkWinNT := true;
end;
begin
(* Let's disable VESA functions by default if running under MS Windows NT+ *)
if ChkWinNT then
DisableVESA := true;
END.