Жанр: Учеба
Техника программирования на турбо-С
...ызывает программные прерывания по вектору 0x28, так
называемые прерывания DOSOK. Это прерывание и его значение для
создания резидентных утилит более подробно рассматриваются в
разделе 11.
Когда вы нажимаете функциональную клавишу, ROM BIOS
возвращает двубайтный код. Первый байт имеет нулевое значение и
обозначает, что следующий за ним код символа соответствует
функциональной клавише. Этот второй байт содержит 7-битный ASCIIкод,
который является уникальным для каждой функциональной
клавиши. Если не учитывать первый нулевой байт, то реакция на
нажатие функциональных клавиш сходна с реакцией на нажатие
клавиш, соответствующих ASCII-символам, в частности, буквам.
Функция get_char преобразует двубайтную последовательность,
возвращаемую ROM-BIOS в ответ на нажатие функциональной клавиши,
в 8-битный код, позволяющий отличать функциональные клавиши от
нефункциональных. Функция осуществляет это преобразование путем
установки старшего разряда в байте, содержащем АSCII-код символа
и следующем за нулевым байтом. Формируемые коды описыватся в
исходном файле keys.h как глобальные символы (см. листинг 4.2).
Функция get_char ожидает нажатия функциональной клавиши,
обозначенной Help. Код, соответствующий функциональной клавише
Help, присвоен целочисленной глобальной переменной, названной
helpkey. Первоначально этой переменной присваивается нулевое
значение, но программные средства, которые работают с окном
Help, предназначенным для отображения справочной информациии,
будут присваивать этой переменной значение, соответствующее
функциональной клавише. При нажатии функциональной клавиши Help
функция get_char проверяет значение глобального указателя
функций, названного helpfunc. Если указатель имеет ненулевое
значение, то функция get_char вызывает адресуемую с помощью
указателя helpfunc функцию выдачи справочной информации.
void vpoke(unsigned vseg,unsigned adr,unsigned chr) int
vpeek (unsigned vseg,unsigned adr)
--------------------------------------------------------
Эти две функции считывают из видеопамяти коды символов и
атрибуты символов и записывают их в видеопамять. Для того, чтобы
использовать эти функции, вы должны разобраться в организации
видеопамяти IBM PC, а также принципах формирования изображения. В
тело функций vpoke и vpeek включены фрагменты на ассемблере. Для
того, чтобы оттранслировать эти функции, вы должны иметь
программу Macro Assembler (MASM) фирмы Microsoft, поскольку
именно она используется в Турбо Си для трансляции ассемблерных
фрагментов. Включение ассемблерных фрагментов необходимо только в
том случае, если ваши программы работают в системах, использущих
Цветной Графический Адаптер (CGA) или совместимый с ним адаптер.
Смысл этого требования разъясняется в разделе 5. Если у вас нет
контроллера CGA или если вы хотите работать с функциями, не
используя ассемблер, то удалите эти функции из исходного модуля
ibmpc.c и вставьте в файл twindow.h из раздела 6 следующие
операторы:
#define vpoke(vseg,adr,chr) poke(vseg,adr,chr)
#define vpeek(vseg,adr) peek(vseg,adr)
Эти макроопределения заменят функции vpoke и vpeek и избавят
от необходимости использования макроассемблера для функций работы
с окнами из этой книги.
Исходные модули функций общего назначения
-----------------------------------------------------------------
Листинг 4.1 представляет собой исходный текст программы
ibmpc.c, которая содержит функции, описанные в данном разделе.
Вследствие того, что функции включают фрагменты на ассемблере,
программу лучше транслировать с помощью командного компилятора
tcc, а не компилятора tc Интегрированной Среды.
Чтобы оттранслировать файл ibmpc.c, введите следующую
команду (не набирая промптер С"):
С"tcc -c ibmpc
Листинг 4.1: ibmpc.c
/* ibmpc.c */
/* Функции нижнего уровня, обращающиеся к BIOS и аппаратным
средствам РС */
#pragma inline #include "dos.h" static union REGS rg;
/* позиция курсора */
void cursor(int x,int y) {
rg.x.ax = 0x0200;
rg.x.bx = 0;
rg.x.dx = ((y "" 8) & 0xff00) + x;
int86( 16, &rg, &rg);
}
/* возвратить позицию курсора */
void curr_cursor( int *x, int *y )
{
rg.x.ax = 0x0300;
rg.x.bx = 0;
int86( 16, &rg, &rg );
*x = rg.h.dl;
*y = rg.h.dh;
}
/* установить тип курсора */
void set_cursor_type( int t )
{
rg.x.ax = 0x0100;
rg.x.bx = 0;
rg.x.cx = t;
int86( 16, &rg, &rg );
}
char attrib = 7;
/* очистить экран */
void clear_screen()
{
cursor(0, 0);
rg.h.al = ' ';
rg.h.ah = 9;
rg.x.bx = attrib;
rg.x.cx = 2000;
int86( 16, &rg, &rg );
}
/* возвратить режим работы видеоконтроллера*/
int vmode()
{
rg.h.ah = 15;
int86( 16, &rg, &rg);
return rg.h.al;
}
/* проверить клавишу Scroll Lock */
int scroll_lock()
{
rg.x.ax = 0x0200;
int86( 0x16, &rg, &rg);
return rg.h.al & 0x10;
}
void (* helpfunc)();
int helpkey = 0;
int helping = 0;
/* принять символ от клавиатуры */
int get_char()
{
int c;
while (1) {
rg.h.ah = 1;
int86(0x16, &rg, &rg);
if (rg.x.flags & 0x40) {
int86(0x28, &rg, &rg);
continue;
}
rg.h.ah = 0;
int86(0x16, &rg, &rg);
if (rg.h.al == 0)
c = rg.h.ah | 128;
else
c = rg.h.al;
if (c == helpkey && helpfunc) {
if (!helping) {
helping = 1;
(*helpfunc)();
helping = 0;
continue;
}
}
break;
}
return c;
}
/* занести код символа и его атрибуты в видеопамять */
void vroke(unsigned vseg, unsigned adr, unsigned chr)
{
if (vseg == 45056) /* монохромный режим */
poke(vseg, adr, chr);
else {
_DI = adr; /* смещение до адреса символа в видеопамяти */
_ES = vseg; /* адрес сегмента видеопамяти */
asm cld;
_BX = chr; /* атрибуты и код символа */
_DX = 986; /* состояние видеопорта */
/* ждать начала обратного хода луча*/
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
_AL = _BL;
asm stosb; /* запомнить символ */
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратнога хода луча */
do
asm in al,dx;
while (!(_AL & 1));
_AL = _BL;
asm stosb; /* запомнить атрибуты */
}
}
/* считать код символа и его атрибуты из видеопамяти */
int vpeek(unsigned vseg, unsigned adr)
{
int ch, at;
if (vseg == 45056) /* монохромный режим */
return peek(vseg, adr);
asm push ds;
_DX = 986; /* состояние видеопорта */
_DS = vseg; /* адрес сегмента видеопамяти */
_SI = adr;/* смещение до адреса символа в видеопамяти */
asm cld;
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
asm lodsb; /* считать символ */
_BL = _AL;
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
asm lodsb; /* считать атрибут */
_BH = _AL;
_AX = _BX;
asm pop ds;
return _AX;
}
/* keys.h */
#define HT 9
#define RUBOUT 8
#define BELL 7
#define ESC 27
#define SHIFT_HT 143
#define CTRL_T 20
#define CTRL_B 2
#define CTRL_D 4
#define ALT_D 160
#define F1 187
#define F2 188
#define F3 189
#define F4 190
#define F5 191
#define F6 192
#define F7 193
#define F8 194
#define F9 195
#define F10 196
#define HOME 199
#define UP 200
#define PGUP 201
#define BS 203
#define FWD 205
#define END 207
#define DN 208
#define PGDN 209
#define INS 210
#define DEL 211
#define CTRL_HOME 247
#define CTRL_BS 243
#define CTRL_FWD 244
#define CTRL_END 245
Заключение
-----------------------------------------------------------------
На основе представленных выше функций нижнего уровня в
разделе 5 будет развиваться и объясняться концепция экранных
окон, которая составляет следующий, более высокий, уровень в
многоуровневом наборе функций, описываемых в данной книге.
ГЛАВА 5
-------
Экранные окна
-----------------------------------------------------------------
Этот раздел посвящен вопросу о том, что собой представляют
экранные окна и как с ними работать. Раздел 6 разъясняет, как
можно использовать окна в ваших программах, создаваемых в среде
Турбо Си, а также содержит полную библиотеку функций управления
окнами. Следующие разделы содержат расширенную библиотеку
функций, поддерживающих работу с окнами для специфических целей,
как, например, для контекстно-чувствительного вывода справочной
информации, редактирования текста, ввода данных и создания меню.
После того, как вы прочитали об экранных окнах, способах их
создания и применения, постарайтесь вспомнить программные
системы, которые используют подобные средства. Подумайте
также и о том, какую пользу могли бы принести эти средства для
тех программных проектов, в которых вы принимали участие. Затем
попытайтесь найти в этих функциях недостатки, устранение которых
позволит сделать функции более подходящими для вашей работы. В
любой программе вы почти всегда можете отыскать эти недостатки.
В данном случае вы имеете большое преимущество: исходные тексты
функций предоставлены, вы можете модифицировать их по своему
усмотрению.
Экранное окно
-----------------------------------------------------------------
Окном называется область экрана дисплея, которая
используется для определенных целей. Окно обычно имеет форму
прямоугольника или квадрата, а его границей служат символы из
набора графических символов. Использование окон становится
наиболее популярным способом представления информации,
предназначенной для восприятия пользователем ПЭВМ. Этот способ
позволяет на ограниченном пространстве экрана отображать
информацию, передаваемую множеством задач, выполняющихся
асинхронно.
Поскольку отображаемые на экране изображения формируются в
видеопамяти с прямым доступом процессора (так называемый способ
формирования изображения путем управления содержимым памяти),
то окна на экране создаются мгновенно, возникая как бы из
ничего, и так же мгновенно исчезают. Использование окон в
программах позволяет очень удобно отображать различного рода
информацию. Окна появляются и сменяют друг друга: они
появляются на экране, когда это необходимо, и исчезают после
того, как информация, которую они содержат, становится вам
ненужной. Программа может отображать столько окон, сколько
нужно программисту. Окна могут иметь различные размеры, цвета и
форматы. Вы можете разместить окно в любом месте экрана, где это
необходимо. Если окно становится ненужным, то вы можете удалить
его, и окно исчезнет, а на его месте будет то, что было до его
появления.
Каждый, кто занимался проблемами автоматизации в последние
годы, обязательно сталкивался с окнами. ПЭВМ IBM PC сейчас
встречаются везде, в любой сфере общественной и деловой жизни. И
почти на каждой из них работает вездесущая программа Sidekick,
включающая калькулятор, календарь и средства вызова абонента в
сети. Каждый из этих компонентов отображает свою информацию с
помощью окон, которые по мере необходимости появляются и
исчезают на экране, не разрушая того изображения, которое было до
их появления. Волшебники в области разработки программного
обеспечения и ее сбыта из фирмы Bоrland International (это они
дали Вам Турбо Си) внедрили окна во всеобщую практику. Если вы
никогда не видели окон, то отложите книгу в сторону, найдите
IBM PC и нажмите одновременно клавиши "Alt" и "Ctrl". Если вы не
увидите после этого красно-бело-зелено-голубого окна на экране
(или зеленого и черного для монохромного экрана), то спросите у
владельца машины, почему у него не работает программа Sidekick.
Если же окно небольшого размера все-таки появится, начните с ним
игру. Сделав выбор в меню, содержащемся в первом окне, вы можете
создать другое окно. Вы можете перемещать окна, нажимая клавишу
со стрелкой. Вы можете удалять окна, нажимая клавишу "Esc". Вы
можете вызвать на экран справочную информацию по работе с
программой, нажав клавишу "F1".
Из этого раздела вы узнаете, как работать с окнами, как
накладывать их друг на друга, как перемещать их по экрану и как
их использовать в своих программах, разрабатываемых с помощью
Турбо Си. Окна используются для отображения меню, информационных
и предупреждающих сообщений, текстовых файлов, шаблона для ввода
данных и контекстно-чувствительных информационных сообщений.
Программные системы могут использовать окна для отображения
информации независимо от содержания других окон и размера области
пространства экрана, занимаемой другими окнами. Благодаря этому
можно обозревать на экране несколько окон, даже если каждое из
них занимает больше половины экрана, потому что окна могут как бы
"всплывать" друг над другом.
Свойство всплывания окон часто объясняют путем сравнения
экрана дисплея с поверхностью стола. Допустим, на вашем столе
лежит множество документов, но в каждый момент времени вы можете
работать только с одним из этих документов, возможно, наиболее
важным для вас. То, что можно делать с бумагами, можно выполнять
и на экране дисплея. Если первоначально изображение на экране
содержит только одно окно, как это показано на рис. 5.1, и
программе необходимо создать второе окно, не уничтожая при этом
первого, то в результате получится изображение, как на рис. 5.2.
Если затем второе окно становится ненужным, то оно уничтожается,
и изображение снова будет соответствовать рис. 5.1.
------------------------------------------------------------¬
¦ ¦
¦ ¦
¦ ---------------------------¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ Окно A ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ L--------------------------- ¦
¦ ¦
¦ ¦
L------------------------------------------------------------
Рис. 5.1 Экранное окно
------------------------------------------------------------¬
¦ -----------------------------¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ------------+ ¦ ¦
¦ ¦ ¦ Окно B ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ Окн¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ L--------------T-------------- ¦
¦ ¦ ¦ ¦
¦ L--------------------------- ¦
¦ ¦
L------------------------------------------------------------
Рис. 5.2 Наложение окон
Для того, чтобы понять, как можно использовать окна,
необходимо знать, какие действия с окнами можно выполнять.
Помните, что окном является прямоугольная область на экране
дисплея. Окна имеют и другие свойства, но главное, что отличает
их от других типов экранных изображений, - это их способность как
бы всплывать и погружаться относительно остального изображения.
Информация, которая содержалась в соответствующей
прямоугольной области экрана до появления нового окна, должна
сохраняться. Окно накладывается на предшествующее изображение,
как бы всплывает. Когда окно уничтожается (погружается), то
информация, которая была до его появления, должна быть
восстановлена. Та часть изображения, на которую накладывается
новое окно, также может содержать окна. На рис. 5.3 представлено
изображение на экране дисплея, которое содержит несколько окон,
причем каждое последующее окно накладывается на часть
предыдущего.
------------------------------------------------------------¬
¦ -----------------------------¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ----------+------¬ ¦
¦ ¦ ¦ ¦ ¦
¦ ------------+ ¦ Окно D ¦ ¦
¦ ¦ ¦ Окно B ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ---------------+ +--¬ ¦
¦ ¦ ¦ ¦ Окно C ¦ ¦ ¦ ¦
¦ ¦ Окн¦ ¦ ---------+----------------+-¬¦ ¦
¦ ¦ ¦ ¦ ¦ ¦¦ ¦
¦ ¦ ¦ ¦ ¦ ¦¦ ¦
¦ ¦ L---+ ¦ Окно E ¦¦ ¦
¦ ¦ ¦ ¦ ¦¦ ¦
¦ L---------------+ ¦ ¦¦ ¦
¦ ¦ L--------T----------------T--¦ ¦
¦ ¦ L----------------- ¦ ¦
¦ L----------------------------------- ¦
¦ ¦
L------------------------------------------------------------
Рис. 5.2 Наложение нескольких окон
Если бы каждая программа, работающая с окнами, должна была
управлять размещением окон и восстанавливать содержимое экрана,
которое было до их появления, то создание и сопровождение таких
программ было бы очень трудным делом. К счастью, в этом нет
необходимости. Поскольку функции и свойства окон являются общими
для разных применений, то можно использовать библиотеку функций
общего назначения для работы с окнами. Настоящая книга содержит
такую библиотеку, ее функции описаны в разделе 6.
Основных операций по работе с окнами, использующихся в
большинстве программ, не так уж много, и они не слишком сложны.
При работе с окнами вам необходимо установить окно, определив его
размеры и местоположение. Вы имеете возможность также
устанавливать его цвета, границу и заголовок. Вы можете вписать
свой текст внутрь окна, а также при необходимости изменять
местоположение окна. Наконец, вы можете удалить окно с экрана,
восстановив при этом изображение, которое было на экране до
появления окна. Основываясь на этих операциях, вы можете
создавать программы, которые используют чарующие многоцветьем
красок окна в качестве пользовательского интерфейса. (Следует
помнить, что использование эффективной библиотеки само по себе не
гарантирует высокое качество пользовательских характеристик
разрабатываемой программы. Программист должен использовать вместе
с окнами также звуковые эффекты и другие инструментальные
программные средства).
Чтобы понять, как можно управлять изображением на экране, вы
должны разобраться в организации видеопамяти. Приводимые ниже
сведения представляют собой введение в архитектуру видеопамяти
IBM PC. Для получения исчерпывающей информации по этому вопросу
обращайтесь к "Руководству Питера Нортона для программистов по
IBM PC" (Питер Нортон, Мicrosoft Press, 1985).
Архитектура видеопамяти
-----------------------------------------------------------------
Система формирования изображения является неотъемлемой
частью ПЭВМ IBM PC. В более ранних моделях персональных ЭВМ
видеотерминалы подключались через последовательные порты
ввода/вывода, но аппаратная архитектура IBM PC включает в себя и
видеосистему.
Предназначенное для вывода на экран изображение создается в
видеопамяти. В IBM PC в качестве видеопамяти используется часть
оперативной памяти. Видеопамять доступна для чтения и записи
процессору и, следовательно, вашим программам. Видеопроцессор,
входящий в состав видеоконтроллера, по содержимому видеопамяти
постоянно формирует изображение на экране дисплея. Поэтому
каждый новый символ, записанный в видеопамять, почти немедленно
появляется на экране. Поскольку видеопамять доступна для
микропроцессора, то скорость формирования изображения
соответствует скорости пересылки содержимого памяти, которая
превышает скорость передачи данных при подключении видеотерминала
через последовательные порты ввода/вывода.
Адреса видеопамяти и ее характеристики являются стандартными
для всех ПЭВМ линии IBM PC, а также совместимых с ними.
ПЭВМ типа IBM PC может иметь одну из трех видеосистем,
использующих различные типы видеомониторов и, следовательно,
различные типы видеоконтроллеров. Видеоконтроллер первого типа
называется Монохромным Адаптером (МА), он обеспечивает работу
только монохромного видеомонитора в символьном режиме и не
поддерживает графического режима. Видеоконтроллер второго типа
называется Цветным Графическим Адаптером (CGA). С помощью
контроллера CGA подключается цветной монитор, который может
работать в двух различных режимах. В символьном режиме имеется
возможность выбирать цвет фона и цвет символа из восьми возможных
цветов, а также один из двух уровней интенсивности цвета для
символа. В графическом режиме низкого разрешения (640x200
растровых точек) можно работать только с одним цветом. Третий
видеоконтроллер называется Усовершенствованным Графическим
Адаптером (EGA), который поддерживает такой же символьный режим,
что и CGA, и многоцветный графический режим более высокого
разрешения.
Приведенные в данной книге программы работают с любым из
этих контроллеров в символьном режиме. Поскольку в этом режиме
контроллеры CGA и EGA функционально эквивалентны, то
нижеследующие рассуждения для CGA относятся к обоим этим
контроллерам.
Видеопамять организована в виде двумерного массива символов,
состоящего из рядов и колонок. Ее можно рассматривать и как набор
следующих друг за другом 16-разрядных слов, по одному на каждый
символ. Каждый ряд содержит 80 следующих друг за другом символов,
все они образуют 25 следующих друг за другом колонок. Слово
содержит информацию об одном символе и состоит из двух
восьмибитных байт: один - для ASCII-кода символа, второй - для
атрибутов символа, определяющих его изображение. Код ASCII
записан в младшем (правом) байте слова.
Видеопамять контроллера МА организована в виде одной
страницы, а контроллера CGA - в виде четырех страниц. Программы
из этой книги используют только первую страницу видеопамяти
контроллера CGA.
Видеопамять расположена в верхних областях доступного
процессору адресного пространства. Разработчики IBM PC,
столкнувшись с ограничением на максимальный объем адресуемого
адресного пространства в 1 мегабайт, решили разместить
видеопамять и ПЗУ Базовой Системы Ввода-Вывода (ROM BIOS) в
верхних областях этого пространства. Для того, чтобы позволить
контроллерам МА и CGA работать совместно на одной ПЭВМ,
разработчики назначили различные адреса сегментов видеопамяти для
разных контроллеров. Память контроллера МА начинается в сегменте
0xB000, а память контроллера CGA - в сегменте 0xB800. Программа
может определить, какой из контроллеров используется, путем
вызова соответствующей функции ROM BIOS, и настроиться таким
образом на соответствующий адрес сегмента видеопамяти. Поскольку
архитектура видеопамяти для того и другого случая в основном
одинакова, то необходимые действия по настройке программы будут
минимальными.
Байт атрибутов символа содержит 2 трехразрядных поля
кодирования цвета (одно для цвета фона символа и одно для цвета
самого символа), разряд для задания уровня интенсивности цвета
символа и разряд для установки режима мерцания символа при его
отображении. На рис. 5.4 представлена конфигурация байта
атрибутов.
-----T----T----T----T----T----T----T----¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ B ¦ R ¦ G ¦ B ¦ I ¦ R ¦ G ¦ B ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
L-T--+----+----+----+--T-+----+----+----+
¦ L-------T------- ¦ L-------T-------
¦ ¦ ¦ ¦
¦ ¦ ¦ L------- цвет символа
¦ ¦ ¦
¦ ¦ L----------------- интенсивность цвета
¦ ¦ символа
¦ L--------------------------- цвет фона
¦
L-------------------------------------- мерцание символа
Рис. 5.4 Байт атрибутов симво
...Поділитися у соцмережах