http://sulfurzona.ru/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Wing-Thunder Game (fly simulator)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
Скачать игру Крыло-Гром (Wing-Thunder) бесплатно
 
 

Паскаль для новичков (часть 16)

 
Спрашивали? Отвечаю…

Работаем с текстовым режимом

 
Автор: Владислав Демьянишин
 
Паскаль для новичковКак и обещал, сегодня я продолжу рассмотрение создания модуля VESACRT.PAS. В предыдущей статье я рассказал о том, как очищать окно и выводить цветной текст в текущем окне. Для чего вообще нужно это окно? Бывают случаи, когда необходимо ограничить вывод текста на экран некоторой областью, например, нужно создать скроллируемое окно с полосой горизонтальной и вертикальной прокрутки. Естественно, что окно прокрутки будет меньше экрана дисплея. При этом текст не должен выходить за рамки этого окна. Таким образом, ограничивая окно различными областями экрана можно создавать отдельные окна для области текста, полос скроллинга, и прочих фитюлек ;O)
 
Как я уже ранее говорил, при установке видеорежима позиция и границы текущего окна совпадают с границами экрана дисплея. Для задания новых параметров окна достаточно создать следующую процедуру:
 
procedure SetWindow( MinX, MinY, MaxX, MaxY : byte );
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;
 
Таким образом, после очередной установки полей переменной WinRect, параметры текущего окна вступают в силу немедленно, и результат будет заметен уже при следующем выводе текста. Возможно, может понадобиться очистка текущего окна специальным символом и цветом, тогда вместо процедуры ClrScr можно использовать следующую процедуру:
 
procedure FillScr( Symbol : char; TextColor, BackColor : byte );
begin
FillRect(0, 0, WinRect.Width, WinRect.Height, Symbol, TextColor, BackColor);
end;
 
Курсор в текстовом режиме играет не последнюю роль. Его задача состоит в том, чтобы указать пользователю, где в данный момент ожидается ввод информации с клавиатуры. Т.е. необходимо предусмотреть возможность позиционирования курсора в нужную позицию окна. Для выполнения этой задачи опишем следующую процедуру, используя стандартную функцию 2 видео BIOS’а:
 
procedure SetCurPos ( x, y : byte ); assembler;
asm
 mov bh,0; mov dh,y; add dh,byte ptr[WinRect.Top]
 cmp dh,byte ptr[WinRect.Bottom]; jna @next1
 mov dh,byte ptr[Screen.Height]
@next1:
 mov dl,x; add dl,byte ptr[WinRect.Left]
 cmp dl,byte ptr[WinRect.Right]
 jna @next2; mov dl,byte ptr[Screen.Width]
@next2:
 mov ah,2; int 10h
end;
 
при этом следует подготовить содержимое регистров так, чтобы в регистре BH хранился номер страницы экрана. Курсор меняет свое положение на экране, только если установка курсора относится к текущей активной странице. В нашем случае 0, поскольку при установке видеорежима текущая страница имеет номер 0. А регистры DH:DL должны содержать номер строки и номер столбца, начиная с (0,0).
 
Так как вывод текста с помощью данного модуля осуществляется через прямое обращение к видеопамяти, то при вводе текста с клавиатуры необходимо предусмотреть сдвиг курсора в соответствии с вводимой текстовой и управляющей информацией. К последней можно отнести нажатия на клавиши Home, End, BackSpace, Delete и т.п.
 
Так как интерфейс программы может содержать окна не только для ввода информации, но и окна для вывода информации, при котором курсор не нужен, то вполне логично, что может возникнуть необходимость скрыть курсор, т.е. сделать его невидимым. Операционная система и BIOS не предоставляют специальных средств выключения курсора, но это легко сделать. Достаточно просто позиционировать курсор за пределы экрана, используя процедуру SetCurPos. Дело в том, что данная процедура проверяет попадание новой позиции курсора в пределы текущего окна, и если курсор оказывается за его логическими пределами, то значит, курсор следует выключить, т.е. установить его координаты за пределами экрана.
Теперь опишем процедуру выключения курсора:
 
procedure CursorHide;
begin
SetCurPos(0, Screen.Height);
end;
 
Для включения курсора достаточно установить его координаты в пределах текущего окна:
 
procedure CursorShow;
begin
SetCurPos(0, 0);
end;
 
Гм, чуть не забыл рассказать о том, как можно менять форму курсора. Ведь, например, в режиме вставки текста курсор, как правило, имеет форму символа “подчеркивание”, а в режиме замены текста имеет форму вертикального прямоугольника в полный рост обычного символа.
 
Курсор может меняться по толщине от тонкой линии до максимального размера, отводимого под символ. Он строится из коротких горизонтальных отрезков, верхний из которых называется начальной строкой (Start) курсора, а нижний – конечной строкой (Finish). Если значения начальной и конечной строк совпадают, то возникает однострочный курсор. Если номер конечной строки меньше, чем номер начальной, то возникает курсор, состоящий из двух частей, так как происходит перенос в верхние строки.
 
Используя функцию 1 видео BIOS’а можно установить произвольную форму курсора, задав начальную строку в регистр CH, а конечную в регистр CL:
 
procedure SetCurSize( Start, Finish : byte );
begin
if (Start > Screen.CharHeight-1) or
   (Finish > Screen.CharHeight-1) then exit;
asm
 mov ah,1; mov ch,Start
 mov cl,Finish; int 10h
end;
end;
 
Теперь, если нужно получить курсор “вставка”, то следует вызвать
 
SetCurSize( Screen.CharHeight-1, Screen.CharHeight-1);
 
А если нужен курсор “замена”, то соответственно
 
SetCurSize(0, Screen.CharHeight-1);
 
Все, о чем я рассказывал выше – это конечно хорошо, но не хватает одной важной детали. При таком разнообразии текстовых режимов, есть одно НО. Оно заключается в том, что при установке необходимого текстового режима проводится инициализация таблицы наборов символов. Даже если пользоваться русификатором клавиатуры KEYRUS.COM, то при выводе символов русского алфавита на экране все равно будут отражаться специальные значки, а не символы кириллицы. Стало быть, нужно предусмотреть возможность установки собственных шрифтов.
 
В этом может помочь подфункция 0 функции $11 по установке набора символов. Число устанавливаемых изображений символов заносим в регистр CX, а ASCII-код первого из них в регистр DX. Число байтов на символ заносим в регистр BH, номер блока устанавливаем BL=0. При этом регистры ES:BP должны указывать на массив изображений символов, их еще называют битовыми картами (графемами). Хочу обратить внимание на то обстоятельство, что регистр BP используется компилятором Turbo Pascal для указания адреса на локальные переменные, поэтому следует сначала загрузить в регистры значения всех соответствующих переменных, сохранить значение регистра BP в стеке командой push bp, и только после этого занести в группу регистров ES:BP указатель на таблицу графем.
 
С этого момента значение регистра BP будет другим и последующие обращения к локальным переменным, скорее всего, вызовут сбой в работе программы. Поэтому перед последующими обращениями к локальным переменным следует извлечь значение регистра BP из стека. Но так как в данной процедуре после команды les bp,CharTable обращений к локальным переменным нет, то значение регистра BP можно не сохранять и соответственно извлекать из стека не надо.Соответствующие строки я закомментировал для наглядности.
 
procedure SetCharTable( var CharTable; FirstChar, CharCount : word; BytePerChar : byte); assembler;
asm
 mov cx,CharCount; mov dx,FirstChar;
 mov bh,BytePerChar; {push bp;}
 les bp,CharTable; mov bl,0; mov ah,11h;
 mov al,0; int 10h; {pop bp;}
end;
 
На основе этой процедуры осталось написать функцию загрузки пользовательского шрифта с диска:
 
function InstallFont( FileName : string; FirstChar, CharCount, BytePerChar : word) : word;
type TBuf = array [0..20*256] of byte;
var Buf, Buf2 : ^TBuf;
      f : file;
      d, j : word;
      k : real;
begin
InstallFont := 0;
Buf := nil;
GetMem(Buf, 256*BytePerChar);
if Buf = nil then begin InstallFont := 8;exit;end;
Buf2 := nil;
GetMem(Buf2, 256*Screen.CharBytes);
if Buf2 = nil then begin
   { не хватает памяти }
   InstallFont := 8;
  FreeMem(Buf, 256*BytePerChar);
   exit;
   end;
 
assign(f, FileName); {$I-}
reset(f, 1); {$I+}
d := IOResult;
if d <> 0 then begin
   { ошибка диска }
   InstallFont := d;
   FreeMem(Buf2, 256*Screen.CharBytes);
   FreeMem(Buf, 256*BytePerChar);
   exit;
  end;
seek(f, 2);
blockread(f, Buf^, 256*BytePerChar, d);
close(f);
 
if Screen.CharBytes = BytePerChar then
   SetCharTable(Buf^[FirstChar*BytePerChar], FirstChar, CharCount, 
          Screen.CharBytes)
   else begin
          k := BytePerChar/(Screen.CharBytes-1);
          for d := 0 to 255 do
            for j := 0 to Screen.CharBytes-1 do
              Buf2^[d*Screen.CharBytes + j] :=  
                   Buf^[d*BytePerChar + trunc(k*j)];
          SetCharTable(Buf2^[FirstChar*Screen.CharBytes], FirstChar,  
               CharCount, Screen.CharBytes);
          end;
FreeMem(Buf2, 256*Screen.CharBytes);
FreeMem(Buf, 256*BytePerChar);
end;
 
Данная функция предусмотрена загружать с диска файл, в котором каждый из 256 символов представлен, например, восемью байтами, а в самом начале файла два байта хранят ширину и высоту шрифта. Во всяком случае, в таком формате я храню свои шрифты, которые легко можно создать в редакторе VG-NOW.
 
Функция InstallFont проверяет количество байт/символ шрифта, и если оно меньше Screen.CharBytes для текущего режима, то предварительно выполняет масштабирование до нужной высоты, иначе просто устанавливает шрифт без изменений.
 
Параметр FileName должен содержать имя файла, содержащего шрифт. FirstChar должен содержать номер первого устанавливаемого символа, а CharCount – количество загружаемых символов. Параметр BytePerChar должен содержать количество байт/символ в соответствии с файлом. Например, файл “vg.fnt” имеет размер 2050 байт, 2048 из которых содержат изображения 256 символов по 8 байт каждый.
 
Для установки всех 256 символов из файла “vg.fnt” следует вызвать
 
if InstallFont('vg.fnt', 0, 256, 8)>0 then
    if SetTextMode(0) then halt;
 
где в случае ошибки предусмотрен выход в DOS с восстановлением обычного текстового режима.
   Для загрузки младших 128 символов надо вызвать
 
if InstallFont('vg.fnt', 0, 128, 8)>0 then
    if SetTextMode(0) then halt;
 
а для загрузки только кириллицы, т.е. старших 128 символов можно вызвать
 
if InstallFont('vg.fnt', 128, 128, 8)>0 then
    if SetTextMode(0) then halt;
 
Ну и напоследок, научим наш модуль воспроизводить звук через PCSpeaker:
 
procedure Sound( Hz : word );
var k : word;
begin
{разрешение канала щ2 системного таймера через порт B микросхемы 8255}
port[$61] := port[$61] or 3;
{установка канала 2 на запись}
port[$43] := $B6;
{находим коэффициент пересчета для частоты Hz}
k := 1193180 div Hz;
{засылка счетчика в задвижку}
port[$42] := Lo(k);
port[$42] := Hi(k);
end;
 
procedure NoSound;
begin
{выключение звука}
port[$61] := port[$61] and $FC;
end;
 
Вот теперь наш модуль VESACRT.PAS умеет почти все, что умеет стандартный модуль CRT, хотя он будет не полной альтернативой модулю CRT, если не дополнить его возможностью ввода информации с клавиатуры. Но это уже отдельная тема и соответствующий код целесообразнее разместить в отдельном модуле, например, с именем KEYBOARD.PAS.
 
Литература
1. Р. Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. – М.: Финансы и статистика, 1992. – 543 с.
2. Диалоговая справочная система Norton Guide.
 
Продолжение следует…
 
© Владислав Демьянишин
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ.
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 16): Работаем с текстовым режимом
 
 
 
 
 
 
На главную страницу На предыдущую страницу На начало страницы