     СОВРЕМЕННЫЕ МЕТОДЫ ПРОМЫШЛЕННОЙ РАЗРАБОТКИ
             ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

                 Б. Ткаченко


"Науки связанные с вычислительной техникой стареют
и идеи, которые развивались в 60-е и 70-е годы,
сегодня являются лишь элементарной основой идей
и методов."
                    Дж.Ульман
                    Computational aspects of VLSI.
                    Computer Sciense Press inc. , 1987

   Чтобы немного прояснить разницу между современностью и
недалеким прошлым я позволю себе перечислить некоторые основ-
ные достижения в науке о программном обеспечении компьютеров,
которые сейчас позволяют нам ( программистам профессионалам )
работать более производительно и более уверенно смотреть в
будущее.
   К концу 70-х годов был достигнут ряд важных результатов:
1. Создание формальной теории проектирования компиляторов
с алгоритмических языков.
2. Формирование концепции абстрактных типов данных.
3. Разделение программного обеспечения на аппаратно-зависимую
и проблемно-зависимую части.
4. Создание переносимой операционной системы UNIX и переноси-
мого языка C.
5. Разработка унифицированных runtime-интерфейсов для приклад-
ных программ ( API ) и стандартных форматов обмена данными.
   Между тем растущее применение компьюттеров ( в особенности
персональных ) в самых различных областях приводило к  росту
потребностей в разработке программного обеспечения.
  В 70-е годы стало
ясно что прогресс в области системнгого программирования
должен быть распостранен и на методы разработки прикладного
ПО.  Таким распостранением явилось внедрение в практику
возникшей на основе концепции абстрактных типов данных
технологии обьектно-ориентированного программирования.
   Обьектно ориентированное программирование не новый термин.
Мы слышим о системах ООП уже с конца 70-х годов. Но подлинное
десятилетие ООП началось только сейчас, с созданием и широким
внедрением эффективных обьектно-ориентированных языков таких
как С++ и обьектно-ориентированные версии Pascal.
  Обьектно-ориентированные языки 70-х ( например такие как
Smalltalk ) опирались на мощные и громоздкие интегрированные
среды и реализовались в значительной мере на принципах интер-
претации.  На сегодняшний же день основным языком ООП стал
C++ созданный группой под руководством Бьерна Страуструпа
из Bell Laboratories ( AT&T corp. , USA ).  Значение этой
разработки нужно рассматривать исходя из того что впервые была
предложена стандартная, базирующаяся на синтаксисе переноси-
мого языка, форма для кодирования обьектов и их методов.
Более того идеология C++ была нацеленна на то что-бы все
операции по связыванию обьектов и манипулирующих ими методов
выполнялись во время компиляции.   В результате того что в C++
механизм скрытия данных работает во время компиляции , получа-
ющаяся выполняемая программа ( .exe - файл ) не содержит ниче-
го лишнего ( ни какого-нибудь там "монитора виртуальных
обьектов" или "динамического интерпретатора методов класса" ).
   Продолжающееся успешное внедрение C++ технологии в коммер-
ческие программы позволяет с уверенностью утверждать что язык
C++ стал основным средством разработки программных систем
ориентированных на конечного пользователя.
   Cистемами ориентированными на конечного пользователя
я называю программные системы которые может быстро освоить
и эксплуатировать персонал с минимальными навыками работы
с компьютером ( такие системы как dBASE , Clipper , ACAD ,
Framework и т.д. ).  Итак C++ является средством разработки
подобных систем.  
   Некоторые уже достаточно хорошо известные зарубежные
программные разработки имеют вполне отчетливый привкус C++.
 Также примером успешного применения методов ООП в индустрии
программного обеспечения может служить создание в самое послед-
нее время целого ряда специализированных генераторов программ
или их частей ( это реализуется путем компоновки обьектов из
библиотеки ).
     Например построитель интерфейса в системе ПО компьютера
 Next[8]. Эта программа позволяет описывать интерфейс пользова-
теля с вашей программой оперируя понятиями вида: окно, функцио-
нальная клавиша, меню и т.д. Построитель СПО Next генерирует на
выходе исходный текст на диалекте C++. Другим примером может
служить возможно уже знакомый читателю построитель интерфейса
из пакета turbo Pascal 6.0 .
 Джефф Уолден[11] описывая пакет Toolbook фирмы Asymetrix, ко-
торый "позволяет строить свою программу компонуя стандартные
обьекты из пакета Tollbook" говорит о целом ряде аналогичных
продуктов конкурирующих фирм.
  Хочу обратить внимание Читателя на то, что время подобных по-
строителей ( к слову сказать существенно облегчающих жизнь их
покупателям ) пришло с внедрением ООП.  Овладев одним из совре-
менных языков ООП ( например Zortech C++ , Borland C++ 2.0 или
даже turbo Pascal 5.5 ) Вы сами почуствуете с какой легкостью
подобные программы могут быть написаны Вами. Время "построителей"
( причем не только интерфейсов пользователя ) пришло с внедре-
нием C++. Что-бы в какой-то мере проилюстрировать технологию
программирования с использованием обьектно-ориентированного
подхода Вашему вниманию предлагается пример разработки небольшой
программы на C++.
  Допустим нам необходимо разработать программу реализующую
простейшие функции меню интерфейса. Что нам необходимо для
этой программы - какие обьекты и какие действия будут над этими
обьектами выполнятся?
  Очевидно - необходим обьект меню.
  Какие действия можно выполнять над меню?
Например следующие:
  - создать пустое меню ;
  - добавить строку к меню ;
  - нарисовать меню на экране дисплея ;
  - выбирать меню ( точнее передать управление меню пользовате-
    лю за клавиатурой ) ;
  - стереть меню с экрана .
 Итак раскроем карты дальше.  Преимущество обьектно ориентирован-
ного подхода заключается в том, что он позволяет нам рассматривать
такой сложный обьект как меню - как элементарный тип данных и
оперировать с ним также просто как мы оперирует с переменной
описанной например так:
                 int  j ;  .
  В сущности меню также просто как j. Ведь просто все то сущность
чего мы до конца понимаем.  А сущность меню ( также как и любого
сложного обьекта ) определяется действиями, которые можно выполнять
над ним.   Действия над обьектом в C++ называются  м е т о д а м и.
Нам необходимо определить эти методы формально - запрограммировать.
   Для того что бы запрограммировать методы: "создать пустое меню",
"добавить строку к меню" , "нарисовать меню на экране дисплея" ,
"выбирать меню"  и "стереть меню с экрана"  нам понадобятся неко-
торые данные отражающие содержимое и текущее состояние меню.
  Какие данные о меню нам понадобятся для реализации методов?
Перечислим их:
  - массив строк меню ;
  - текущее число строк в меню ;
  - номер текущей строки меню ( той которая подсвечена ) ;
  - координаты зкрана X и Y левого верхнего угла меню ( например ) ;
  - последняя нажатая пользователем клавиша.
 Язык C++ позволяет нам рассматривать данные обьекта и методы
обьекта вместе.  Закодировав список переменных, представляющих
данные перечисленные выше, и запрограммировав методы мы опреде-
лим , кстати, не какойто отдельный обьект меню , а любое про-
извольное меню. Мы определим целый класс обьектов. Совокупность
данных и оперирующих ими методов в C++ специально оформляется
синтаксически и называется описанием к л а с с а.
  Запишем теперь описание класса меню в синтаксической нотации C++.

class menu_vert
{
private:
   int x  ;  // Текущий левый верхний угол меню
   int y  ;
   int present_item ;    // номер текущей строки меню
   int number_of_item ;  // Текущее число строк меню
   int x_with  ;    //  длина самой длиной строки ( ширина меню )
   int glob_menu_key ;       // выбирающая клавиша
   int glob_menu_item ;      // номер выбранной строки меню
   char item[MAX_ITEM_AMOUNT][MAX_ITEM_WITH] ;  // сами строки меню
public:
void init()  ;                 //  инициализировать пустое меню
void add_item( char *newitem ) ; //    добавить строку в меню
void draw_menu()  ;             //   нарисовать меню на экране
void menu_choice()  ;           //  выбирать меню
void erase_menu() ;             // стереть меню с экрана
void set_menu_xy( int inx , int iny  ) ; // установить положение
                                         // меню на экране
int get_glob_menu_item() ; //  получить номер строки меню которую
                           // выбрал пользователь
int get_glob_menu_key() ;  //  получить код клавиши которой
                // производился выбор (например Esc или Enter)
}       ;

  Все что описано выше слова  public  является скрытым в меню
и недоступным для любого участка программы кроме методов класса,
которые описаны ниже public.   Полностью программа приведена в
приложении к статье ( эта статья не ставит своей целью описать
весь язык С++ и его синтаксис ).  Там Вы найдете и реализацию
методов класса  menu_vert .   А здсь мы посмотрим как легко
теперь станет использовать различные меню в программе.
  Допустим мы хотим иметь меню оболочку для компилятора.
Нет ничего проще - ведь у нас уже есть класс  МЕНЮ.

main()
{
   .
   .
   .
 menu_vert   abcd1 , abcd2 , abcd3  ;     // опишем 3 меню
             // не правда ли - не сложнее чем  "int i, j, k;"

  abcd1.init() ;              //  инициализируем 1-е меню
  abcd1.add_item("ЗАГРУЗИТЬ") ;
  abcd1.add_item("НОВЫЙ ФАЙЛ") ;
  abcd1.add_item("СОХРАНИТЬ") ;
  abcd1.add_item("ЗАПИСАТЬ В ...") ;
  abcd1.add_item("КАТАЛОГ") ;
  abcd1.add_item("ВЫХОД В DOS") ;
  abcd1.add_item("ПОКИНУТЬ - Alt X") ;
  abcd1.set_menu_xy(1,1) ;

  abcd2.init() ;             //  инициализируем 2-е меню
  abcd2.add_item("КОНТРОЛЬ СКОБОК") ;
  abcd2.add_item("КОНРОЛЬ СИНТАКСИСА") ;
  abcd2.add_item("ВЫПОЛНИТЬ ПРОГРАММУ") ;
  abcd2.set_menu_xy(20,1) ;

  abcd3.init() ;            //  инициализируем 3-е меню
  abcd3.add_item("СПИСОК СИНТАКСИЧЕСКИХ ОШИБОК") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ ЛИТЕРАЛЫ") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ КОНСТАНТЫ") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ СТАНДАРТНЫЕ ФУНКЦИИ") ;
  abcd3.add_item("ФУНКЦИИ ПОЛЬЗОВАТЕЛЯ") ;
  abcd3.set_menu_xy(43,1) ;
   .
   .
   .
// теперь если пользователь должен вступить в диалог с меню 2
  abcd2.draw_menu() ;    // мы его сначала рисуем

  abcd2.menu_choice() ; // затем передаем управление пользователю
                        //  так же просто как  "i=i+1 ;"

   user_string_number = get_glob_menu_key() ;
                             // получаем результат диалога
   user_key = get_glob_menu_item() ;   // с пользователем

   abcd2.erase_menu() ;        //  стираем меню
   if ( user_string_nubmer  ...  и т.д.
   .
   .
   .
}
  Вся утомительная возня с массивами и счетчиками по модулю N.
cкрыта внутри методов класса , причем она автоматически воспро-
изводится  - стоит лишь описать новую меню-переменную.
   Класс C++ наиболее близок к шаблону структуры в языке C.
Переменные структурного типа ( как abcd1 , abcd2 , abcd3 ) на-
зываются экземплярами класса. Отличие  классов C++ от структур 
в C заключается в том, что в C++ область видимости переменных, 
принадлежащих структуре - экземпляру класса находится исключи-
тельно внутри методов класса. Т.е. идентификаторы  present_item,
x_with и т.д. имеют смысл только внутри функций: init(),...
,get_glob_menu_key(). Причем вызов abcd1.init()  оперирует с  
abcd1.present_item , а вызов abcd2.init() с abcd2.present_item
 и т.д.
 Вызов  abcd2.init()  означает что функция menu_vert::init()
получает в качестве параметра указатель на abcd2 - экземпляр 
класса menu_vert.
  Так работает механизм скрытия данных C++ . Cкрытие данных 
позволяет нам избежать многих видов ошибок, сохранять концеп-
туальную целостность обьектов, проясняет структуру больших 
программ.
 Умело применяя механизм скрытия можно писать довольно сложные
программы, оперирующие сложноструктурированными данными. Причем
я не побоюсь утверждать что на C++ такие программы можно писать
быстро.  C++ значительно повышает повторную используемость, 
разработанных   а л г о р и т м о в   и  введенных в память ЭВМ
их исходных текстов. Этим целям, в частности, служит механизм 
производных классов и виртуальных функций.  Возможности хорошо 
написанной обьектно-ориентированной программы на C++ могут быть
существенно расширены или изменены с модификациями исходного
текста на порядок меньшими чем, например, при использовании 
обычного ( не ОО ) языка программирования. С++ позволяет писать
очень гибкие программы.  В связи с этим давайте вернемся к 
нашему примеру с меню ( я надеюсь Вы уже ознакомились с реали-
зацией методов класса menu_vert в программе из приложения ).
  Попробуйте представить себе что Вам необходимо теперь иметь
меню не из текстовых строк а из графических изображений на
экране дисплея.  Состав данных и методов класса для графичес-
кого меню практически тек же прост , как и для текстового.
  Например он может быть таким:

class slide_menu
{
private:
 char slb_file_name[FILENAME_LENGTH] ; // имя файла содержащего
                   // библиотеку изображений - пунктов меню
 int max_item ;    // реальное текущее число установленных
                   //  пунктов меню
 int present_item ;    // текущий пункт для menu_choice
 sld_menu_item  *items[MAX_ITEMS_AMOUNT] ; // сами изображения
                                          // составляющие меню
 int ret_codes[MAX_ITEMS_AMOUNT] ; // коды возвращаемые вызывающей
                       // программе при выборе каждого пункта меню
public:
 slide_menu( char *slb_f_n ) ;         // создать пустое меню
 void set_sld_item( sld_menu_item  *x , int ret_c ) ;  // добавить
                                      // изображение в меню
 int fill_menu( input_buffer &buffered_slb ) ; // заполнить
            // адреса поиска ( найти адреса всех изображений 
            // меню в оглавлении библиотеки )
 void draw_menu ( input_buffer &buffered_slb ); //отрисовать меню
 int menu_choice(int &key_code ) ; // выбирать меню
                        // возвращает текущий выбранный item
                        // и код выбирающей клавиши как параметр
 void menu_erase() ;       // стереть меню с экрана

}   ;

 Назначение методов аналогично: создать пустое меню, добавить
пункт , нарисовать меню , выбирать меню. Все так же как и в 
меню из текстовых строк. Только сам пункт меню теперь предста-
вляет собой уже сложную структуру
                - экземпляр класса: sld_menu_item.
   Спецификация этого класса приведена ниже:
class sld_menu_item
{
private:
 char member_slb_name[MAXCHNUM] ; // имя изображения в каталоге
                                  // библиотеки
 long seek_addr_in_slb ; // адрес начала изображения в библиотеке

 int  left_up_corner_X ;  //  границы  области экрана в которой
 int  left_up_corner_Y ;  // будет отрисовано изображение.
 int  right_down_corner_X ;
 int  right_down_corner_Y ;

 int  deltX_slide_file ; // экранное соотношение изображения
 int  deltY_slide_file ;
public:
   sld_menu_item(char *m_s_name,int l_u_X,int l_u_Y,
                                int r_d_X,int r_d_Y) ;
                            // создать пункт графического меню
   void direct_fill_seek_addr ( long s_addr ) ;
                    // установить адрес начала графических
                    // данных пункта меню в файле библиотеки
   void draw_menu_item( int buffered_slb ) ;
                        // отрисовать пункт графического меню
   void menu_item_activate() ;  // выделить пункт меню 
                       //  ( подсветить или взять в рамку )
   void menu_item_deactivate() ; // снять подсветку или рамку

   void erase_menu_item() ;   // стереть мункт меню с экрана
   void set_menu_item_borders( int lux , int luy ,
                               int rdx , int rdy ) ;
        // установить пределы области изображения пункта
        //    меню на экране
}  ;
 Обратите внимание на то что при написании самих методов
класса slide_menu нам необходимо знать об этой структуре
( о sld_menu_item ) только спецификацию вызова методов класса.
 Т.е. только секцию public спецификации класса.  Теперь, когда 
мы будем программировать метод draw_menu класса slide_menu, нам
необходимо будет , для того что-бы отрисовать пункт, вместо
   gotoxy( <X i-го пункта меню> , <Y i-го пункта меню> )  ;
   сputs( <строка i-го пункта меню> ) ;
 как в случае текстового меню , писать:
       (*items[i]).draw_menu_item( lib_handle ) ;
 где lib_handle - указатель на открытый файл библиотеки, откуда
читать графические данные.
  Конкретный вид программы отрисовки графического меню ( метод
draw_menu класса slide_menu ) может быть например таким:

void slide_menu::draw_menu( int buffered_slb )
{
// вычитслить все текущие углы слайдов по процедуре 
// item_coordinates затем отрисовать все слайды меню

  int ic ; //   счетчик item-ов
  int lux , luy , rdx , rdy ;

  for ( ic=0 ; ic < max_item ; ic++ )
 {
 // вычисление координат углов всех item-ов ( и их установка )
 item_coordinates( max_item , ic , &lux , &luy , &rdx , &rdy ) ;
 (*items[ic]).set_menu_item_borders (lux,luy,rdx,rdy ) ;
  } ;

  clearviewport() ;
            // порт вывода и input_buffer устанавливаются для
            // меню или меню системы в вызывающей программе

  for ( ic=0 ; ic < max_item ; ic++ )
  {
    // отрисовать все item-ы
    (*items[ic]).draw_menu_item( buffered_slb ) ;
  } ;

}

  Метод draw_menu_item класса sld_menu_item выводит графический
пункт меню на экран путем чтения и интерпретации структуры
графического файла выполняя при этом масштабные преобразования.
Это довольно сложная программа , но пользоватся ей просто.
  Таким образом мы подходим к еще одной замечательной особен-
ности обьектно-ориентированных языков.
 ОО языки позволяют разбить прикладную задачу на уровни
программного обеспечения ( нечто подобное принципу уровневого
построения эталонной модели программного обеспечения взаимодей-
ствия открытых систем ). Каждый уровень при этом имеет четко
определенный лаконичный интерфейс и набор услуг ( методов )
предоставляемых верхнему уровню.  Используя механизм виртуальных
функций C++ можно организовать интерфейс между уровнями так,
что   и с х о д н ы е   т е к с т ы   ( ! )  верхнего уровня
не будут зависеть от структуры обьектов нижнего уровня, с кото-
рыми они оперируют.  Одной и той же программой можно в этом 
случае оперировать с существенно различными данными.  Пример
из области организации интерфейса пользователя достаточто на-
гляден. Но тот же подход распостраним и на любые задачи.
 Как например программа расчета по методу МКЭ может не зависить
от вида самих конечных элементов , сетевая операционная система
- работать без изменения с различными протоколами нижнего уровня
и т.д.
  Перед нами маячит перспектива формализации алгоритмических
знаний человесества в различных прикладных областях на основе
единых инженерных средств.  Огромные библиотеки прикладных обь-
ектов с четко определенными интерфейсами, созданные в переноси-
мой, независящей от оборудрования ЭВМ форме - мы станем свиде-
телями их создания.
 Разбиение на уровни и спецификация уровней в прикладном прог-
раммном обеспечении скорее всего станет следующим обьектом 
стандартизации в компьютерной науке.
 Особенностью современного этапа компьютеризации в нашей стране
является то, что теперь  все чаще с ЭВМ вынуждены взаимодей-
ствовать люди не просто неподготовленные к этому, а неподготов-
ленные  в о о б щ е  - зто и бухгалтера , и конструкторы ,
и рабочие , и технологи , и многие другие.  При всей своей
некомпьтеризованности эти люди несут в себе громадный обьем
прикладных знаний. И применение ими новых методов компьютерной
обработки информации должно дать для общества значительный эффект.
  Некомпьтеризованный пользователь должен работать с компьютером
оперируя привычными для него понятиями и образами известными из
реальной жизни и производственной деятельности.
  Именно рассматривая под этим углом сегодняшние процессы ши-
рокомасштабного внедрения персональных компьютеров , коллектив 
нашего предприятия применил предлагаемый выше обьектно - 
ориентированный подход к созданию графического  ( основанного 
на принципах multimedia ) интерфейса пользователя в разработанном
нами продукте AutoMENU.
  Термин multimedia[9] означает среду сочетающую в себе взаимо-
связное представление текста, графики , звука и элементов мульти-
пликации.
Буквальный перевод multimedia - многосредность.
  В настоящее время нами РАЗРАБОТАН БАЗОВЫЙ ПАКЕТ ФУНКЦИЙ И 
ОБЬЕКТОВ РЕАЛИЗУЮЩИХ ВОЗМОЖНОСТИ MULTIMEDIA.   Этот базовый 
пакет вошел в разработанный нами программный продукт AutoMENU. 
AutoMENU - это не просто графический ToolBox а обьектно - 
ориентированная среда.
  Продукт AutoMENU написан на C++ и предназначен для предостав-
ления развитых средств графического взаимодействия с пользова-
телем прикладным программистам разрабатывающим программное обе-
спечение на языках традиционно лишенных таких возможностей  
( как например Clipper , Foxbase , FORTRAN и т.д.).   
 При использовании AutoMENU Вы получаете возможность использо-
вать в своих программах не точки, линии и окружности а обьекты 
более высокого уровня.
  Такие как:
    - графическое изображение в определенном месте экрана ;
    - иерархическое меню из графических изображений ;
    - текстовое меню на графическом экране ;
    - взаимосвязные текстовые и графические меню ;
   - экранные формы с заполняемыми полями ( как SAY , GET
      в dBASE ) в графическом режиме работы экрана ;
   - комбинации на экране графического изображения и экранной
     формы ;
   - комбинации на экране графических меню , экранных форм
     и наложенного на это текста ;
и многие другие.
  Причем Вы сможете работать с этими обьектами как с единым 
целым не погружаясь в детали их реализации.
  Схема применения продукта изображена на рисунке 1.
   Особенно удобно то что ДЛЯ ВВОДА ИСХОДНОЙ ГРАФИЧЕСКОЙ ИНФОР-
МАЦИИ ( для организации multimedia интерфейса с пользователем )
ВЫ МОЖЕТЕ ИСПОЛЬЗОВАТЬ такой широко распостраненный продукт как
AUTOCAD.  Исходные данные для интерфейсных функций могут дос-
тигать значительного обьема и хранятся в библиотечных файлах 
( как разделы ).  Для помощи в создании иерархически организо-
ванных обьектов поставляется руководство программиста и специ-
альная программа ObjMAKER.  Для создания библиотек изображений 
в формате системы AutoCAD Вы можете использовать программу 
AutoLIB.
 Программный продукт AutoMENU в настоящее время уже коммерчески
доступен. Приобрести его можно обратившись к нам по адресу:
 330027 , г.Днепропетровск , ул. Кирова 2 - 4 ,
 МП "КОМПЬЮТЕР-ГРАФИКА" , отв.исп. Б.Ткаченко
 телефоны: 58-58-23, 47-30-54 в г.Днепропетровске ( 8-0562 )
 Можно сейчас с уверенностью сказать что разработка multimedia
систем является одной из областей в которых обьектно-
ориентированный подход сулит получение значительных преимуществ
уже в самое ближайшее время и в первую очередь на персональных
компьютерах IBM PC.  Общественно-необходимым и интересным на-
правлением работ является их внедрение в программные системы 
применяемые в управлении производством и программные обучающие
системы в образовании. Любые контакты в этой связи  являются 
для автора данной статьи и коллектива предприятия очень жела-
тельными ( включая некоммерческий обмен научными идеями и
мнениями ). Это активное приглашение к сотрудничеству. 
  Давайте войдем в XXI век вместе.
  Кратко резюмирую опыт нашего коллектива полученный от при-
менения обьектно-ориентированных методов промышленной разра-
ботки программного обеспечения:
 1. Повышается индивидуальная производительность труда
программиста.
 2. Повышается предел проектирования ( используя C++ програм-
мист вполне может развивать программу обьемом 25 000 строк и 
более, при этом полность сохраняя ее всю в поле зрения ).
 3. Увеличивается время затарачиваемое программистом на пред-
варительное проектирование программ ( продумывание ее функций 
и структуры).
 4. Существенно уменьшается время идущее на отладку программы.
 5. На порядок уменьшается время на введение новых функций и 
любые изменения в программе. Чем тщательнее проведено предвари-
тельное проектирование тем больше возможно внесение значитель-
ных изменений в готовую уже разработку с сохранением ее работо-
способности.
 6. ОО программа лучше приспособлена для ее совместной разра-
ботки несколькими программистами одновременно.
 7. Существенно повышается надежность функционирования программ
при их эксплуатации у Заказчика. Логические ошибки практически 
полностью устраняются на стадии предварительного проектирования
( см. пункт 3 ).
 8. Общее время разработки и отладки программы уменьшается.
 9. Возрастают возможности использования частей предыдущих
разработок в новых с минимальными модификациями.
   Я думаю все сказанное выше, а также литература и техническая
документация по C++ убедит читателя в том что освоение языка C++
чрезвычайно необходимо специалисту, желающему идти в ногу со
временем. Более того необходимо его широкое и массовое освоения
в нашей стране.  Необходимо так-же создание отечественных тран-
сляторов с этого языка.  Из зарубежных компиляторов можно
рекомендовать Borland C++ 2.0  ( Borland international , дис-
трибутор INTERQUADRO ) хотя turbo C++ 1.0 уже достаточно хорош.
  Компиляторы фирмы Zortech имеют очень хорошую runtime - 
библиотеку но начинающие могут испытывать затруднения с ними
из за недостаточно строгого контроля типов и слабой обработки 
ошибок времени выполнения.
  Кроме того эти компиляторы сначала транслируют C++ на С , 
а это тоже не очень удобно. Интегрированная среда Zortech -
компиляторов C++ серьезно уступает turbo-оболочкам Borland.
  С++ впервые предлагает нам единую платформу для прикладного 
программирования и алгоритмического структурирования знаний
накопленных в прикладных областях. Эта единая платформа - 
язык спецификации обьектов.
Программы верхнего уровня C++ фактически оперируют понятиями
достаточно высокого уровня, которые вполне могут быть прямо
соотнесены с понятиями задачи и прикладной области.
 Исходя из последнего важным и продуктивным направлением ис-
следований может стать создание проблемно-ориентированных сис-
тем автоматической генерации программ из библиотек типовых
модулей, на основе соединения концепций заложенных в организа-
ции исходного кода C++ с возможностями систем типа ПРИЗ (разра-
ботка ИК АН ЭССР ) , языка пролог и экспертных систем.

  Литература

 1. С++ Primer. , Stanley B. Lippman , AT&T Bell Laboratories ,
    Addison Wesley Publishing company , 1989
 2. An Introduction to Object-Oriented Programming and C++,
    Richard S. Weiner and Lewis Pinson, MA: Adisson Wesley, 1988
 3. Stanley Lippman and Bjarne Stroustrup, Pointers to Class
    Members in C++. Proc. of the USENIX C++ Conference,
    Denever, CO , october, 1988.
 4. Bjarne Stroustrup: The C++ Programming Language,
    Addison Wesley, Reading, MA, 1986.
 5. Adle Golberg and David Robson:
    SMALLTALK-80: The language and its implementation.,
    Addison-Wesley, MA, 1983.
 6. Ralph Johnson and Brian Foote: Designing Reusable Classes,
    Journal of Object-Oriented Programming, June/July 1988.
 7. Turbo C++ 1.0. Programmers Guide., Borland International,
    CA , USA.
 8. Что в будущем ... Next. Брюс Ф. Уебстер "МИР ПК" 4/1989
 9. Baн Дем  , Электронные книги , Доклад на советско - 
    американской конференции ASM SIGGRAPH/GRAPHICON'91.
10. M.И.Кахро , А.П.Калья , А.П.Тыугу
    Инструментальная система программирования ПРИЗ. ,
    М: "Финансы и статистика" , 1988
11. Джефф Уолден , Пакеты HYPERCARD и WINDOWS обьединяют
    усилия. , "МИР ПК" 5/90.


P.S.  Все имена продуктов и торговые марки которые возможно
содержаться в статье, являются собственностью соответствую-
щих компаний и использованы здесь исключительно к выгоде
их владельцев.



Приложение:
  Приведенная ниже программа тестировалась в среде
turbo C++ v 1.0 .  Методы, декларируемые в описании
класса ниже функции windpop , в данном примере не
используются, но они иллюстрируют то как добавляя
новые методы можно сделать меню интерфейс более гибким.
Эти, неиспользованные здесь методы, также, отлажены и готовы
к применению.
_________________________________________________________________

#define  MAX_VAR_NUMBER   160

#define  COL_ORDINARY     GREEN
#define  BK_ORDINARY      BLACK

#define  COL_SELECTION    WHITE
#define  BK_SELECTION     MAGENTA

#define  COL_ALARM        WHITE
#define  BK_ALARM         RED

#define  COL_ALARM_SEL        BLACK
#define  BK_ALARM_SEL         LIGHTGRAY

#include <mem.h>
#include <dos.h>
#include <string.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
				  //  для exit
#include <math.h>

#include "keyboard.h"

// ***BEGIN***************************************************************** *)
//  C o n s t a n t s   o f   t h e    M E N U _ V E R T    c l a s s        *)
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city        *)
// ************************************************************************* *)
#define MAX_ITEM_AMOUNT      20
#define MAX_ITEM_WITH        80
#define BORDER_COLOR         COL_ORDINARY
#define FRAME_COLOR          COL_ORDINARY
#define SELECTION_COLOR      COL_SELECTION
#define BORDER_BK_COLOR      BK_ORDINARY
#define FRAME_BK_COLOR       BK_ORDINARY
#define SELECTION_BK_COLOR   BK_SELECTION
// ************************************************************************* **
//  C o n s t a n t s   o f   t h e    M E N U _ V E R T    c l a s s        **
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city        **
// ****END****************************************************************** **

// ***BEGIN***************************************************************** **
//  D e f i n i t i o n    o f   t h e    M E N U _ V E R T    c l a s s     **
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city        **
// ************************************************************************* **
// class menu_vert begin  **
class menu_vert
{
private:
   int x  ;  // Текущий левый верхний угол меню
   int y  ;
   int present_item ;        // Текущий ( подсвеченный ) пункт меню.
   int number_of_item ;  // Текущее число пунктов меню
   int x_with  ;         //  = max ( strlen ( item all  ))
   int glob_menu_key ;        // выбирающая клавиша
   int glob_menu_item ;       // выбранный пункт меню
   char item[MAX_ITEM_AMOUNT][MAX_ITEM_WITH] ;  // сами пункты меню
   char save_area[(MAX_ITEM_AMOUNT*MAX_ITEM_WITH)*2+10]  ;
                        // область сохранения экрана
public:

void init()  ;                           //  Инициализировать пустое меню.
void add_item( char *newitem ) ;          // добавить строкеу в меню
void draw_menu()  ;  //  Отрисовать меню с сохранением экрана ( под ним ).
void menu_choice()  ;               //  Выбирать меню.
void erase_menu() ;   // стереть меню с восстановление экрана
void set_menu_xy( int inx , int iny  ) ;  // установить левый верхний угол
int get_glob_menu_key() ;   // получить клавишу которой произведен
                            // последний выбор строки меню
int get_glob_menu_item() ;  // получить номер строки которая была выбрана

                 // служебные функции сохранения старого экрана
void windsw( int x1 , int y1 , int x2 , int y2 ) ;
void windpop( int x1 , int y1  , int x2  , int y2 )  ;


int get_x_with() ;              // Функции доступа к скрытым данным класса.
int get_number_of_item() ;
void set_item( int item_number , char *newitem ) ;
                                        // измнить существующий пункт
void set_present_item ( int item1 ) ;
                     // принудительно установить номер текущего пункта меню
void hide_items_cursor()  ; // убрапть подсветку текущего пункта
void draw_items_cursor()  ; // снова подсветить текущий пункт
void clear_menu() ;                   // прописать все item пробелами
}       ;
// *************************************************************************
//  D e f i n i t i o n    o f   t h e    M E N U _ V E R T    c l a s s
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city
// ***END*******************************************************************

// **BEGIN************************************************************** **
//     ФУНКЦИИ ИСПОЛЬЗУЮТСЯ ЧЛЕНАМИ КЛАССА  MENU_VERT                    **
// ************************************Copyright B.Tkachenko 1990 ****** **

void draw_box(
           int     typ   ,
                                    /*          0- double line */
                                    /*  3 - space characters only */
           int     col   ,    /*  color of the box border */
           int     colbk   ,  /*  color of the box border */
           int     y1  ,      /*  x1 <  x2  ;  y1 < y2  */
           int     x1  ,
           int     y2  ,
           int     x2
                  )
{
  int i  ;
  int dx , dy  ;
  char   *right_upper , *hor_lin , *left_upper , *vert_lin ,
		 *right_bottom , *left_bottom ;
switch ( typ  )
{
case 0: {
  left_upper   ="╔";
  right_upper  ="╗";
  vert_lin     ="║";
  hor_lin      ="═";
  right_bottom ="╝";
  left_bottom  ="╚";
        } ; break ;
case 3: {
  left_upper   =" ";
  right_upper  =" ";
  vert_lin     =" ";
  hor_lin      =" ";
  right_bottom =" ";
  left_bottom  =" ";
        } ; break ;
} ;
textcolor(col) ;
textbackground(colbk) ;

dx = x2 - x1 ;
dy = y2 - y1 ;
gotoxy(x1,y1) ;
cputs(left_upper) ;
for ( i=1 ; i<= dx-1 ; i++ ) cputs(hor_lin) ;
cputs(right_upper) ;
for ( i=1 ; i<= dy-1 ; i++ )
  {
  gotoxy(x2,y1+i) ;
  cputs(vert_lin) ;
  gotoxy(x1,y1+i) ;
  cputs(vert_lin) ;
  } ;
gotoxy(x1,y2) ;
cputs(left_bottom) ;
for ( i=1 ; i<= dx-1 ; i++ ) cputs(hor_lin) ;
cputs(right_bottom) ;
}


int inc_pos(int *cur_x , int *cur_y , int *x1 , int *x2 , int *y2 )
/* ----------- for procedures WINDSW and WINDPOP sedrvice small subroutine */
{
 int inc_pos_var ;
 inc_pos_var = 1 ;
 (*cur_x)++ ;
 if ( (*cur_x) > (*x2) )
  {
    (*cur_y)++ ;
	(*cur_x) = (*x1) - 1 ;
   } ;
 if ( (*cur_y) > (*y2) ) inc_pos_var = 0 ;
return( inc_pos_var ) ;
}




/*  ПРОЦЕДУРА СОХРАНЕНИЯ ВСЕГО ОКНА ВО ВРЕМЕННОМ ХРАНИЛИЩЕ */
void menu_vert::windsw( int x1 ,
			 int y1 ,
			 int x2 ,
			 int y2
		   )
#define  ScreenBuffer  0xb800
                           /*  $b000 - монохроматический */
{
int  i, cur_x , cur_y  ;
	cur_x = x1 -2 ;
	cur_y = y1 -1 ;
	i = 0 ;
while ( inc_pos( &cur_x , &cur_y ,  &x1 , &x2 , &y2 ) )
	   {
movedata(ScreenBuffer,(cur_x+cur_y*80)*2,FP_SEG(&save_area[i]),
										 FP_OFF(&save_area[i]),2) ;
	   i = i + 2 ;
	   } ;
	   return ;
}

/*  ПРОЦЕДУРА ВОССТАНОВЛЕНИЯ ВСЕГО ОКНА ИЗ ВРЕМЕННОГО ХРАНИЛИЩА */
void menu_vert::windpop(
              int        x1 ,
              int        y1  ,
              int        x2  ,
              int        y2
            )
#define   ScreenBuffer  0xb800
                          /*  $b000 - монохроматический */
{
int  i, cur_x , cur_y  ;
    cur_x = x1 -2 ;
    cur_y = y1 -1 ;
    i = 0 ;
	while ( inc_pos( &cur_x , &cur_y , &x1 , &x2 , &y2 ) )
       {
movedata(FP_SEG(&save_area[i]),FP_OFF(&save_area[i])
                              ,ScreenBuffer,(cur_x+cur_y*80)*2,2) ;
       i = i + 2 ;
   } ;
}

// **END**************************************************************** **
//     ФУНКЦИИ ИСПОЛЬЗУЮТСЯ ЧЛЕНАМИ КЛАССА  MENU_VERT                    **
// ************************************Copyright B.Tkachenko 1990 ****** **

// ***BEGIN***************************************************************** **
//    M e t o d s    o f    t h e     M E N U _ V E R T     c l a s s        **
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city        **
// ************************************************************************* **
int menu_vert::get_x_with ()
      {
		 return(x_with)  ;
	  }

int menu_vert::get_number_of_item()
      {
		 return(number_of_item)  ;
	  }

int menu_vert::get_glob_menu_key()
	   {
		 return(glob_menu_key) ;
	   }

int menu_vert::get_glob_menu_item()
	   {
		 return(glob_menu_item) ;
	   }

void menu_vert::init()
//  initialize one new vertical menu  **
{
      x = 0 ;
      y = 0 ;
      number_of_item = 0 ;
      present_item = 0 ;
      x_with = 0 ;
	  glob_menu_key = 0x20 ;
	  glob_menu_item = 0 ;
}



void menu_vert::clear_menu()
{
  int i , j  ;
      for ( i=1 ; i<=number_of_item ; i++ )
	   {
		 item[i][0] = 0x00 ;
		 for ( j=1 ; j<= x_with ;  j++ )  strcat(item[i]," ") ;
       } ;
}


void  menu_vert::menu_choice()
    //  selection in vertical menu **
{
 int     i ;
 int     j   ;
 int     user_key   ;
 int     previous_item   ;

      user_key = 0x20 ;

      while   (
			  ( user_key != EASC_Enter )      &&
			  ( user_key != EASC_Left )       &&
			  ( user_key != EASC_Right )      &&
			  ( user_key != EASC_Esc )
              )
{
	  user_key = i_readkey() ;
      previous_item = present_item ;

   switch ( user_key )
    {
     case      EASC_Home   :
                              {
                              present_item = 0 ;
                              } ; break ;
     case      EASC_Left   :
                              {
                              } ; break ;
     case      EASC_End    :
                              {
                              present_item = number_of_item - 1 ;
                              } ; break ;
     case      EASC_Up     :
                              {
                              if ( present_item == 0 )
                              present_item = number_of_item ;
                              if ( present_item > 0 )
                              present_item = present_item - 1 ;
                              } ; break ;
     case      EASC_Down   :
                              {
                              present_item = present_item + 1 ;
                              if ( present_item >= number_of_item )
                              present_item=0 ;
                              } ; break ;
     case      EASC_PgUp   :
                              {
                              } ; break ;
     case      EASC_Right  :
                              {
                              } ; break ;
     case      EASC_PgDn   :
                              {
                              } ; break ;
    } ;  // switch


               textcolor(FRAME_COLOR) ;
               gotoxy(x+1,y+1+previous_item) ;
               cputs(item[previous_item]) ;
               textcolor(SELECTION_COLOR) ;
               gotoxy(x+1,y+1+present_item) ;
               cputs(item[present_item]) ;

      glob_menu_key=user_key ;
      glob_menu_item=present_item ;

}   ;       // while
}                       // menu_choice  metod


void menu_vert::draw_menu()    //  with saving of previous screen **
{
 int     i , j , y2 , x2 ;

      y2= y + number_of_item + 1 ;
      x2= x + x_with + 1 ;
      (*this).windsw(
                   x,
                   y,
                   x2,
                   y2
             ) ;

      gotoxy(x,y) ;
      textcolor(BORDER_COLOR) ;
      textbackground(BORDER_BK_COLOR) ;

      draw_box( 0,
                BORDER_COLOR,
                BORDER_BK_COLOR,
                y,
                x,
                y2,
                x2
              ) ;

      textcolor(FRAME_COLOR) ;
      textbackground(FRAME_BK_COLOR) ;
        for ( i=0 ; i<=number_of_item - 1 ; i++ )
          {
               if ( i == present_item )
               {     textcolor(FRAME_COLOR) ;
                     textbackground(FRAME_BK_COLOR) ;
               } ;

               gotoxy(x+1,y+1+i) ;

               for ( j=x+1 ; j<=x+x_with ; j++ )
               {
                  gotoxy(j,y+1+i) ;
				  cputs(" ") ;
               } ;
               gotoxy(x+1,y+1+i) ;
               cputs( item[i] ) ;
               textcolor(FRAME_COLOR) ;
               textbackground(FRAME_BK_COLOR) ;
          } ;
}




void menu_vert::set_menu_xy( int inx , int iny )
   // set upper left corner  **
{
       x = inx ;
       y = iny ;
}





void menu_vert::add_item( char *newitem )
{
     strcpy(item[number_of_item],newitem) ;
     if ( x_with < strlen(newitem) ) x_with = strlen(newitem) ;
     number_of_item = number_of_item + 1 ;
}


void menu_vert::set_item( int item_number , char *newitem )
//  ИЗМЕНИТЬ ЗНАЧЕНИЕ УЖЕ СУЩЕСТВУЮЩЕЙ СТРОКИ МЕНЮ
{
  if ( item_number < number_of_item )
  { strcpy(item[item_number],newitem) ;
  }
  else
  {
   strcpy(item[number_of_item],newitem) ;
  } ;
}



void menu_vert::erase_menu()
// without  restore previous screen **
{
 int     i , j , y2 , x2 , y1 , x1 ;

      y2 = y+number_of_item+1 ;
      x2 = x+x_with+1 ;
      y1 = y ;
      x1 = x ;

	  (*this).windpop(
                   x1,
                   y1,
                   x2,
                   y2
             ) ;

}


void menu_vert::set_present_item ( int item1 )
{
  present_item=item1 ;
  if ( present_item > number_of_item-1 ) present_item = number_of_item-1 ;
}



void menu_vert::draw_items_cursor()
{
  int  i , j ;
   i = present_item ;
               textcolor(SELECTION_COLOR) ;
          textbackground(SELECTION_BK_COLOR) ;
               gotoxy(x+1,y+1+i) ;
               for ( j=x+1 ; i<=x+x_with  ; i++ )
               {
                  gotoxy(j,y+1+i) ;
                  cputs(" ") ;
               } ;
               gotoxy(x+1,y+1+i) ;
               cputs( item[i] ) ;
}



void menu_vert::hide_items_cursor()
{
  int   i , j ;
   i = present_item ;
               textcolor(FRAME_COLOR) ;
               textbackground(FRAME_BK_COLOR) ;
               gotoxy(x+1,y+1+i) ;
               for ( j=x+1 ; i<= x+x_with  ; i++ )
               {
                  gotoxy(j,y+1+i) ;
                  cputs(" ") ;
               } ;
               gotoxy(x+1,y+1+i) ;
               cputs( item[i] ) ;
}

// ************************************************************************* **
//    M e t o d s    o f    t h e     M E N U _ V E R T     c l a s s        **
//                    Copyright B.Tkachenko,1990,Dniepropetrovsk city        **
// ***END******************************************************************* **

main()

{

 menu_vert   abcd1 , abcd2 , abcd3  ;

  abcd1.init() ;

  abcd1.add_item("ЗАГРУЗИТЬ") ;
  abcd1.add_item("НОВЫЙ ФАЙЛ") ;
  abcd1.add_item("СОХРАНИТЬ") ;
  abcd1.add_item("ЗАПИСАТЬ В ...") ;
  abcd1.add_item("КАТАЛОГ") ;
  abcd1.add_item("ВЫХОД В DOS") ;
  abcd1.add_item("ПОКИНУТЬ - Alt X") ;
  abcd1.set_menu_xy(1,1) ;
  abcd1.draw_menu() ;
  abcd1.menu_choice() ;
  abcd1.erase_menu() ;

  abcd2.init() ;

  abcd2.add_item("КОНТРОЛЬ СКОБОК") ;
  abcd2.add_item("КОНРОЛЬ СИНТАКСИСА") ;
  abcd2.set_menu_xy(20,1) ;
  abcd2.draw_menu() ;
  abcd2.menu_choice() ;
  abcd2.erase_menu() ;

  abcd3.init() ;


  abcd3.add_item("СПИСОК СИНТАКСИЧЕСКИХ ОШИБОК") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ ЛИТЕРАЛЫ") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ КОНСТАНТЫ") ;
  abcd3.add_item("ИСПОЛЬЗОВАННЫЕ СТАНДАРТНЫЕ ФУНКЦИИ") ;
  abcd3.add_item("ФУНКЦИИ ПОЛЬЗОВАТЕЛЯ") ;
  abcd3.set_menu_xy(43,1) ;
  abcd3.draw_menu() ;
  abcd3.menu_choice() ;
  abcd3.erase_menu() ;

}              //  end main




             INCLUDE - файл  KEYBOARD.H
            ______________________________



/*   ****Begin*****************************************************    */
/*   **       K E Y B O A R D      D E F I N I T I O N           **    */
/*   **                        Copyright B.Tkachenko 1990        **    */
/*   *********CONST*SECTION*BEGIN**********************************    */
#define       EASC_F1               1059
#define       EASC_F2               1060
#define       EASC_F3               1061
#define       EASC_F4               1062
#define       EASC_F5               1063
#define       EASC_F6               1064
#define       EASC_F7               1065
#define       EASC_F8               1066
#define       EASC_F9               1067
#define       EASC_F10              1068

#define       EASC_Shift_F1         1084
#define       EASC_Shift_F2         1085
#define       EASC_Shift_F3         1086
#define       EASC_Shift_F4         1087
#define       EASC_Shift_F5         1088
#define       EASC_Shift_F6         1089
#define       EASC_Shift_F7         1090
#define       EASC_Shift_F8         1091
#define       EASC_Shift_F9         1092
#define       EASC_Shift_F10        1093

#define       EASC_Ctrl_F1         1094
#define       EASC_Ctrl_F2         1095
#define       EASC_Ctrl_F3         1096
#define       EASC_Ctrl_F4         1097
#define       EASC_Ctrl_F5         1098
#define       EASC_Ctrl_F6         1099
#define       EASC_Ctrl_F7         1100
#define       EASC_Ctrl_F8         1101
#define       EASC_Ctrl_F9         1102
#define       EASC_Ctrl_F10        1103

#define       EASC_Alt_F1         1104
#define       EASC_Alt_F2         1105
#define       EASC_Alt_F3         1106
#define       EASC_Alt_F4         1107
#define       EASC_Alt_F5         1108
#define       EASC_Alt_F6         1109
#define       EASC_Alt_F7         1110
#define       EASC_Alt_F8         1111
#define       EASC_Alt_F9         1112
#define       EASC_Alt_F10        1113

#define       EASC_Home             1071
#define       EASC_Left             1075
#define       EASC_End              1079
#define       EASC_Up               1072
#define       EASC_Down             1080
#define       EASC_PgUp             1073
#define       EASC_Right            1077
#define       EASC_PgDn             1081

#define       EASC_Ctrl_Home        1119
#define       EASC_Ctrl_Left        1115
#define       EASC_Ctrl_End         1117
#define       EASC_Ctrl_PgUp        1132
#define       EASC_Ctrl_Right       1116
#define       EASC_Ctrl_PgDn        1118

#define       EASC_Ins              1082
#define       EASC_Del              1083
#define       EASC_Shift_Tab        1015
#define       EASC_Ctrl_Prtsc       1114
#define       EASC_Ctrl_Break       1000

#define       EASC_Enter           13
#define       EASC_Esc             27
#define       EASC_Space           32

#define       EASC_Alt_A          1030
#define       EASC_Alt_B          1048
#define       EASC_Alt_C          1046
#define       EASC_Alt_D          1032
#define       EASC_Alt_E          1018
#define       EASC_Alt_F          1033
#define       EASC_Alt_G          1034
#define       EASC_Alt_H          1035
#define       EASC_Alt_I          1023
#define       EASC_Alt_J          1036
#define       EASC_Alt_K          1037
#define       EASC_Alt_L          1038
#define       EASC_Alt_M          1050

#define       EASC_Alt_N          1049
#define       EASC_Alt_O          1024
#define       EASC_Alt_P          1025
#define       EASC_Alt_Q          1016
#define       EASC_Alt_R          1019
#define       EASC_Alt_S          1031
#define       EASC_Alt_T          1020
#define       EASC_Alt_U          1022
#define       EASC_Alt_V          1047
#define       EASC_Alt_W          1017
#define       EASC_Alt_X          1045
#define       EASC_Alt_Y          1021
#define       EASC_Alt_Z          1044

#define    EASC_Ctrl_A       01
#define    EASC_Ctrl_B       02
#define    EASC_Ctrl_C       03
#define    EASC_Ctrl_D       04
#define    EASC_Ctrl_E       05
#define    EASC_Ctrl_F       06
#define    EASC_Ctrl_G       07
#define    EASC_Ctrl_H       08
#define    EASC_Ctrl_I       09
#define    EASC_Ctrl_J       10
#define    EASC_Ctrl_K       11
#define    EASC_Ctrl_L       12
#define    EASC_Ctrl_M       13

#define    EASC_Ctrl_N       14
#define    EASC_Ctrl_O       15
#define    EASC_Ctrl_P       16
#define    EASC_Ctrl_Q       17
#define    EASC_Ctrl_R       18
#define    EASC_Ctrl_S       19
#define    EASC_Ctrl_T       20
#define    EASC_Ctrl_U       21
#define    EASC_Ctrl_V       22
#define    EASC_Ctrl_W       23
#define    EASC_Ctrl_X       24
#define    EASC_Ctrl_Y       25
#define    EASC_Ctrl_Z       26

#define       EASC_Alt_1          1120
#define       EASC_Alt_2          1121
#define       EASC_Alt_3          1122
#define       EASC_Alt_4          1123
#define       EASC_Alt_5          1124
#define       EASC_Alt_6          1125
#define       EASC_Alt_7          1126
#define       EASC_Alt_8          1127
#define       EASC_Alt_9          1128
#define       EASC_Alt_0          1129
#define       EASC_Alt_Minus      1130
#define       EASC_Alt_Equal      1131
/*   ****Begin*****************************************************    */
/*   **       K E Y B O A R D      D E F I N I T I O N           **    */
/*   **                        Copyright B.Tkachenko 1990        **    */
/*   **********CONST*SECTION*END***********************************    */

/*   ****Begin*****************************************************    */
/*   **       K E Y B O A R D      D E F I N I T I O N           **    */
/*   **                        Copyright B.Tkachenko 1990        **    */
/*   ******FUNCTION*SECTION*BEGIN**********************************    */

int i_readkey()   /*  INTEGER CODING OF KEYBOARD INPUT   */
{
   unsigned char a2 ;
   int a  ;
   unsigned char a1 ;

   a = 0 ;  // если нажата необрабатываемая управляющая клавиша
            // то возвращается нуль

   a1 = getch() ;

   if ( a1 != 0 )
   {
     return(a1) ;       /*   NORMAL ASCII codes pressed    */
   } ;

   if ( a1 == 0 )
   {
     a2 = getch() ;
     switch ( a2  )        /*   EXTENDED ASCII codes     */
     {
case       59  :  a = 1059 ;  /*  F1   */                break ;
case       60  :  a = 1060 ;  /*  F2   */                break ;
case       61  :  a = 1061 ;  /*  F3   */                break ;
case       62  :  a = 1062 ;  /*  F4   */                break ;
case       63  :  a = 1063 ;  /*  F5   */                break ;
case       64  :  a = 1064 ;  /*  F6   */                break ;
case       65  :  a = 1065 ;  /*  F7   */                break ;
case       66  :  a = 1066 ;  /*  F8   */                break ;
case       67  :  a = 1067 ;  /*  F9   */                break ;
case       68  :  a = 1068 ;  /*  F10  */                break ;

case       84  :  a = 1084 ;  /*  Shift_F1   */          break ;
case       85  :  a = 1085 ;  /*  Shift_F2   */          break ;
case       86  :  a = 1086 ;  /*  Shift_F3   */          break ;
case       87  :  a = 1087 ;  /*  Shift_F4   */          break ;
case       88  :  a = 1088 ;  /*  Shift_F5   */          break ;
case       89  :  a = 1089 ;  /*  Shift_F6   */          break ;
case       90  :  a = 1090 ;  /*  Shift_F7   */          break ;
case       91  :  a = 1091 ;  /*  Shift_F8   */          break ;
case       92  :  a = 1092 ;  /*  Shift_F9   */          break ;
case       93  :  a = 1093 ;  /*  Shift_F10  */          break ;

case       94  :  a = 1094 ;  /*  Ctrl_F1   */           break ;
case       95  :  a = 1095 ;  /*  Ctrl_F2   */           break ;
case       96  :  a = 1096 ;  /*  Ctrl_F3   */           break ;
case       97  :  a = 1097 ;  /*  Ctrl_F4   */           break ;
case       98  :  a = 1098 ;  /*  Ctrl_F5   */           break ;
case       99  :  a = 1099 ;  /*  Ctrl_F6   */           break ;
case       100 :  a = 1100 ;  /*  Ctrl_F7   */           break ;
case       101 :  a = 1101 ;  /*  Ctrl_F8   */           break ;
case       102 :  a = 1102 ;  /*  Ctrl_F9   */           break ;
case       103 :  a = 1103 ;  /*  Ctrl_F10  */           break ;

case       104 :  a = 1104 ;  /*  Alt_F1   */            break ;
case       105 :  a = 1105 ;  /*  Alt_F2   */            break ;
case       106 :  a = 1106 ;  /*  Alt_F3   */            break ;
case       107 :  a = 1107 ;  /*  Alt_F4   */            break ;
case       108 :  a = 1108 ;  /*  Alt_F5   */            break ;
case       109 :  a = 1109 ;  /*  Alt_F6   */            break ;
case       110 :  a = 1110 ;  /*  Alt_F7   */            break ;
case       111 :  a = 1111 ;  /*  Alt_F8   */            break ;
case       112 :  a = 1112 ;  /*  Alt_F9   */            break ;
case       113 :  a = 1113 ;  /*  Alt_F10  */            break ;

case       71  :  a = 1071 ;  /*  Home   */              break ;
case       75  :  a = 1075 ;  /*  Left   */              break ;
case       79  :  a = 1079 ;  /*  End    */              break ;
case       72  :  a = 1072 ;  /*  Up     */              break ;
case       80  :  a = 1080 ;  /*  Down   */              break ;
case       73  :  a = 1073 ;  /*  PgUp   */              break ;
case       77  :  a = 1077 ;  /*  Right  */              break ;
case       81  :  a = 1081 ;  /*  PgDn   */              break ;

case       119 :  a = 1119 ;  /*  Ctrl_Home   */         break ;
case       115 :  a = 1115 ;  /*  Ctrl_Left   */         break ;
case       117 :  a = 1117 ;  /*  Ctrl_End    */         break ;
case       132 :  a = 1132 ;  /*  Ctrl_PgUp   */         break ;
case       116 :  a = 1116 ;  /*  Ctrl_Right  */         break ;
case       118 :  a = 1118 ;  /*  Ctrl_PgDn   */         break ;

case       82  :  a = 1082 ;  /*  Ins   */               break ;
case       83  :  a = 1083 ;  /*  Del   */               break ;
case       15  :  a = 1015 ;  /*  Shift_Tab     */       break ;
case       114 :  a = 1114 ;  /*  Ctrl_Prtsc    */       break ;
case       0   :  a = 1000 ;  /*  Ctrl_Break    */       break ;

case       30  :  a =  EASC_Alt_A  ;                     break ;
case       48  :  a =  EASC_Alt_B  ;                     break ;
case       46  :  a =  EASC_Alt_C  ;                     break ;
case       32  :  a =  EASC_Alt_D  ;                     break ;
case       18  :  a =  EASC_Alt_E  ;                     break ;
case       33  :  a =  EASC_Alt_F  ;                     break ;
case       34  :  a =  EASC_Alt_G  ;                     break ;
case       35  :  a =  EASC_Alt_H  ;                     break ;
case       23  :  a =  EASC_Alt_I  ;                     break ;
case       36  :  a =  EASC_Alt_J  ;                     break ;
case       37  :  a =  EASC_Alt_K  ;                     break ;
case       38  :  a =  EASC_Alt_L  ;                     break ;
case       50  :  a =  EASC_Alt_M  ;                     break ;

case       49  :  a =  EASC_Alt_N  ;                     break ;
case       24  :  a =  EASC_Alt_O  ;                     break ;
case       25  :  a =  EASC_Alt_P  ;                     break ;
case       16  :  a =  EASC_Alt_Q  ;                     break ;
case       19  :  a =  EASC_Alt_R  ;                     break ;
case       31  :  a =  EASC_Alt_S  ;                     break ;
case       20  :  a =  EASC_Alt_T  ;                     break ;
case       22  :  a =  EASC_Alt_U  ;                     break ;
case       47  :  a =  EASC_Alt_V  ;                     break ;
case       17  :  a =  EASC_Alt_W  ;                     break ;
case       45  :  a =  EASC_Alt_X  ;                     break ;
case       21  :  a =  EASC_Alt_Y  ;                     break ;
case       44  :  a =  EASC_Alt_Z  ;                     break ;


case       120   : a =  EASC_Alt_1        ;              break ;
case       121   : a =  EASC_Alt_2        ;              break ;
case       122   : a =  EASC_Alt_3        ;              break ;
case       123   : a =  EASC_Alt_4        ;              break ;
case       124   : a =  EASC_Alt_5        ;              break ;
case       125   : a =  EASC_Alt_6        ;              break ;
case       126   : a =  EASC_Alt_7        ;              break ;
case       127   : a =  EASC_Alt_8        ;              break ;
case       128   : a =  EASC_Alt_9        ;              break ;
case       129   : a =  EASC_Alt_0        ;              break ;
case       130   : a =  EASC_Alt_Minus    ;              break ;
case       131   : a =  EASC_Alt_Equal    ;              break ;
    }  ;   /*  switch    a2     */

     }   ;   /*   if    */

return(a) ;

}    /* f u n c t i o n    i _ r e a d k e y    */

/*   ****End*******************************************************    */
/*   **       K E Y B O A R D      D E F I N I T I O N           **    */
/*   **                        Copyright B.Tkachenko 1990        **    */
/*   ***FUNCTION*SECTION*END***************************************    */




