|
| |||||||||||||||||
Паскаль для новичков (часть 40)Спрашивали? Отвечаю…
Работаем с графическим режимом 3 (продолжение)Автор: Владислав Демьянишин
![]() А вот и краткий код подпрограмм PutPixel24BitsVesa, GetPixel24BitsVesa:
procedure PutPixel24BitsVesa( X, Y : word; Color : TColor ); assembler;
asm
mov si,X; shl si,2 {24 bits/color}; mov bx,Y
…
int 10h
@a: mov ax,word ptr Color; mov es:[di],ax
add di,2; mov ax,word ptr Color+2; mov es:[di],ax
end;
function GetPixel24BitsVesa( X, Y : word ): TColor; assembler;
asm
mov si,X; shl si,2; mov bx,Y
…
int 10h
@a: mov ax,es:[di]; add di,2; mov dx,es:[di]
end;
Так как все дальние подпрограммы уже описаны, то можно закрыть блок директивой {$F-}. Теперь можно перейти к универсальным подпрограммам PutPixel и GetPixel. Они обеспечивают не только вывод и чтение точки, но и проверку выхода ее координат за пределы текущего ограничивающего окна, что позволит избежать вычисления некорректного адреса точки и обращения по нему, стало быть, и аварии.
procedure PutPixel( X, Y : integer; Color : TColor );
begin
if (X<0) or (X>=WinRect.Width) then exit;
if (Y<0) or (Y>=WinRect.Height) then exit;
X := X + WinRect.Left;
Y := Y + WinRect.Top;
PutPixelProc(X,Y,Color);
end;
function GetPixel( X, Y : integer ): TColor;
begin
GetPixel := 0;
if (X<0) or (X>=WinRect.Width) then exit;
if (Y<0) or (Y>=WinRect.Height) then exit;
X := X + WinRect.Left;
Y := Y + WinRect.Top;
GetPixel := GetPixelProc(X,Y);
end;
А вот и те две пресловутые функции, позволяющие получить информацию о видеоадаптере и параметрах поддерживаемых видеорежимов в глобальные переменные VESAInfo и ModeInfo.
function GetVESAInfo: boolean; assembler;
asm
mov ax,4f00h; lea di,VESAInfo; int 10h
shr ax,8; sub ax,1
end;
Данный код вызывает функцию 4f00h видео BIOS’а, которая возвращает информацию об адаптере в переменную типа TVESAInfo, адрес которой должен быть в регистровой паре ES:DI. В регистр AH при успехе возвращает нуль, иначе единицу. Поэтому это значение приходится командами shr ax,8; sub ax,1 преобразовать в тип boolean, где значение true будет означать успех. Интерфейс вызова следующей функции BIOS’а аналогичен, но она в переменную типа TVESAModeInfo возвращает информацию о видеорежиме с номером Mode.
function GetVESAModeInfo( Mode : word ): boolean; assembler;
asm
mov ax,4f01h; mov cx,mode; lea di,ModeInfo
int 10h; shr ax,8; sub ax,1
end;
Процедура инициализации констант базовых цветов:
procedure PrepareColors;
begin
clBlack := RGBToColor(0,0,0);
clBlue := RGBToColor(0,0,255);
clGreen := RGBToColor(0,128,0);
clRed := RGBToColor(255,0,0);
…
clYellow := RGBToColor(255,255,0);
clWhite := RGBToColor(255,255,255);
end;
А вот и функция для определения значения параметра GranulShift, то есть длины битового сдвига, соответствующего гранулярности текущего видеорежима:
function GetGranulShift( WinGranul : word ): word;
var Count : word;
begin
Count := 0;
while WinGranul <> 64 do begin
WinGranul := WinGranul shl 1;
inc( Count );
end;
GetGranulShift := Count;
end;
Тут все просто, и сводится к подсчету количества бит, на которое следует сдвинуть количество 64Кб-ых страниц, чтобы получить количество реальных страниц для текущего адаптера.
Очередная функция выполняет подготовку к установке затребованного видеорежима VESA по индексу массива режимов. Для этого вызывается функция GetVESAModeInfo для получения информации о режиме. Если информация получена, то режим поддерживается. Тогда инициализируется параметр GranulShift, затем выполняется установка видеорежима при помощи ассемблерной вставки с вызовом функции $4F02 BIOS’а, а в переменную Res возвращается значение типа boolean, где true – это успех. Завершает функцию код инициализации структур Screen и WinRect. Функция возвращает true в случае успеха.
function PrepareVesaMode( Index : word ): boolean;
var Res : boolean;
begin
PrepareVesaMode := false;
if not GetVESAModeInfo(Metrics[Index].mode) then exit;
GranulShift:=GetGranulShift(ModeInfo.WinGranul);
asm
mov LastPage,$FFFF; mov bx,Screen.Mode
mov ax,4F02h; int 10h; shr ax,8; sub ax,1
mov Res,al
end;
PrepareVesaMode := Res;
if Res then begin
with Screen do begin
Width := Metrics[Index].Width;
Height := Metrics[Index].Height;
BytesPerScanline:=ModeInfo.BytesPerScanline;
BitsPerPixel := ModeInfo.BitsPerPixel;
end;
DefaultWindow;
end;
end;
Функция SetMode обеспечивает установку текстового режима и графических режимов VGA, VESA. При этом, в ней выполняется инициализация процедурных переменных PutPixelProc, GetPixelProc, RGBToColor, а также константы базовых цветов. Функция возвращает true в случае успеха.
function SetMode( Index : word ): boolean;
begin
SetMode := false;
Move( Metrics[Index], Screen,
Sizeof( TVESAGraphMode ) );
case Index of
0: begin
asm
mov ax,Screen.Mode; int 10h
end;
PutPixelProc := NilProc;
GetPixelProc := NilFunc;
end;
1: begin
asm
mov ax,Screen.Mode; int 10h
end;
PutPixelProc := PutPixel8BitsVga;
GetPixelProc := GetPixel8BitsVga;
with Screen do begin
Width := Metrics[Index].Width;
Height := Metrics[Index].Height;
BytesPerScanline := Width;
BitsPerPixel := 8;
end;
clGray := 15;
DefaultWindow;
end;
2..6: if PrepareVesaMode( Index ) then begin
PutPixelProc := PutPixel8BitsVesa;
GetPixelProc := GetPixel8BitsVesa;
end
else exit;
7..11: if PrepareVesaMode( Index ) then begin
PutPixelProc := PutPixel16BitsVesa;
GetPixelProc := GetPixel16BitsVesa;
RGBToColor := RGBTo16Bits;
PrepareColors;
end
else exit;
else if PrepareVesaMode( Index ) then begin
PutPixelProc := PutPixel24BitsVesa;
GetPixelProc := GetPixel24BitsVesa;
RGBToColor := RGBTo24Bits;
PrepareColors;
end
else exit;
end; {case}
SetMode := true;
end;
Следующие две подпрограммы реализуют привычный интерфейс включения/выключения видеорежима.
function InitGraph( ModeIndex : word ): boolean;
begin
InitGraph := SetMode( ModeIndex );
end;
procedure ExitGraph;
begin
if SetMode( VESAText ) then;
end;
При установке видеорежима позиция и границы текущего окна совпадают с границами экрана дисплея. Для задания новых параметров окна достаточно создать следующую процедуру:
procedure SetWindow( MinX, MinY, MaxX, MaxY : word );
begin
with WinRect do begin
if MinX>Screen.Width-1 then MinX:=Screen.Width-1;
Left:=MinX;
if MinY>Screen.Height-1 then MinY:=Screen.Height-1;
Top:=MinY;
if MaxX>Screen.Width-1 then MaxX:=Screen.Width-1;
Right:=MaxX;
if MaxY>Screen.Height-1 then MaxY:=Screen.Height-1;
Bottom := MaxY;
Width := (Right-Left)+1;
Height := (Bottom-Top)+1;
end;
end;
При вызове данной процедуры параметры текущего окна задаются в глобальных координатах всего экрана (в пикселях), после чего вывод графики в окне производится в локальных координатах относительно верхнего левого угла окна (0,0). Если возникнет необходимость вернуться к исходному окну, то это легко осуществить с помощью следующей процедуры:
procedure DefaultWindow;
begin
with WinRect do begin
Left := 0;
Top := 0;
Right := Screen.Width-1;
Bottom := Screen.Height-1;
Width := Screen.Width;
Height := Screen.Height;
end;
end;
Следующие две процедуры предназначены для чтения/установки текущей палитры для 256-цветных режимов отображения. Для этого используются функции $1017 и $1012 BIOS’а соответственно.
procedure GetPalette( var Palette ); assembler;
asm
mov ax,1017h; mov bx,0; mov cx,256
les dx,Palette; int 10h
end;
procedure SetPalette( var Palette ); assembler;
asm
mov ax,1012h; mov bx,0; mov cx,256
les dx,Palette; int 10h
end;
На всякий случай оформим процедуру установки текущей страницы видеопамяти для отображения в видеобуфере.
procedure SetPage( Index : word ); assembler;
asm
mov dx,Index; mov LastPage,dx; mov ax,4f05h
xor bx,bx; int 10h
end;
Следующая функция позволяет определить, является ли видеоадаптер VGA-совместимым. Для этого вызывается подфункция 0 функции $1A BIOS’а для получения кода сочетания дисплея. Если функция возвращает значение $1A в регистре AL, то эта функция поддерживается, и тогда можно по значению в регистре BL определить тип дисплея: $00 – нет дисплея; $07 – монохромный VGA; $08 – цветной VGA; $FF – неизвестный тип дисплея. При успехе функция возвращает true.
function IsVGA : boolean; assembler;
asm
mov ah,1ah; mov al,00h; int 10h; cmp al,1ah
jne @NoVGA
cmp bl,07h; je @VgaPresent
cmp bl,08h; je @VgaPresent
@NoVGA: xor ax,ax; jmp @end
@VgaPresent: mov al,1
@end:
end;
Еще одна процедура, о которой уже слагают легенды ;0) позволяет дождаться момента, когда луч ЭЛТ-монитора начнет свой обратный ход из нижней строки в верхнюю. Обычно в момент обратного хода луча экран выключен и бит 3 (считать от нуля) регистра состояния дисплея, который доступен для чтения через порт $3DA, установлен в единицу. При этом, если в этот момент рисовать точки на экране, то это позволит избежать побочного эффекта "хлопьев" или мерцания изображения.
procedure WaitRetrace; assembler;
asm
mov dx, 3DAh
@L1: in al,dx; and al,08h; jnz @L1
@L2: in al,dx; and al,08h; jz @L2
end;
Наконец, модуль можно завершить блоком инициализации.
begin
PutPixelProc := NilProc;
GetPixelProc := NilFunc;
RGBToColor := NilRGBFunc;
end.
Подпрограммы данного модуля обеспечивают лишь самые примитивные манипуляции с графикой. Чтобы иметь возможность рисовать на экране линии, окружности, эллипсы, прямоугольники, закрашивать сложные фигуры, рисовать текст и выводить и масштабировать изображения в формате BMP могут пригодиться модули SBRUSH.PAS, FONTS.PAS и BITMAPS.PAS вашего покорного слуги. ;O)
Эти модули базируются на возможностях модуля, описанного в данной статье. Освещение этих модулей могло бы занять еще много страниц МК, поэтому я не стану испытывать терпение редакции, и не буду ущемлять других авторов, отбирая у них свободные страницы МК, чем обрекаю себя на голодную смерть ;о) , а просто предложу вам самим скачать с выше указанного сайта все эти модули вместе с примерами и самостоятельно разобраться в них.
Данный титанический труд вынудил меня составить материал объемом, равным трем статьям. Спасибо вам за терпение, с которым вы смогли прочесть данный толмут. ;0)
Продолжение следует…
© Владислав Демьянишин
Литература3. Interrupt list by Ralf Brawn v.3.3. На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 40): Работаем с графическим режимом 3 (продолжение)
| ||||||||||||||||||
|
||||||||||||||||||